Skip to content

Commit 7fd5a82

Browse files
authored
Merge pull request #10930 from itaiferber/synthesized-member-lookup
Expose synthesized members in AST lookups
2 parents 758b632 + e864285 commit 7fd5a82

9 files changed

+1432
-3
lines changed

include/swift/AST/LazyResolver.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class LazyResolver {
9191
/// Resolve any implicitly-declared constructors within the given nominal.
9292
virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) = 0;
9393

94+
/// Resolve an implicitly-generated member with the given name.
95+
virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) = 0;
96+
9497
/// Resolve any implicitly-generated members and conformances for generated
9598
/// external decls.
9699
virtual void resolveExternalDeclImplicitMembers(NominalTypeDecl *nominal) = 0;
@@ -156,6 +159,10 @@ class DelegatingLazyResolver : public LazyResolver {
156159
Principal.resolveImplicitConstructors(nominal);
157160
}
158161

162+
void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) override {
163+
Principal.resolveImplicitMember(nominal, member);
164+
}
165+
159166
void resolveExternalDeclImplicitMembers(NominalTypeDecl *nominal) override {
160167
Principal.resolveExternalDeclImplicitMembers(nominal);
161168
}

lib/AST/NameLookup.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,9 +1499,13 @@ bool DeclContext::lookupQualified(Type type,
14991499
if (tracker)
15001500
tracker->addUsedMember({current, member.getBaseName()},isLookupCascading);
15011501

1502-
// Make sure we've resolved implicit constructors, if we need them.
1503-
if (member.getBaseName() == ctx.Id_init && typeResolver)
1504-
typeResolver->resolveImplicitConstructors(current);
1502+
// Make sure we've resolved implicit members, if we need them.
1503+
if (typeResolver) {
1504+
if (member.getBaseName() == ctx.Id_init)
1505+
typeResolver->resolveImplicitConstructors(current);
1506+
1507+
typeResolver->resolveImplicitMember(current, member);
1508+
}
15051509

15061510
// Look for results within the current nominal type and its extensions.
15071511
bool currentIsProtocol = isa<ProtocolDecl>(current);

lib/Sema/DerivedConformanceCodingKey.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ deriveBodyCodingKey_init_stringValue(AbstractFunctionDecl *initDecl) {
393393
/// \param enumDecl The enum to check.
394394
static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl,
395395
EnumDecl *enumDecl) {
396+
// Validate the enum and its raw type.
397+
tc.validateDecl(enumDecl);
398+
396399
// If the enum has a raw type (optional), it must be String or Int.
397400
Type rawType = enumDecl->getRawType();
398401
if (rawType) {

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc,
334334

335335
static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl,
336336
EnumDecl *enumDecl) {
337+
// Validate the enum and its raw type.
338+
tc.validateDecl(enumDecl);
337339

338340
// It must have a valid raw type.
339341
Type rawType = enumDecl->getRawType();

lib/Sema/TypeCheckDecl.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// A similar struct with Codable properties adopting Codable should get a
2+
// synthesized init(from:), along with the memberwise initializer.
13
//===--- TypeCheckDecl.cpp - Type Checking for Declarations ---------------===//
24
//
35
// This source file is part of the Swift.org open source project
@@ -8326,6 +8328,96 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
83268328
}
83278329
}
83288330

8331+
void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
8332+
DeclName member) {
8333+
auto baseName = member.getBaseName();
8334+
if (baseName.isSpecial())
8335+
return;
8336+
8337+
// Checks whether the target conforms to the given protocol. If the
8338+
// conformance is incomplete, check the conformance to force synthesis, if
8339+
// possible.
8340+
//
8341+
// Swallows diagnostics if conformance checking is already in progress (so we
8342+
// don't display diagnostics twice).
8343+
//
8344+
// Returns whether the target conforms to the protocol and the conformance is
8345+
// complete.
8346+
auto evaluateTargetConformanceTo = [&](ProtocolDecl *protocol) {
8347+
auto targetType = target->getDeclaredInterfaceType();
8348+
if (auto ref = conformsToProtocol(targetType, protocol, target,
8349+
ConformanceCheckFlags::Used,
8350+
SourceLoc())) {
8351+
if (auto *conformance =
8352+
dyn_cast_or_null<NormalProtocolConformance>(ref->getConcrete())) {
8353+
if (conformance->isIncomplete()) {
8354+
// Check conformance, forcing synthesis.
8355+
//
8356+
// If synthesizing conformance fails, this will produce diagnostics.
8357+
// If conformance checking was already in progress elsewhere, though,
8358+
// this could produce diagnostics twice.
8359+
//
8360+
// To prevent this duplication, we swallow the diagnostics if the
8361+
// state of the conformance is not Incomplete.
8362+
DiagnosticTransaction transaction(Context.Diags);
8363+
auto shouldSwallowDiagnostics =
8364+
conformance->getState() != ProtocolConformanceState::Incomplete;
8365+
8366+
checkConformance(conformance);
8367+
if (shouldSwallowDiagnostics)
8368+
transaction.abort();
8369+
8370+
return conformance->isComplete();
8371+
}
8372+
}
8373+
}
8374+
8375+
return false;
8376+
};
8377+
8378+
if (member.isSimpleName()) {
8379+
if (baseName.getIdentifier() == Context.Id_CodingKeys) {
8380+
// CodingKeys is a special type which may be synthesized as part of
8381+
// Encodable/Decodable conformance. If the target conforms to either
8382+
// protocol and would derive conformance to either, the type may be
8383+
// synthesized.
8384+
// If the target conforms to either and the conformance has not yet been
8385+
// evaluated, then we should do that here.
8386+
//
8387+
// Try to synthesize Decodable first. If that fails, try to synthesize
8388+
// Encodable. If either succeeds and CodingKeys should have been
8389+
// synthesized, it will be synthesized.
8390+
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
8391+
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
8392+
if (!evaluateTargetConformanceTo(decodableProto))
8393+
(void)evaluateTargetConformanceTo(encodableProto);
8394+
}
8395+
} else {
8396+
auto argumentNames = member.getArgumentNames();
8397+
if (argumentNames.size() != 1)
8398+
return;
8399+
8400+
auto argumentName = argumentNames.front();
8401+
if (baseName.getIdentifier() == Context.Id_init &&
8402+
argumentName == Context.Id_from) {
8403+
// init(from:) may be synthesized as part of derived confromance to the
8404+
// Decodable protocol.
8405+
// If the target should conform to the Decodable protocol, check the
8406+
// conformance here to attempt synthesis.
8407+
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
8408+
(void)evaluateTargetConformanceTo(decodableProto);
8409+
} else if (baseName.getIdentifier() == Context.Id_encode &&
8410+
argumentName == Context.Id_to) {
8411+
// encode(to:) may be synthesized as part of derived confromance to the
8412+
// Encodable protocol.
8413+
// If the target should conform to the Encodable protocol, check the
8414+
// conformance here to attempt synthesis.
8415+
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
8416+
(void)evaluateTargetConformanceTo(encodableProto);
8417+
}
8418+
}
8419+
}
8420+
83298421
void TypeChecker::addImplicitStructConformances(StructDecl *SD) {
83308422
// Type-check the protocol conformances of the struct decl to instantiate its
83318423
// derived conformances.

lib/Sema/TypeChecker.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,10 @@ class TypeChecker final : public LazyResolver {
12731273
addImplicitConstructors(nominal);
12741274
}
12751275

1276+
virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) override {
1277+
synthesizeMemberForLookup(nominal, member);
1278+
}
1279+
12761280
virtual void
12771281
resolveExternalDeclImplicitMembers(NominalTypeDecl *nominal) override {
12781282
handleExternalDecl(nominal);
@@ -1447,6 +1451,11 @@ class TypeChecker final : public LazyResolver {
14471451
/// enum with a raw type.
14481452
void addImplicitEnumConformances(EnumDecl *ED);
14491453

1454+
/// Synthesize the member with the given name on the target if applicable,
1455+
/// i.e. if the member is synthesizable and has not yet been added to the
1456+
/// target.
1457+
void synthesizeMemberForLookup(NominalTypeDecl *target, DeclName member);
1458+
14501459
/// The specified AbstractStorageDecl \c storage was just found to satisfy
14511460
/// the protocol property \c requirement. Ensure that it has the full
14521461
/// complement of accessors.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
2+
3+
class SynthesizedSuperclass : Codable {
4+
let superValue: Double = .pi
5+
}
6+
7+
// Classes which subclass something Codable should be able to override their
8+
// superclasses methods (the methods should be visible in member lookup despite
9+
// being synthesized).
10+
class ExplicitSubclass : SynthesizedSuperclass {
11+
required init(from decoder: Decoder) throws {
12+
try super.init(from: decoder)
13+
}
14+
15+
override func encode(to encoder: Encoder) throws {
16+
try super.encode(to: encoder)
17+
}
18+
}

0 commit comments

Comments
 (0)