Skip to content

Commit a30e911

Browse files
author
Itai Ferber
committed
Responses to feedback
* Use `isSpecificProtocol` instead of `==` * Synthesize Encodable implementation lazily * Don’t mark conformances as Used when they don’t need to be * Avoid creating explicit one-element arrays * Use lookupDirect instead of walking AST manually * Produce diagnostic for Decodable classes whose non-Decodable superclass has a failable init * Filter lazy vars manually since getStoredProperties() does not always do so * Amend Decodable class diagnostic text * Check for enum RawType errors only if the type was already validated * Update unit tests to match changes
1 parent f873f5e commit a30e911

8 files changed

+123
-101
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,13 +2073,15 @@ NOTE(codable_codingkeys_type_is_not_an_enum_here,none,
20732073
NOTE(codable_codingkeys_type_does_not_conform_here,none,
20742074
"cannot automatically synthesize %0 because 'CodingKeys' does not conform to CodingKey", (Type))
20752075
NOTE(decodable_no_super_init_here,none,
2076-
"cannot automatically synthesize %0 because superclass does not have a callable 'init()' or 'init(from:)'", (DeclName))
2076+
"cannot automatically synthesize %0 because superclass does not have a callable %1", (DeclName, DeclName))
20772077
NOTE(decodable_super_init_not_designated_here,none,
2078-
"cannot automatically synthesize %0 because required superclass initializer %1 is not designated", (DeclName, DeclName))
2078+
"cannot automatically synthesize %0 because implementation would need to call %1, which is not designated", (DeclName, DeclName))
20792079
NOTE(decodable_inaccessible_super_init_here,none,
2080-
"cannot automatically synthesize %0 because required superclass initializer %1 is inaccessible due to "
2080+
"cannot automatically synthesize %0 because implementation would need to call %1, which is inaccessible due to "
20812081
"'%select{private|fileprivate|internal|%error|%error}2' protection level",
20822082
(DeclName, DeclName, Accessibility))
2083+
NOTE(decodable_super_init_is_failable_here,none,
2084+
"cannot automatically synthesize %0 because implementation would need to call %1, which is failable", (DeclName, DeclName))
20832085
NOTE(decodable_suggest_overriding_init_here,none,
20842086
"did you mean to override 'init(from:)'?", ())
20852087
NOTE(codable_suggest_overriding_init_here,none,

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,7 @@ static bool
184184
validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
185185
NominalTypeDecl *target, ProtocolDecl *proto) {
186186
// Look through all var decls in the given type.
187-
// * Filter out lazy/computed vars (currently already done by
188-
// getStoredProperties).
187+
// * Filter out lazy/computed vars.
189188
// * Filter out ones which are present in the given decl (by name).
190189
//
191190
// If any of the entries in the CodingKeys decl are not present in the type
@@ -197,6 +196,9 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
197196
// against its CodingKey entry, it will get removed.
198197
llvm::SmallDenseMap<Identifier, VarDecl *, 8> properties;
199198
for (auto *varDecl : target->getStoredProperties(/*skipInaccessible=*/true)) {
199+
if (varDecl->getAttrs().hasAttribute<LazyAttr>())
200+
continue;
201+
200202
properties[varDecl->getName()] = varDecl;
201203
}
202204

@@ -242,7 +244,7 @@ validateCodingKeysEnum(TypeChecker &tc, EnumDecl *codingKeysDecl,
242244
// we can skip them on encode. On decode, though, we can only skip them if
243245
// they have a default value.
244246
if (!properties.empty() &&
245-
proto == tc.Context.getProtocol(KnownProtocolKind::Decodable)) {
247+
proto->isSpecificProtocol(KnownProtocolKind::Decodable)) {
246248
for (auto it = properties.begin(); it != properties.end(); ++it) {
247249
if (it->second->getParentInitializer() != nullptr) {
248250
// Var has a default value.
@@ -303,6 +305,9 @@ static CodingKeysValidity hasValidCodingKeysEnum(TypeChecker &tc,
303305
return CodingKeysValidity(/*hasType=*/true, /*isValid=*/false);
304306
}
305307

308+
// If the decl hasn't been validated yet, do so.
309+
tc.validateDecl(codingKeysTypeDecl);
310+
306311
// CodingKeys may be a typealias. If so, follow the alias to its canonical
307312
// type.
308313
auto codingKeysType = codingKeysTypeDecl->getDeclaredInterfaceType();
@@ -381,6 +386,9 @@ static EnumDecl *synthesizeCodingKeysEnum(TypeChecker &tc,
381386
// conforms to {En,De}codable, add it to the enum.
382387
bool allConform = true;
383388
for (auto *varDecl : target->getStoredProperties(/*skipInaccessible=*/true)) {
389+
if (varDecl->getAttrs().hasAttribute<LazyAttr>())
390+
continue;
391+
384392
auto conformance = varConformsToCodable(tc, target->getDeclContext(),
385393
varDecl, proto);
386394
switch (conformance) {
@@ -528,7 +536,7 @@ static CallExpr *createContainerKeyedByCall(ASTContext &C, DeclContext *DC,
528536
/// Synthesizes the body for `func encode(to encoder: Encoder) throws`.
529537
///
530538
/// \param encodeDecl The function decl whose body to synthesize.
531-
static BraceStmt *deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) {
539+
static void deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) {
532540
// struct Foo : Codable {
533541
// var x: Int
534542
// var y: String
@@ -696,8 +704,9 @@ static BraceStmt *deriveBodyEncodable_encode(AbstractFunctionDecl *encodeDecl) {
696704
statements.push_back(tryExpr);
697705
}
698706

699-
return BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
700-
/*implicit=*/true);
707+
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
708+
/*implicit=*/true);
709+
encodeDecl->setBody(body);
701710
}
702711

703712
/// Synthesizes a function declaration for `encode(to: Encoder) throws` with a
@@ -756,7 +765,7 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl,
756765
TypeLoc::withoutLoc(returnType),
757766
target);
758767
encodeDecl->setImplicit();
759-
encodeDecl->setBody(deriveBodyEncodable_encode(encodeDecl));
768+
encodeDecl->setBodySynthesizer(deriveBodyEncodable_encode);
760769

761770
// This method should be marked as 'override' for classes inheriting Encodable
762771
// conformance from a parent class.
@@ -962,10 +971,10 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
962971
// superclass is Decodable, or super.init() if it is not.
963972
if (auto *classDecl = dyn_cast<ClassDecl>(targetDecl)) {
964973
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
965-
auto superType = superclassDecl->getDeclaredInterfaceType();
974+
auto superType = superclassDecl->getDeclaredInterfaceType();
966975
if (tc.conformsToProtocol(superType,
967976
C.getProtocol(KnownProtocolKind::Decodable),
968-
superclassDecl, ConformanceCheckFlags::Used)) {
977+
superclassDecl, ConformanceCheckOptions())) {
969978
// Need to generate `try super.init(from: container.superDecoder())`
970979

971980
// container.superDecoder
@@ -984,7 +993,7 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
984993
SourceLoc(), /*Implicit=*/true);
985994

986995
// super.init(from:)
987-
auto initName = DeclName(C, C.Id_init, (Identifier[1]){ C.Id_from });
996+
auto initName = DeclName(C, C.Id_init, C.Id_from);
988997
auto *initCall = new (C) UnresolvedDotExpr(superRef, SourceLoc(),
989998
initName, DeclNameLoc(),
990999
/*Implicit=*/true);
@@ -1005,21 +1014,12 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
10051014
DeclName initName(C, C.Id_init, ArrayRef<Identifier>());
10061015

10071016
// We need to look this up in the superclass to see if it throws.
1008-
// We should have bailed one level up if super.init() is not available.
10091017
ConstructorDecl *superInitDecl = nullptr;
1010-
for (auto *decl : superclassDecl->getMembers()) {
1011-
auto *ctorDecl = dyn_cast<ConstructorDecl>(decl);
1012-
if (!ctorDecl)
1013-
continue;
1014-
1015-
if (ctorDecl->getParameters()->size() == 0) {
1016-
superInitDecl = ctorDecl;
1017-
break;
1018-
}
1019-
}
1018+
auto result = superclassDecl->lookupDirect(initName);
10201019

10211020
// We should have bailed one level up if this were not available.
1022-
assert(superInitDecl);
1021+
assert(!result.empty() &&
1022+
(superInitDecl = dyn_cast<ConstructorDecl>(result.front())));
10231023

10241024
// super
10251025
auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(),
@@ -1030,18 +1030,20 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
10301030
initName, DeclNameLoc(),
10311031
/*Implicit=*/true);
10321032
// super.init() call
1033-
auto *callExpr = CallExpr::createImplicit(C, superInitRef,
1033+
Expr *callExpr = CallExpr::createImplicit(C, superInitRef,
10341034
ArrayRef<Expr *>(),
10351035
ArrayRef<Identifier>());
10361036

1037-
if (superInitDecl->hasThrows()) {
1038-
// try super.init()
1039-
auto *tryExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
1040-
/*Implicit=*/true);
1041-
statements.push_back(tryExpr);
1042-
} else {
1043-
statements.push_back(callExpr);
1044-
}
1037+
// If super.init is failable, super.init()!
1038+
if (superInitDecl->getFailability() != OTK_None)
1039+
callExpr = new (C) ForceValueExpr(callExpr, SourceLoc());
1040+
1041+
// If super.init throws, try super.init()
1042+
if (superInitDecl->hasThrows())
1043+
callExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
1044+
/*Implicit=*/true);
1045+
1046+
statements.push_back(callExpr);
10451047
}
10461048
}
10471049
}
@@ -1178,14 +1180,14 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target,
11781180
// synthesize CodingKeys.
11791181
ASTContext &C = tc.Context;
11801182
auto *classDecl = dyn_cast<ClassDecl>(target);
1181-
if (proto == C.getProtocol(KnownProtocolKind::Decodable) && classDecl) {
1183+
if (proto->isSpecificProtocol(KnownProtocolKind::Decodable) && classDecl) {
11821184
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
11831185
DeclName memberName;
11841186
auto superType = superclassDecl->getDeclaredInterfaceType();
11851187
if (tc.conformsToProtocol(superType, proto, superclassDecl,
11861188
ConformanceCheckFlags::Used)) {
11871189
// super.init(from:) must be accessible.
1188-
memberName = cast<ConstructorDecl>(requirement)->getEffectiveFullName();
1190+
memberName = cast<ConstructorDecl>(requirement)->getFullName();
11891191
} else {
11901192
// super.init() must be accessible.
11911193
// Passing an empty params array constructs a compound name with no
@@ -1199,15 +1201,16 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target,
11991201
if (result.empty()) {
12001202
// No super initializer for us to call.
12011203
tc.diagnose(superclassDecl, diag::decodable_no_super_init_here,
1202-
requirement->getFullName());
1204+
requirement->getFullName(), memberName);
12031205
return false;
12041206
} else if (result.size() > 1) {
12051207
// There are multiple results for this lookup. We'll end up producing a
12061208
// diagnostic later complaining about duplicate methods (if we haven't
12071209
// already), so just bail with a general error.
12081210
return false;
12091211
} else {
1210-
auto *initializer = cast<ConstructorDecl>(result.front().Decl);
1212+
auto *initializer =
1213+
cast<ConstructorDecl>(result.front().getValueDecl());
12111214
if (!initializer->isDesignatedInit()) {
12121215
// We must call a superclass's designated initializer.
12131216
tc.diagnose(initializer,
@@ -1221,6 +1224,11 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target,
12211224
requirement->getFullName(), memberName,
12221225
accessScope.accessibilityForDiagnostics());
12231226
return false;
1227+
} else if (initializer->getFailability() != OTK_None) {
1228+
// We can't call super.init() if it's failable, since init(from:)
1229+
// isn't failable.
1230+
tc.diagnose(initializer, diag::decodable_super_init_is_failable_here,
1231+
requirement->getFullName(), memberName);
12241232
}
12251233
}
12261234
}

lib/Sema/DerivedConformanceCodingKey.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,9 @@ static bool canSynthesizeCodingKey(TypeChecker &tc, Decl *parentDecl,
408408
return false;
409409
}
410410

411-
if (!enumDecl->getInherited().empty() &&
412-
enumDecl->getInherited().front().isError())
411+
auto inherited = enumDecl->getInherited();
412+
if (!inherited.empty() && inherited.front().wasValidated() &&
413+
inherited.front().isError())
413414
return false;
414415

415416
// If it meets all of those requirements, we can synthesize CodingKey

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ static bool canSynthesizeRawRepresentable(TypeChecker &tc, Decl *parentDecl,
344344
auto parentDC = cast<DeclContext>(parentDecl);
345345
rawType = parentDC->mapTypeIntoContext(rawType);
346346

347-
if (!enumDecl->getInherited().empty() &&
348-
enumDecl->getInherited().front().isError())
347+
auto inherited = enumDecl->getInherited();
348+
if (!inherited.empty() && inherited.front().wasValidated() &&
349+
inherited.front().isError())
349350
return false;
350351

351352
// The raw type must be Equatable, so that we have a suitable ~= for

lib/Sema/TypeCheckDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7978,8 +7978,8 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
79787978
NameLookupFlags::ProtocolMembers |
79797979
NameLookupFlags::IgnoreAccessibility);
79807980

7981-
if (!result.empty() && !result.front()->isImplicit())
7982-
diagDest = result.front();
7981+
if (!result.empty() && !result.front().getValueDecl()->isImplicit())
7982+
diagDest = result.front().getValueDecl();
79837983

79847984
auto diagName = diag::decodable_suggest_overriding_init_here;
79857985

@@ -7998,7 +7998,7 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
79987998
// subclass doesn't directly implement encode(to:).
79997999
// The direct lookup here won't see an encode(to:) if it is inherited
80008000
// from the superclass.
8001-
auto encodeTo = DeclName(C, C.Id_encode, (Identifier[1]){ C.Id_to });
8001+
auto encodeTo = DeclName(C, C.Id_encode, C.Id_to);
80028002
if (classDecl->lookupDirect(encodeTo).empty())
80038003
diagName = diag::codable_suggest_overriding_init_here;
80048004
}

