Skip to content

Commit 071caf2

Browse files
author
Itai Ferber
committed
Additional responses to feedback
* Make Decodable synthesis lazy again * Remove side effects from assertions * Assert on superclass failable initializers (and verify via a unit test) * Fix space typo * Remove more needless instances of single-element arrays * Unambiguously refer to init() in unit tests * Add failable superclass init tests and a test to ensure Decodable fails to be inherited when superclass synthesis fails * Add tests for structs to ensure they receive no-argument initializers where appropriate
1 parent a30e911 commit 071caf2

File tree

5 files changed

+58
-33
lines changed

5 files changed

+58
-33
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -804,12 +804,8 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl,
804804

805805
/// Synthesizes the body for `init(from decoder: Decoder) throws`.
806806
///
807-
/// \param tc The \c TypeChecker to use in looking up potential superclass
808-
/// Decodable conformance.
809-
///
810807
/// \param initDecl The function decl whose body to synthesize.
811-
static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
812-
AbstractFunctionDecl *initDecl) {
808+
static void deriveBodyDecodable_init(AbstractFunctionDecl *initDecl) {
813809
// struct Foo : Codable {
814810
// var x: Int
815811
// var y: String
@@ -971,10 +967,7 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
971967
// superclass is Decodable, or super.init() if it is not.
972968
if (auto *classDecl = dyn_cast<ClassDecl>(targetDecl)) {
973969
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
974-
auto superType = superclassDecl->getDeclaredInterfaceType();
975-
if (tc.conformsToProtocol(superType,
976-
C.getProtocol(KnownProtocolKind::Decodable),
977-
superclassDecl, ConformanceCheckOptions())) {
970+
if (superclassIsDecodable(classDecl)) {
978971
// Need to generate `try super.init(from: container.superDecoder())`
979972

980973
// container.superDecoder
@@ -1014,12 +1007,15 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
10141007
DeclName initName(C, C.Id_init, ArrayRef<Identifier>());
10151008

10161009
// We need to look this up in the superclass to see if it throws.
1017-
ConstructorDecl *superInitDecl = nullptr;
10181010
auto result = superclassDecl->lookupDirect(initName);
10191011

10201012
// We should have bailed one level up if this were not available.
1021-
assert(!result.empty() &&
1022-
(superInitDecl = dyn_cast<ConstructorDecl>(result.front())));
1013+
assert(!result.empty());
1014+
1015+
// If the init is failable, we should have already bailed one level
1016+
// above.
1017+
ConstructorDecl *superInitDecl = cast<ConstructorDecl>(result.front());
1018+
assert(superInitDecl->getFailability() == OTK_None);
10231019

10241020
// super
10251021
auto *superRef = new (C) SuperRefExpr(initDecl->getImplicitSelfDecl(),
@@ -1034,10 +1030,6 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
10341030
ArrayRef<Expr *>(),
10351031
ArrayRef<Identifier>());
10361032

1037-
// If super.init is failable, super.init()!
1038-
if (superInitDecl->getFailability() != OTK_None)
1039-
callExpr = new (C) ForceValueExpr(callExpr, SourceLoc());
1040-
10411033
// If super.init throws, try super.init()
10421034
if (superInitDecl->hasThrows())
10431035
callExpr = new (C) TryExpr(SourceLoc(), callExpr, Type(),
@@ -1048,8 +1040,9 @@ static BraceStmt *deriveBodyDecodable_init(TypeChecker &tc,
10481040
}
10491041
}
10501042

1051-
return BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
1052-
/*implicit=*/true);
1043+
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
1044+
/*implicit=*/true);
1045+
initDecl->setBody(body);
10531046
}
10541047

10551048
/// Synthesizes a function declaration for `init(from: Decoder) throws` with a
@@ -1114,7 +1107,7 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl,
11141107
SourceLoc(), selfDecl, paramList,
11151108
/*GenericParams=*/nullptr, target);
11161109
initDecl->setImplicit();
1117-
initDecl->setBody(deriveBodyDecodable_init(tc, initDecl));
1110+
initDecl->setBodySynthesizer(deriveBodyDecodable_init);
11181111

11191112
// This constructor should be marked as `required` for non-final classes.
11201113
if (isa<ClassDecl>(target) && !target->getAttrs().hasAttribute<FinalAttr>()) {
@@ -1229,6 +1222,7 @@ static bool canSynthesize(TypeChecker &tc, NominalTypeDecl *target,
12291222
// isn't failable.
12301223
tc.diagnose(initializer, diag::decodable_super_init_is_failable_here,
12311224
requirement->getFullName(), memberName);
1225+
return false;
12321226
}
12331227
}
12341228
}

