Skip to content

Commit 0e1766e

Browse files
authored
Merge pull request #28068 from CodaFi/welcome-to-the-salty-spitoon
Rationalize Implicit Member Synthesis Somewhat
2 parents cb25560 + a01a6bb commit 0e1766e

File tree

11 files changed

+187
-82
lines changed

11 files changed

+187
-82
lines changed

include/swift/AST/ASTTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
SWIFT_TYPEID(AncestryFlags)
1919
SWIFT_TYPEID(CtorInitializerKind)
2020
SWIFT_TYPEID(GenericSignature)
21+
SWIFT_TYPEID(ImplicitMemberAction)
2122
SWIFT_TYPEID(ParamSpecifier)
2223
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo)
2324
SWIFT_TYPEID(PropertyWrapperTypeInfo)

include/swift/AST/ASTTypeIDs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class TypeAliasDecl;
5555
class Type;
5656
struct TypePair;
5757
enum class AncestryFlags : uint8_t;
58+
enum class ImplicitMemberAction : uint8_t;
5859

5960
// Define the AST type zone (zone 1)
6061
#define SWIFT_TYPEID_ZONE AST

include/swift/AST/Decl.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,13 +477,16 @@ class alignas(1 << DeclAlignInBits) Decl {
477477
IsDebuggerAlias : 1
478478
);
479479

480-
SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1,
480+
SWIFT_INLINE_BITFIELD(NominalTypeDecl, GenericTypeDecl, 1+1+1,
481481
/// Whether we have already added implicitly-defined initializers
482482
/// to this declaration.
483483
AddedImplicitInitializers : 1,
484484

485485
/// Whether there is are lazily-loaded conformances for this nominal type.
486-
HasLazyConformances : 1
486+
HasLazyConformances : 1,
487+
488+
/// Whether this nominal type is having its semantic members resolved.
489+
IsComputingSemanticMembers : 1
487490
);
488491

489492
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+2+1+1+8+16,
@@ -3328,6 +3331,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
33283331
Bits.NominalTypeDecl.AddedImplicitInitializers = false;
33293332
ExtensionGeneration = 0;
33303333
Bits.NominalTypeDecl.HasLazyConformances = false;
3334+
Bits.NominalTypeDecl.IsComputingSemanticMembers = false;
33313335
}
33323336

33333337
friend class ProtocolType;
@@ -3475,6 +3479,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
34753479
/// declaration, or \c nullptr if it doesn't have one.
34763480
ConstructorDecl *getDefaultInitializer() const;
34773481

3482+
void synthesizeSemanticMembersIfNeeded(DeclName member);
3483+
34783484
// Implement isa/cast/dyncast/etc.
34793485
static bool classof(const Decl *D) {
34803486
return D->getKind() >= DeclKind::First_NominalTypeDecl &&

include/swift/AST/LazyResolver.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@ class LazyResolver {
5252
/// the given protocol conformance.
5353
virtual void resolveWitness(const NormalProtocolConformance *conformance,
5454
ValueDecl *requirement) = 0;
55-
56-
/// Resolve an implicitly-generated member with the given name.
57-
virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) = 0;
5855
};
5956

6057
class LazyMemberLoader;

include/swift/AST/TypeCheckRequests.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,35 @@ class InheritsSuperclassInitializersRequest
16681668
void cacheResult(bool value) const;
16691669
};
16701670

1671+
// The actions this request takes are all huge layering violations.
1672+
//
1673+
// Please do not add any more.
1674+
enum class ImplicitMemberAction : uint8_t {
1675+
ResolveImplicitInit,
1676+
ResolveCodingKeys,
1677+
ResolveEncodable,
1678+
ResolveDecodable,
1679+
};
1680+
1681+
class ResolveImplicitMemberRequest
1682+
: public SimpleRequest<ResolveImplicitMemberRequest,
1683+
bool(NominalTypeDecl *, ImplicitMemberAction),
1684+
CacheKind::Uncached> {
1685+
public:
1686+
using SimpleRequest::SimpleRequest;
1687+
1688+
private:
1689+
friend SimpleRequest;
1690+
1691+
// Evaluation.
1692+
llvm::Expected<bool> evaluate(Evaluator &evaluator, NominalTypeDecl *NTD,
1693+
ImplicitMemberAction action) const;
1694+
1695+
public:
1696+
// Separate caching.
1697+
bool isCached() const { return true; }
1698+
};
1699+
16711700
// Allow AnyValue to compare two Type values, even though Type doesn't
16721701
// support ==.
16731702
template<>
@@ -1689,6 +1718,7 @@ AnyValue::Holder<GenericSignature>::equals(const HolderBase &other) const {
16891718

16901719
void simple_display(llvm::raw_ostream &out, Type value);
16911720
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);
1721+
void simple_display(llvm::raw_ostream &out, ImplicitMemberAction action);
16921722