test/decl/protocol/special/coding/class_codable_failure_diagnostics.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ class C1 : Codable {
88
var c: Nested = Nested()
99

1010
// CHECK: error: type 'C1' does not conform to protocol 'Decodable'
11-
// CHECK: note: cannot automatically synthesize 'Decodable' because 'C1.Nested' does not conform to 'Decodable'
1211
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
12+
// CHECK: note: cannot automatically synthesize 'Decodable' because 'C1.Nested' does not conform to 'Decodable'
1313

1414
// CHECK: error: type 'C1' does not conform to protocol 'Encodable'
15-
// CHECK: note: cannot automatically synthesize 'Encodable' because 'C1.Nested' does not conform to 'Encodable'
1615
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
16+
// CHECK: note: cannot automatically synthesize 'Encodable' because 'C1.Nested' does not conform to 'Encodable'
1717
}
1818

1919
// Codable class with non-enum CodingKeys.
@@ -30,12 +30,12 @@ class C2 : Codable {
3030
}
3131

3232
// CHECK: error: type 'C2' does not conform to protocol 'Decodable'
33-
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
3433
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
34+
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' is not an enum
3535

3636
// CHECK: error: type 'C2' does not conform to protocol 'Encodable'
37-
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
3837
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
38+
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' is not an enum
3939
}
4040