lib/Sema/TypeCheckDecl.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7963,17 +7963,17 @@ static void diagnoseClassWithoutInitializers(TypeChecker &tc,
79637963
ASTContext &C = tc.Context;
79647964
auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable);
79657965
auto superclassType = superclassDecl->getDeclaredInterfaceType();
7966-
if (auto ref = tc. conformsToProtocol(superclassType, decodableProto,
7967-
superclassDecl,
7968-
ConformanceCheckOptions(),
7969-
SourceLoc())) {
7966+
if (auto ref = tc.conformsToProtocol(superclassType, decodableProto,
7967+
superclassDecl,
7968+
ConformanceCheckOptions(),
7969+
SourceLoc())) {
79707970
// super conforms to Decodable, so we've failed to inherit init(from:).
79717971
// Let's suggest overriding it here.
79727972
//
79737973
// We're going to diagnose on the concrete init(from:) decl if it exists
79747974
// and isn't implicit; otherwise, on the subclass itself.
79757975
ValueDecl *diagDest = classDecl;
7976-
auto initFrom = DeclName(C, C.Id_init, (Identifier[1]){ C.Id_from });
7976+
auto initFrom = DeclName(C, C.Id_init, C.Id_from);
79777977
auto result = tc.lookupMember(superclassDecl, superclassType, initFrom,
79787978
NameLookupFlags::ProtocolMembers |
79797979
NameLookupFlags::IgnoreAccessibility);
@@ -8204,10 +8204,11 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
82048204
// NOTE: Lookups of synthesized initializers MUST come after
82058205
// decl->setAddedImplicitInitializers() in case synthesis requires
82068206
// protocol conformance checking, which might be recursive here.
8207+
// FIXME: Disable this code and prevent _any_ implicit constructors from doing
8208+
// this. Investigate why this hasn't worked otherwise.
82078209
DeclName synthesizedInitializers[1] = {
82088210
// init(from:) is synthesized by derived conformance to Decodable.
8209-
DeclName(Context, DeclBaseName(Context.Id_init),
8210-
(Identifier[1]) { Context.Id_from })
8211+
DeclName(Context, DeclBaseName(Context.Id_init), Context.Id_from)
82118212
};
82128213

82138214
auto initializerIsSynthesized = [=](ConstructorDecl *initializer) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class NoInitializers { // expected-error {{class 'NoInitializers' has no initial
88

99
func foo() {
1010
// The class should not receive a default constructor.
11-
let _ = NoInitializers.init // expected-error {{type 'NoInitializers' has no member 'init'}}
11+
let _ = NoInitializers.init() // expected-error {{type 'NoInitializers' has no member 'init'}}
1212
}
1313
}
1414

@@ -23,7 +23,7 @@ class CodableNoExplicitInitializers : Codable {
2323
let _ = CodableNoExplicitInitializers.encode(to:)
2424

2525
// It should not, however, receive a default constructor.
26-
let _ = CodableNoExplicitInitializers.init
26+
let _ = CodableNoExplicitInitializers.init() // expected-error {{missing argument for parameter 'from' in call}}
2727
}
2828
}
2929

@@ -32,7 +32,7 @@ class DefaultConstructed {
3232
var x: Double = .pi
3333

3434
func foo() {
35-
let _ = DefaultConstructed.init
35+
let _ = DefaultConstructed.init()
3636
}
3737
}
3838

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ class InaccessibleNonDecodableSuper {
3131
class InaccessibleNonDecodableSub : InaccessibleNonDecodableSuper, Decodable { // expected-error {{type 'InaccessibleNonDecodableSub' does not conform to protocol 'Decodable'}}
3232
}
3333

34+
// Non-Decodable superclasses of synthesized Decodable classes must have a
35+
// non-failable init().
36+
class FailableNonDecodableSuper {
37+
init?() {} // expected-note {{cannot automatically synthesize 'init(from:)' because implementation would need to call 'init()', which is failable}}
38+
}
39+
40+
class FailableNonDecodableSub : FailableNonDecodableSuper, Decodable { // expected-error {{type 'FailableNonDecodableSub' does not conform to protocol 'Decodable'}}
41+
}
42+
43+
// Subclasses of classes whose Decodable synthesis fails should not inherit
44+
// conformance.
45+
class FailedSynthesisDecodableSuper : Decodable { // expected-error {{type 'FailedSynthesisDecodableSuper' does not conform to protocol 'Decodable'}}
46+
enum CodingKeys : String, CodingKey {
47+
case nonexistent // expected-note {{CodingKey case 'nonexistent' does not match any stored properties}}
48+
}
49+
}
50+
51+
class FailedSynthesisDecodableSub : FailedSynthesisDecodableSuper { // expected-note {{did you mean 'init'?}}
52+
func foo() {
53+
// Decodable should fail to synthesis or be inherited.
54+
let _ = FailedSynthesisDecodableSub.init(from:) // expected-error {{type 'FailedSynthesisDecodableSub' has no member 'init(from:)'}}
55+
}
56+
}
57+
3458
// Subclasses of Decodable classes which can't inherit their initializers should
3559
// produce diagnostics.
3660
class DecodableSuper : Decodable {

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ struct InitialValueNoInitializers {
3131

3232
func foo() {
3333
// The struct should receive a memberwise initializer.
34-
let _ = NoInitializers.init(x:)
34+
let _ = InitialValueNoInitializers.init(x:)
35+
36+
// The struct should receive a no-argument initializer.
37+
let _ = InitialValueNoInitializers.init()
3538
}
3639
}
3740

@@ -40,10 +43,13 @@ struct InitialValueCodableNoExplicitInitializers : Codable {
4043

4144
func foo() {
4245
// The struct should receive a synthesized init(from:) and encode(to:).
43-
let _ = CodableNoExplicitInitializers.init(from:)
44-
let _ = CodableNoExplicitInitializers.encode(to:)
46+
let _ = InitialValueCodableNoExplicitInitializers.init(from:)
47+
let _ = InitialValueCodableNoExplicitInitializers.encode(to:)
4548

4649
// It should still receive a memberwise initializer.
47-
let _ = CodableNoExplicitInitializers.init(x:)
50+
let _ = InitialValueCodableNoExplicitInitializers.init(x:)
51+
52+
// It should still receive a no-argument initializer.
53+
let _ = InitialValueCodableNoExplicitInitializers.init()
4854
}
4955
}

0 commit comments

Comments
 (0)