16931723
#define SWIFT_TYPEID_ZONE TypeChecker
16941724
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest,
174174
bool(NominalTypeDecl *), Cached, NoLocationInfo)
175175
SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest,
176176
bool(StructDecl *), Cached, NoLocationInfo)
177+
SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
178+
bool(NominalTypeDecl *, ImplicitMemberAction), Uncached,
179+
NoLocationInfo)
177180
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
178181
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
179182
SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,

lib/AST/Decl.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,6 +3956,47 @@ ConstructorDecl *NominalTypeDecl::getDefaultInitializer() const {
39563956
SynthesizeDefaultInitRequest{mutableThis}, nullptr);
39573957
}
39583958

3959+
void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) {
3960+
// Silently break cycles here because we can't be sure when and where a
3961+
// request to synthesize will come from yet.
3962+
// FIXME: rdar://56844567
3963+
if (Bits.NominalTypeDecl.IsComputingSemanticMembers)
3964+
return;
3965+
3966+
Bits.NominalTypeDecl.IsComputingSemanticMembers = true;
3967+
SWIFT_DEFER { Bits.NominalTypeDecl.IsComputingSemanticMembers = false; };
3968+
3969+
auto baseName = member.getBaseName();
3970+
auto &Context = getASTContext();
3971+
Optional<ImplicitMemberAction> action = None;
3972+
if (baseName == DeclBaseName::createConstructor())
3973+
action.emplace(ImplicitMemberAction::ResolveImplicitInit);
3974+
3975+
if (member.isSimpleName() && !baseName.isSpecial()) {
3976+
if (baseName.getIdentifier() == getASTContext().Id_CodingKeys) {
3977+
action.emplace(ImplicitMemberAction::ResolveCodingKeys);
3978+
}
3979+
} else {
3980+
auto argumentNames = member.getArgumentNames();
3981+
if (!member.isCompoundName() || argumentNames.size() == 1) {
3982+
if (baseName == DeclBaseName::createConstructor() &&
3983+
(member.isSimpleName() || argumentNames.front() == Context.Id_from)) {
3984+
action.emplace(ImplicitMemberAction::ResolveDecodable);
3985+
} else if (!baseName.isSpecial() &&
3986+
baseName.getIdentifier() == Context.Id_encode &&
3987+
(member.isSimpleName() ||
3988+
argumentNames.front() == Context.Id_to)) {
3989+
action.emplace(ImplicitMemberAction::ResolveEncodable);
3990+
}
3991+
}
3992+
}
3993+
3994+
if (auto actionToTake = action) {
3995+
(void)evaluateOrDefault(Context.evaluator,
3996+
ResolveImplicitMemberRequest{this, actionToTake.getValue()}, false);
3997+
}
3998+
}
3999+
39594000
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
39604001
MutableArrayRef<TypeLoc> Inherited,
39614002
GenericParamList *GenericParams, DeclContext *Parent)

lib/AST/NameLookup.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,34 @@ bool DeclContext::lookupQualified(Type type,
15171517
return lookupQualified(nominalTypesToLookInto, member, options, decls);
15181518
}
15191519

