Skip to content

Commit 00b0abd

Browse files
committed
[CodeSynthesis] Make sure that init synthesis expands macros
Previously both `AreAllStoredPropertiesDefaultInitableRequest` and `HasMemberwiseInitRequest` checked only "current" properties of the type but macro expansions can add new stored and/or init accessor properties that affect this logic so we need to make sure that macro expansions happen and new properties are accounted for. Note that the original idea was to use `getStoredProperties()` but that runs into multiple circularity issues related to lazy properties. Resolves: rdar://112153201
1 parent 281207f commit 00b0abd

File tree

3 files changed

+187
-91
lines changed

3 files changed

+187
-91
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 135 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,45 @@ static void diagnoseMissingRequiredInitializer(
841841
diag::required_initializer_here);
842842
}
843843

844+
/// FIXME: This is temporary until we come up with a way to overcome circularity
845+
/// issues.
846+
///
847+
/// This method is intended to be used only in places that expect
848+
/// lazy and property wrapper backing storage synthesis has happened
849+
/// or can tolerate absence of such properties.
850+
///
851+
/// \param typeDecl The nominal type to enumerate current properties and their
852+
/// auxiliary vars for.
853+
///
854+
/// \param callback The callback to be called for each property and auxiliary
855+
/// var associated with the given type. The callback should return `true` to
856+
/// indicate that enumeration should continue and `false` otherwise.
857+
///
858+
/// \returns true which indicates "failure" if callback returns `false`
859+
/// at least once.
860+
static bool enumerateCurrentPropertiesAndAuxiliaryVars(
861+
NominalTypeDecl *typeDecl, llvm::function_ref<bool(VarDecl *)> callback) {
862+
for (auto *member :
863+
typeDecl->getImplementationContext()->getCurrentMembers()) {
864+
if (auto *var = dyn_cast<VarDecl>(member)) {
865+
if (!callback(var))
866+
return true;
867+
}
868+
869+
bool hadErrors = false;
870+
member->visitAuxiliaryDecls([&](Decl *auxDecl) {
871+
if (auto *auxVar = dyn_cast<VarDecl>(auxDecl)) {
872+
hadErrors |= !callback(auxVar);
873+
}
874+
});
875+
876+
if (hadErrors)
877+
return true;
878+
}
879+
880+
return false;
881+
}
882+
844883
bool AreAllStoredPropertiesDefaultInitableRequest::evaluate(
845884
Evaluator &evaluator, NominalTypeDecl *decl) const {
846885
assert(!hasClangImplementation(decl));
@@ -849,55 +888,61 @@ bool AreAllStoredPropertiesDefaultInitableRequest::evaluate(
849888
decl->collectPropertiesInitializableByInitAccessors(
850889
initializedViaInitAccessor);
851890

852-
for (auto member : decl->getImplementationContext()->getMembers()) {
853-
// If a stored property lacks an initial value and if there is no way to
854-
// synthesize an initial value (e.g. for an optional) then we suppress
855-
// generation of the default initializer.
856-
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
857-
// Static variables are irrelevant.
858-
if (pbd->isStatic()) {
859-
continue;
860-
}
861-
862-
for (auto idx : range(pbd->getNumPatternEntries())) {
863-
bool HasStorage = false;
864-
bool CheckDefaultInitializer = true;
865-
pbd->getPattern(idx)->forEachVariable(
866-
[&HasStorage, &CheckDefaultInitializer,
867-
&initializedViaInitAccessor](VarDecl *VD) {
868-
// If one of the bound variables is @NSManaged, go ahead no matter
869-
// what.
870-
if (VD->getAttrs().hasAttribute<NSManagedAttr>())
871-
CheckDefaultInitializer = false;
872-
873-
// If this property is covered by one or more init accessor(s)
874-
// check whether at least one of them is initializable.
875-
auto initAccessorProperties =
876-
llvm::make_range(initializedViaInitAccessor.equal_range(VD));
877-
if (llvm::any_of(initAccessorProperties, [&](const auto &entry) {
878-
auto *property =
879-
entry.second->getParentPatternBinding();
880-
return property->isInitialized(0);
881-
}))
882-
return;
883-
884-
if (VD->hasStorageOrWrapsStorage())
885-
HasStorage = true;
886-
});
887-
888-
if (!HasStorage) continue;
889-
890-
if (pbd->isInitialized(idx)) continue;
891-
892-
// If we cannot default initialize the property, we cannot
893-
// synthesize a default initializer for the class.
894-
if (CheckDefaultInitializer && !pbd->isDefaultInitializable())
895-
return false;
896-
}
897-
}
898-
}
899-
900-
return true;
891+
llvm::SmallPtrSet<PatternBindingDecl *, 4> checked;
892+
return !enumerateCurrentPropertiesAndAuxiliaryVars(
893+
decl, [&](VarDecl *property) {
894+
auto *pbd = property->getParentPatternBinding();
895+
if (!pbd || !checked.insert(pbd).second)
896+
return true;
897+
898+
// If a stored property lacks an initial value and if there is no way to
899+
// synthesize an initial value (e.g. for an optional) then we suppress
900+
// generation of the default initializer.
901+
902+
// Static variables are irrelevant.
903+
if (pbd->isStatic())
904+
return true;
905+
906+
for (auto idx : range(pbd->getNumPatternEntries())) {
907+
bool HasStorage = false;
908+
bool CheckDefaultInitializer = true;
909+
pbd->getPattern(idx)->forEachVariable([&HasStorage,
910+
&CheckDefaultInitializer,
911+
&initializedViaInitAccessor](
912+
VarDecl *VD) {
913+
// If one of the bound variables is @NSManaged, go ahead no matter
914+
// what.
915+
if (VD->getAttrs().hasAttribute<NSManagedAttr>())
916+
CheckDefaultInitializer = false;
917+
918+
// If this property is covered by one or more init accessor(s)
919+
// check whether at least one of them is initializable.
920+
auto initAccessorProperties =
921+
llvm::make_range(initializedViaInitAccessor.equal_range(VD));
922+
if (llvm::any_of(initAccessorProperties, [&](const auto &entry) {
923+
auto *property = entry.second->getParentPatternBinding();
924+
return property->isInitialized(0);
925+
}))
926+
return;
927+
928+
if (VD->hasStorageOrWrapsStorage())
929+
HasStorage = true;
930+
});
931+
932+
if (!HasStorage)
933+
return true;
934+
935+
if (pbd->isInitialized(idx))
936+
return true;
937+
938+
// If we cannot default initialize the property, we cannot
939+
// synthesize a default initializer for the class.
940+
if (CheckDefaultInitializer && !pbd->isDefaultInitializable()) {
941+
return false;
942+
}
943+
}
944+
return true;
945+
});
901946
}
902947