4141
// Codable class with CodingKeys not conforming to CodingKey.
@@ -51,12 +51,12 @@ class C3 : Codable {
5151
}
5252

5353
// CHECK: error: type 'C3' does not conform to protocol 'Decodable'
54-
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
5554
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
55+
// CHECK: note: cannot automatically synthesize 'Decodable' because 'CodingKeys' does not conform to CodingKey
5656

5757
// CHECK: error: type 'C3' does not conform to protocol 'Encodable'
58-
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
5958
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
59+
// CHECK: note: cannot automatically synthesize 'Encodable' because 'CodingKeys' does not conform to CodingKey
6060
}
6161

6262
// Codable class with extraneous CodingKeys
@@ -75,16 +75,16 @@ class C4 : Codable {
7575
}
7676

7777
// CHECK: error: type 'C4' does not conform to protocol 'Decodable'
78+
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
7879
// CHECK: note: CodingKey case 'a2' does not match any stored properties
7980
// CHECK: note: CodingKey case 'b2' does not match any stored properties
8081
// CHECK: note: CodingKey case 'c2' does not match any stored properties
81-
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
8282

8383
// CHECK: error: type 'C4' does not conform to protocol 'Encodable'
84+
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
8485
// CHECK: note: CodingKey case 'a2' does not match any stored properties
8586
// CHECK: note: CodingKey case 'b2' does not match any stored properties
8687
// CHECK: note: CodingKey case 'c2' does not match any stored properties
87-
// CHECK: note: protocol requires function 'encode(to:)' with type 'Encodable'
8888
}
8989

9090
// Codable class with non-decoded property (which has no default value).
@@ -98,12 +98,12 @@ class C5 : Codable {
9898
case c
9999
}
100100

101-
// CHECK: error: class 'C5' has no initializers
102-
// CHECK: note: stored property 'b' without initial value prevents synthesized initializers
103-
104101
// CHECK: error: type 'C5' does not conform to protocol 'Decodable'
105-
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
106102
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
103+
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
104+
105+
// CHECK: error: class 'C5' has no initializers
106+
// CHECK: note: stored property 'b' without initial value prevents synthesized initializers
107107
}
108108

109109
// Codable class with non-decoded property (which has no default value).
@@ -122,8 +122,8 @@ class C6 : Codable {
122122
}
123123

124124
// CHECK: error: type 'C6' does not conform to protocol 'Decodable'
125-
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
126125
// CHECK: note: protocol requires initializer 'init(from:)' with type 'Decodable'
126+
// CHECK: note: cannot automatically synthesize 'Decodable' because 'b' does not have a matching CodingKey and does not have a default value
127127
}
128128

129129
// Classes cannot yet synthesize Encodable or Decodable in extensions.

0 commit comments

Comments
 (0)