1520+
static void installPropertyWrapperMembersIfNeeded(NominalTypeDecl *target,
1521+
DeclName member) {
1522+
auto &Context = target->getASTContext();
1523+
auto baseName = member.getBaseName();
1524+
if (!member.isSimpleName() || baseName.isSpecial())
1525+
return;
1526+
1527+
if ((!baseName.getIdentifier().str().startswith("$") &&
1528+
!baseName.getIdentifier().str().startswith("_")) ||
1529+
baseName.getIdentifier().str().size() <= 1) {
1530+
return;
1531+
}
1532+
1533+
// $- and _-prefixed variables can be generated by properties that have
1534+
// attached property wrappers.
1535+
auto originalPropertyName =
1536+
Context.getIdentifier(baseName.getIdentifier().str().substr(1));
1537+
for (auto member : target->lookupDirect(originalPropertyName)) {
1538+
if (auto var = dyn_cast<VarDecl>(member)) {
1539+
if (var->hasAttachedPropertyWrapper()) {
1540+
auto sourceFile = var->getDeclContext()->getParentSourceFile();
1541+
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface)
1542+
(void)var->getPropertyWrapperBackingProperty();
1543+
}
1544+
}
1545+
}
1546+
}
1547+
15201548
bool DeclContext::lookupQualified(ArrayRef<NominalTypeDecl *> typeDecls,
15211549
DeclName member,
15221550
NLOptions options,
@@ -1572,7 +1600,8 @@ bool DeclContext::lookupQualified(ArrayRef<NominalTypeDecl *> typeDecls,
15721600

15731601
// Make sure we've resolved implicit members, if we need them.
15741602
if (typeResolver) {
1575-
typeResolver->resolveImplicitMember(current, member);
1603+
current->synthesizeSemanticMembersIfNeeded(member);
1604+
installPropertyWrapperMembersIfNeeded(current, member);
15761605
}
15771606

15781607
// Look for results within the current nominal type and its extensions.

lib/AST/TypeCheckRequests.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,3 +1071,26 @@ void InheritsSuperclassInitializersRequest::cacheResult(bool value) const {
10711071
auto *decl = std::get<0>(getStorage());
10721072
decl->setInheritsSuperclassInitializers(value);
10731073
}
1074+
1075+
//----------------------------------------------------------------------------//
1076+
// ResolveImplicitMemberRequest computation.
1077+
//----------------------------------------------------------------------------//
1078+
1079+
void swift::simple_display(llvm::raw_ostream &out,
1080+
ImplicitMemberAction action) {
1081+
switch (action) {
1082+
case ImplicitMemberAction::ResolveImplicitInit:
1083+
out << "resolve implicit initializer";
1084+
break;
1085+
case ImplicitMemberAction::ResolveCodingKeys:
1086+
out << "resolve CodingKeys";
1087+
break;
1088+
case ImplicitMemberAction::ResolveEncodable:
1089+
out << "resolve Encodable.encode(to:)";
1090+
break;
1091+
case ImplicitMemberAction::ResolveDecodable:
1092+
out << "resolve Decodable.init(from:)";
1093+
break;
1094+
}
1095+
}
1096+

lib/Sema/CodeSynthesis.cpp

Lines changed: 50 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,12 +1075,12 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
10751075
(void)decl->getDefaultInitializer();
10761076
}
10771077

1078-
void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
1079-
DeclName member) {
1080-
auto baseName = member.getBaseName();
1081-
1082-
if (baseName == DeclBaseName::createConstructor())
1083-
addImplicitConstructors(target);
1078+
llvm::Expected<bool>
1079+
ResolveImplicitMemberRequest::evaluate(Evaluator &evaluator,
1080+
NominalTypeDecl *target,
1081+
ImplicitMemberAction action) const {
1082+
// FIXME: This entire request is a layering violation made of smaller,
1083+
// finickier layering violations. See rdar://56844567
10841084

10851085
// Checks whether the target conforms to the given protocol. If the
10861086
// conformance is incomplete, force the conformance.
@@ -1091,9 +1091,9 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
10911091
return false;
10921092

10931093
auto targetType = target->getDeclaredInterfaceType();
1094-
auto ref =
1095-
conformsToProtocol(targetType, protocol, target,
1096-
ConformanceCheckFlags::SkipConditionalRequirements);
1094+
auto ref = TypeChecker::conformsToProtocol(
1095+
targetType, protocol, target,
1096+
ConformanceCheckFlags::SkipConditionalRequirements);
10971097

10981098
if (ref.isInvalid()) {
10991099
return false;
@@ -1109,67 +1109,50 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
11091109
return true;
11101110
};
11111111

1112-
if (member.isSimpleName() && !baseName.isSpecial()) {
1113-
if (baseName.getIdentifier() == Context.Id_CodingKeys) {
1114-
// CodingKeys is a special type which may be synthesized as part of
1115-
// Encodable/Decodable conformance. If the target conforms to either
1116-
// protocol and would derive conformance to either, the type may be
1117-
// synthesized.
1118-
// If the target conforms to either and the conformance has not yet been
1119-
// evaluated, then we should do that here.
1120-
//
1121-
// Try to synthesize Decodable first. If that fails, try to synthesize
1122-
// Encodable. If either succeeds and CodingKeys should have been
1123-
// synthesized, it will be synthesized.
1124-
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
1125-
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
1126-
if (!evaluateTargetConformanceTo(decodableProto))
1127-
(void)evaluateTargetConformanceTo(encodableProto);
1128-
}
1129-
1130-
if ((baseName.getIdentifier().str().startswith("$") ||
1131-
baseName.getIdentifier().str().startswith("_")) &&
1132-
baseName.getIdentifier().str().size() > 1) {
1133-
// $- and _-prefixed variables can be generated by properties that have
1134-
// attached property wrappers.
1135-
auto originalPropertyName =
1136-
Context.getIdentifier(baseName.getIdentifier().str().substr(1));
1137-
for (auto member : target->lookupDirect(originalPropertyName)) {
1138-
if (auto var = dyn_cast<VarDecl>(member)) {
1139-
if (var->hasAttachedPropertyWrapper()) {
1140-
auto sourceFile = var->getDeclContext()->getParentSourceFile();
1141-
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface)
1142-
(void)var->getPropertyWrapperBackingPropertyInfo();
1143-
}
1144-
}
1145-
}
1146-
}
1147-
1148-
} else {
1149-
auto argumentNames = member.getArgumentNames();
1150-
if (member.isCompoundName() && argumentNames.size() != 1)
1151-
return;
1152-
1153-
if (baseName == DeclBaseName::createConstructor() &&
1154-
(member.isSimpleName() || argumentNames.front() == Context.Id_from)) {
1155-
// init(from:) may be synthesized as part of derived conformance to the
1156-
// Decodable protocol.
1157-
// If the target should conform to the Decodable protocol, check the
1158-
// conformance here to attempt synthesis.
1159-
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
1160-
(void)evaluateTargetConformanceTo(decodableProto);
1161-
} else if (!baseName.isSpecial() &&
1162-
baseName.getIdentifier() == Context.Id_encode &&
1163-
(member.isSimpleName() ||
1164-
argumentNames.front() == Context.Id_to)) {
1165-
// encode(to:) may be synthesized as part of derived conformance to the
1166-
// Encodable protocol.
1167-
// If the target should conform to the Encodable protocol, check the
1168-
// conformance here to attempt synthesis.
1169-
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
1112+
auto &Context = target->getASTContext();
1113+
switch (action) {
1114+
case ImplicitMemberAction::ResolveImplicitInit:
1115+
TypeChecker::addImplicitConstructors(target);
1116+
break;
1117+
case ImplicitMemberAction::ResolveCodingKeys: {
1118+
// CodingKeys is a special type which may be synthesized as part of
1119+
// Encodable/Decodable conformance. If the target conforms to either
1120+
// protocol and would derive conformance to either, the type may be
1121+
// synthesized.
1122+
// If the target conforms to either and the conformance has not yet been
1123+
// evaluated, then we should do that here.
1124+
//
1125+
// Try to synthesize Decodable first. If that fails, try to synthesize
1126+
// Encodable. If either succeeds and CodingKeys should have been
1127+
// synthesized, it will be synthesized.
1128+
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
1129+
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
1130+
if (!evaluateTargetConformanceTo(decodableProto)) {
11701131
(void)evaluateTargetConformanceTo(encodableProto);
11711132
}
11721133
}
1134+
break;
1135+
case ImplicitMemberAction::ResolveEncodable: {
1136+
// encode(to:) may be synthesized as part of derived conformance to the
1137+
// Encodable protocol.
1138+
// If the target should conform to the Encodable protocol, check the
1139+
// conformance here to attempt synthesis.
1140+
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
1141+
(void)evaluateTargetConformanceTo(encodableProto);
1142+
}
1143+
break;
1144+
case ImplicitMemberAction::ResolveDecodable: {
1145+
// init(from:) may be synthesized as part of derived conformance to the
1146+
// Decodable protocol.
1147+
// If the target should conform to the Decodable protocol, check the
1148+
// conformance here to attempt synthesis.
1149+
TypeChecker::addImplicitConstructors(target);
1150+
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
1151+
(void)evaluateTargetConformanceTo(decodableProto);
1152+
}
1153+
break;
1154+
}
1155+
return true;
11731156
}
11741157

11751158
llvm::Expected<bool>

0 commit comments

Comments
 (0)