903948
static bool areAllStoredPropertiesDefaultInitializable(Evaluator &eval,
@@ -1304,53 +1349,52 @@ HasMemberwiseInitRequest::evaluate(Evaluator &evaluator,
13041349
llvm::SmallPtrSet<VarDecl *, 4> initializedProperties;
13051350
llvm::SmallVector<std::pair<VarDecl *, Identifier>> invalidOrderings;
13061351

1307-
for (auto *member : decl->getMembers()) {
1308-
if (auto *var = dyn_cast<VarDecl>(member)) {
1309-
// If this is a backing storage property for a property wrapper,
1310-
// skip it.
1311-
if (var->getOriginalWrappedProperty())
1312-
continue;
1313-
1314-
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1315-
continue;
1352+
if (enumerateCurrentPropertiesAndAuxiliaryVars(decl, [&](VarDecl *var) {
1353+
if (var->isStatic())
1354+
return true;
13161355

1317-
// Check whether use of init accessors results in access to uninitialized
1318-
// properties.
1356+
if (var->getOriginalWrappedProperty())
1357+
return true;
13191358

1320-
if (auto *initAccessor = var->getAccessor(AccessorKind::Init)) {
1321-
// Make sure that all properties accessed by init accessor
1322-
// are previously initialized.
1323-
for (auto *property : initAccessor->getAccessedProperties()) {
1324-
if (!initializedProperties.count(property))
1325-
invalidOrderings.push_back({var, property->getName()});
1326-
}
1359+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1360+
return true;
13271361

1328-
// Record all of the properties initialized by calling init accessor.
1329-
auto properties = initAccessor->getInitializedProperties();
1330-
initializedProperties.insert(var);
1331-
initializedProperties.insert(properties.begin(), properties.end());
1332-
continue;
1333-
}
1334-
1335-
switch (initializedViaAccessor.count(var)) {
1336-
// Not covered by an init accessor.
1337-
case 0:
1338-
initializedProperties.insert(var);
1339-
continue;
1362+
// Check whether use of init accessors results in access to
1363+
// uninitialized properties.
1364+
if (auto *initAccessor = var->getAccessor(AccessorKind::Init)) {
1365+
// Make sure that all properties accessed by init accessor
1366+
// are previously initialized.
1367+
for (auto *property : initAccessor->getAccessedProperties()) {
1368+
if (!initializedProperties.count(property))
1369+
invalidOrderings.push_back({var, property->getName()});
1370+
}
13401371

1341-
// Covered by a single init accessor, we'll handle that
1342-
// once we get to the property with init accessor.
1343-
case 1:
1344-
continue;
1372+
// Record all of the properties initialized by calling init accessor.
1373+
auto properties = initAccessor->getInitializedProperties();
1374+
initializedProperties.insert(var);
1375+
initializedProperties.insert(properties.begin(), properties.end());
1376+
return true;
1377+
}
13451378

1346-
// Covered by more than one init accessor which means that we
1347-
// cannot synthesize memberwise initializer due to intersecting
1348-
// initializations.
1349-
default:
1350-
return false;
1351-
}
1352-
}
1353-
}
1379+
switch (initializedViaAccessor.count(var)) {
1380+
// Not covered by an init accessor.
1381+
case 0:
1382+
initializedProperties.insert(var);
1383+
return true;
1384+
1385+
// Covered by a single init accessor, we'll handle that
1386+
// once we get to the property with init accessor.
1387+
case 1:
1388+
return true;
1389+
1390+
// Covered by more than one init accessor which means that we
1391+
// cannot synthesize memberwise initializer due to intersecting
1392+
// initializations.
1393+
default:
1394+
return false;
1395+
}
1396+
}))
1397+
return false;
13541398

13551399
if (invalidOrderings.empty())
13561400
return !initializedProperties.empty();

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,3 +1871,24 @@ public struct NestedMagicLiteralMacro: ExpressionMacro {
18711871
"""
18721872
}
18731873
}
1874+
1875+
public struct InitWithProjectedValueWrapperMacro: PeerMacro {
1876+
public static func expansion(
1877+
of node: AttributeSyntax,
1878+
providingPeersOf declaration: some DeclSyntaxProtocol,
1879+
in context: some MacroExpansionContext
1880+
) throws -> [DeclSyntax] {
1881+
return [
1882+
"""
1883+
private var _value: Wrapper
1884+
var _$value: Wrapper {
1885+
@storageRestrictions(initializes: _value)
1886+
init {
1887+
self._value = newValue
1888+
}
1889+
get { _value }
1890+
}
1891+
"""
1892+
]
1893+
}
1894+
}

test/Macros/init_accessor.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath
3+
// RUN: %target-typecheck-verify-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name MacroUser -DTEST_DIAGNOSTICS -swift-version 5
4+
// RUN: %target-build-swift -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %s -o %t/main -module-name MacroUser -swift-version 5
5+
// RUN: %target-codesign %t/main
6+
// RUN: %target-run %t/main
7+
8+
// REQUIRES: swift_swift_parser, executable_test
9+
10+
@attached(peer, names: named(_value), named(_$value))
11+
macro Wrapped() = #externalMacro(module: "MacroDefinition",
12+
type: "InitWithProjectedValueWrapperMacro")
13+
14+
@propertyWrapper
15+
struct Wrapper {
16+
var wrappedValue: Int {
17+
1
18+
}
19+
var projectedValue: Wrapper {
20+
self
21+
}
22+
}
23+
24+
struct Test {
25+
@Wrapped
26+
var value: Int { 1 }
27+
}
28+
29+
let test = Test(_$value: Wrapper())
30+
print(test.value)
31+
// CHECK: 1

0 commit comments

Comments
 (0)