Skip to content

Commit 4ad33bf

Browse files
authored
Merge pull request #67721 from xedin/rdar-112153201
[CodeSynthesis] Make sure that init synthesis expands macros
2 parents e7a5887 + a0573e0 commit 4ad33bf

File tree

3 files changed

+192
-96
lines changed

3 files changed

+192
-96
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 140 additions & 96 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,62 +888,68 @@ 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-
}
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+
property->isDefaultInitializable();
926+
}))
927+
return;
928+
929+
if (VD->hasStorageOrWrapsStorage())
930+
HasStorage = true;
931+
932+
// Treat an init accessor property that doesn't initialize other
933+
// properties as stored for initialization purposes.
934+
if (auto *initAccessor = VD->getAccessor(AccessorKind::Init)) {
935+
HasStorage |= initAccessor->getInitializedProperties().empty();
936+
}
937+
});
861938

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-
property->isDefaultInitializable();
882-
}))
883-
return;
884-
885-
if (VD->hasStorageOrWrapsStorage())
886-
HasStorage = true;
887-
888-
// Treat an init accessor property that doesn't initialize other
889-
// properties as stored for initialization purposes.
890-
if (auto *initAccessor = VD->getAccessor(AccessorKind::Init)) {
891-
HasStorage |= initAccessor->getInitializedProperties().empty();
892-
}
893-
});
894-
895-
if (!HasStorage) continue;
896-
897-
if (pbd->isInitialized(idx)) continue;
898-
899-
// If we cannot default initialize the property, we cannot
900-
// synthesize a default initializer for the class.
901-
if (CheckDefaultInitializer && !pbd->isDefaultInitializable())
902-
return false;
903-
}
904-
}
905-
}
939+
if (!HasStorage)
940+
return true;
906941

907-
return true;
942+
if (pbd->isInitialized(idx))
943+
return true;
944+
945+
// If we cannot default initialize the property, we cannot
946+
// synthesize a default initializer for the class.
947+
if (CheckDefaultInitializer && !pbd->isDefaultInitializable()) {
948+
return false;
949+
}
950+
}
951+
return true;
952+
});
908953
}
909954

910955
static bool areAllStoredPropertiesDefaultInitializable(Evaluator &eval,
@@ -1311,53 +1356,52 @@ HasMemberwiseInitRequest::evaluate(Evaluator &evaluator,
13111356
llvm::SmallPtrSet<VarDecl *, 4> initializedProperties;
13121357
llvm::SmallVector<std::pair<VarDecl *, Identifier>> invalidOrderings;
13131358

1314-
for (auto *member : decl->getMembers()) {
1315-
if (auto *var = dyn_cast<VarDecl>(member)) {
1316-
// If this is a backing storage property for a property wrapper,
1317-
// skip it.
1318-
if (var->getOriginalWrappedProperty())
1319-
continue;
1320-
1321-
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1322-
continue;
1359+
if (enumerateCurrentPropertiesAndAuxiliaryVars(decl, [&](VarDecl *var) {
1360+
if (var->isStatic())
1361+
return true;
13231362

1324-
// Check whether use of init accessors results in access to uninitialized
1325-
// properties.
1363+
if (var->getOriginalWrappedProperty())
1364+
return true;
13261365

1327-
if (auto *initAccessor = var->getAccessor(AccessorKind::Init)) {
1328-
// Make sure that all properties accessed by init accessor
1329-
// are previously initialized.
1330-
for (auto *property : initAccessor->getAccessedProperties()) {
1331-
if (!initializedProperties.count(property))
1332-
invalidOrderings.push_back({var, property->getName()});
1333-
}
1366+
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
1367+
return true;
13341368

1335-
// Record all of the properties initialized by calling init accessor.
1336-
auto properties = initAccessor->getInitializedProperties();
1337-
initializedProperties.insert(var);
1338-
initializedProperties.insert(properties.begin(), properties.end());
1339-
continue;
1340-
}
1341-
1342-
switch (initializedViaAccessor.count(var)) {
1343-
// Not covered by an init accessor.
1344-
case 0:
1345-
initializedProperties.insert(var);
1346-
continue;
1369+
// Check whether use of init accessors results in access to
1370+
// uninitialized properties.
1371+
if (auto *initAccessor = var->getAccessor(AccessorKind::Init)) {
1372+
// Make sure that all properties accessed by init accessor
1373+
// are previously initialized.
1374+
for (auto *property : initAccessor->getAccessedProperties()) {
1375+
if (!initializedProperties.count(property))
1376+
invalidOrderings.push_back({var, property->getName()});
1377+
}
13471378

1348-
// Covered by a single init accessor, we'll handle that
1349-
// once we get to the property with init accessor.
1350-
case 1:
1351-
continue;
1379+
// Record all of the properties initialized by calling init accessor.
1380+
auto properties = initAccessor->getInitializedProperties();
1381+
initializedProperties.insert(var);
1382+
initializedProperties.insert(properties.begin(), properties.end());
1383+
return true;
1384+
}
13521385

1353-
// Covered by more than one init accessor which means that we
1354-
// cannot synthesize memberwise initializer due to intersecting
1355-
// initializations.
1356-
default:
1357-
return false;
1358-
}
1359-
}
1360-
}
1386+
switch (initializedViaAccessor.count(var)) {
1387+
// Not covered by an init accessor.
1388+
case 0:
1389+
initializedProperties.insert(var);
1390+
return true;
1391+
1392+
// Covered by a single init accessor, we'll handle that
1393+
// once we get to the property with init accessor.
1394+
case 1:
1395+
return true;
1396+
1397+
// Covered by more than one init accessor which means that we
1398+
// cannot synthesize memberwise initializer due to intersecting
1399+
// initializations.
1400+
default:
1401+
return false;
1402+
}
1403+
}))
1404+
return false;
13611405

13621406
if (invalidOrderings.empty())
13631407
return !initializedProperties.empty();

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,3 +1928,24 @@ public struct NestedMagicLiteralMacro: ExpressionMacro {
19281928
"""
19291929
}
19301930
}
1931+
1932+
public struct InitWithProjectedValueWrapperMacro: PeerMacro {
1933+
public static func expansion(
1934+
of node: AttributeSyntax,
1935+
providingPeersOf declaration: some DeclSyntaxProtocol,
1936+
in context: some MacroExpansionContext
1937+
) throws -> [DeclSyntax] {
1938+
return [
1939+
"""
1940+
private var _value: Wrapper
1941+
var _$value: Wrapper {
1942+
@storageRestrictions(initializes: _value)
1943+
init {
1944+
self._value = newValue
1945+
}
1946+
get { _value }
1947+
}
1948+
"""
1949+
]
1950+
}
1951+
}

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)