Skip to content

Commit abdaaef

Browse files
committed
[Serialization] Drop overriding initializers with missing bases.
This is the same as the last few commits, but with the additional complication of designated initializers affecting other behavior around the type. In particular, convenience initializers cannot be invoked on subclasses if the designated initializers are not all present on the subclass. If a designated initializer is dropped, it's not possible to satisfy that. It would be nice to do better here, since a class's initializers are mostly independent of the superclass's initializers. Unfortunately, it still affects whether /this/ class can inherit convenience initializers, as well as vtable layout. This is conservative, at least.
1 parent b76774f commit abdaaef

File tree

3 files changed

+223
-14
lines changed

3 files changed

+223
-14
lines changed

lib/Serialization/Deserialization.cpp

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -299,17 +299,32 @@ namespace {
299299
const char XRefError::ID = '\0';
300300

301301
class OverrideError : public llvm::ErrorInfo<OverrideError> {
302+
public:
303+
enum Kind {
304+
Normal,
305+
DesignatedInitializer
306+
};
307+
308+
private:
302309
friend ErrorInfo;
303310
static const char ID;
304311

305312
DeclName name;
313+
Kind kind;
314+
306315
public:
307-
explicit OverrideError(DeclName name) : name(name) {}
316+
317+
explicit OverrideError(DeclName name, Kind kind = Normal)
318+
: name(name), kind(kind) {}
308319

309320
void log(raw_ostream &OS) const override {
310321
OS << "could not find '" << name << "' in parent class";
311322
}
312323

324+
Kind getKind() const {
325+
return kind;
326+
}
327+
313328
std::error_code convertToErrorCode() const override {
314329
// This is a deprecated part of llvm::Error, so we just return a very
315330
// generic value.
@@ -2694,6 +2709,29 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
26942709
genericEnvID, interfaceID,
26952710
overriddenID, rawAccessLevel,
26962711
argNameIDs);
2712+
2713+
// Resolve the name ids.
2714+
SmallVector<Identifier, 2> argNames;
2715+
for (auto argNameID : argNameIDs)
2716+
argNames.push_back(getIdentifier(argNameID));
2717+
DeclName name(ctx, ctx.Id_init, argNames);
2718+
2719+
Optional<swift::CtorInitializerKind> initKind =
2720+
getActualCtorInitializerKind(storedInitKind);
2721+
2722+
auto overridden = getDeclChecked(overriddenID);
2723+
if (!overridden) {
2724+
llvm::handleAllErrors(overridden.takeError(),
2725+
[](const XRefError &) { /* expected */ },
2726+
[this](std::unique_ptr<llvm::ErrorInfoBase> unhandled) {
2727+
fatal(std::move(unhandled));
2728+
});
2729+
auto kind = OverrideError::Normal;
2730+
if (initKind == CtorInitializerKind::Designated)
2731+
kind = OverrideError::DesignatedInitializer;
2732+
return llvm::make_error<OverrideError>(name, kind);
2733+
}
2734+
26972735
auto parent = getDeclContext(contextID);
26982736
if (declOrOffset.isComplete())
26992737
return declOrOffset;
@@ -2702,16 +2740,10 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
27022740
if (declOrOffset.isComplete())
27032741
return declOrOffset;
27042742

2705-
// Resolve the name ids.
2706-
SmallVector<Identifier, 2> argNames;
2707-
for (auto argNameID : argNameIDs)
2708-
argNames.push_back(getIdentifier(argNameID));
2709-
27102743
OptionalTypeKind failability = OTK_None;
27112744
if (auto actualFailability = getActualOptionalTypeKind(rawFailability))
27122745
failability = *actualFailability;
27132746

2714-
DeclName name(ctx, ctx.Id_init, argNames);
27152747
auto ctor =
27162748
createDecl<ConstructorDecl>(name, SourceLoc(),
27172749
failability, /*FailabilityLoc=*/SourceLoc(),
@@ -2761,11 +2793,10 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
27612793
ctor->setImplicit();
27622794
if (hasStubImplementation)
27632795
ctor->setStubImplementation(true);
2764-
if (auto initKind = getActualCtorInitializerKind(storedInitKind))
2765-
ctor->setInitKind(*initKind);
2766-
if (auto overridden
2767-
= dyn_cast_or_null<ConstructorDecl>(getDecl(overriddenID)))
2768-
ctor->setOverriddenDecl(overridden);
2796+
if (initKind.hasValue())
2797+
ctor->setInitKind(initKind.getValue());
2798+
if (auto overriddenCtor = cast_or_null<ConstructorDecl>(overridden.get()))
2799+
ctor->setOverriddenDecl(overriddenCtor);
27692800
break;
27702801
}
27712802

@@ -4429,9 +4460,17 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) {
44294460
if (!getContext().LangOpts.EnableDeserializationRecovery)
44304461
fatal(next.takeError());
44314462

4432-
// Silently drop the member if it had an override-related problem.
4463+
// Drop the member if it had an override-related problem.
4464+
auto handleMissingOverrideBase = [container](const OverrideError &error) {
4465+
if (error.getKind() != OverrideError::DesignatedInitializer)
4466+
return;
4467+
auto *containingClass = dyn_cast<ClassDecl>(container);
4468+
if (!containingClass)
4469+
return;
4470+
containingClass->setHasMissingDesignatedInitializers();
4471+
};
44334472
llvm::handleAllErrors(next.takeError(),
4434-
[](const OverrideError &) { /* expected */ },
4473+
handleMissingOverrideBase,
44354474
[this](std::unique_ptr<llvm::ErrorInfoBase> unhandled) {
44364475
fatal(std::move(unhandled));
44374476
});

test/Serialization/Recovery/Inputs/custom-modules/Overrides.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,50 @@
6868
// - (nonnull Element)objectAtIndexedSubscript:(nonnull id)key;
6969
#endif
7070
@end
71+
72+
73+
@interface DesignatedInitDisappearsBase : Object
74+
- (nonnull instancetype)init __attribute__((objc_designated_initializer));
75+
- (nonnull instancetype)initConvenience:(long)value;
76+
#if !BAD
77+
- (nonnull instancetype)initWithValue:(long)value __attribute__((objc_designated_initializer));
78+
#else
79+
//- (nonnull instancetype)initWithValue:(long)value __attribute__((objc_designated_initializer));
80+
#endif
81+
@end
82+
83+
@interface OnlyDesignatedInitDisappearsBase : Object
84+
- (nonnull instancetype)initConvenience:(long)value;
85+
#if !BAD
86+
- (nonnull instancetype)initWithValue:(long)value __attribute__((objc_designated_initializer));
87+
#else
88+
//- (nonnull instancetype)initWithValue:(long)value __attribute__((objc_designated_initializer));
89+
#endif
90+
@end
91+
92+
@interface ConvenienceInitDisappearsBase : Object
93+
- (nonnull instancetype)init __attribute__((objc_designated_initializer));
94+
- (nonnull instancetype)initConvenience:(long)value;
95+
#if !BAD
96+
- (nonnull instancetype)initWithValue:(long)value;
97+
#else
98+
//- (nonnull instancetype)initWithValue:(long)value;
99+
#endif
100+
@end
101+
102+
@interface UnknownInitDisappearsBase : Object
103+
- (nonnull instancetype)init;
104+
#if !BAD
105+
- (nonnull instancetype)initWithValue:(long)value;
106+
#else
107+
//- (nonnull instancetype)initWithValue:(long)value;
108+
#endif
109+
@end
110+
111+
@interface OnlyUnknownInitDisappearsBase : Object
112+
#if !BAD
113+
- (nonnull instancetype)initWithValue:(long)value;
114+
#else
115+
//- (nonnull instancetype)initWithValue:(long)value;
116+
#endif
117+
@end

test/Serialization/Recovery/overrides.swift

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,60 @@
55

66
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -enable-experimental-deserialization-recovery | %FileCheck -check-prefix CHECK-RECOVERY %s
77

8+
// RUN: %target-swift-frontend -typecheck %s -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -enable-experimental-deserialization-recovery -D TEST -verify
9+
810
// REQUIRES: objc_interop
911

12+
#if TEST
13+
import Lib
14+
15+
func testInitializers() {
16+
_ = D1_DesignatedInitDisappears()
17+
_ = D1_DesignatedInitDisappears(value: 0) // expected-error {{incorrect argument label in call}}
18+
_ = D1_DesignatedInitDisappears(convenience: 0)
19+
20+
_ = D2_OnlyDesignatedInitDisappears(value: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
21+
_ = D2_OnlyDesignatedInitDisappears(convenience: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
22+
23+
_ = D3_ConvenienceInitDisappears()
24+
_ = D3_ConvenienceInitDisappears(value: 0) // expected-error {{incorrect argument label in call}}
25+
_ = D3_ConvenienceInitDisappears(convenience: 0)
26+
27+
_ = D4_UnknownInitDisappears()
28+
_ = D4_UnknownInitDisappears(value: 0) // expected-error {{argument passed to call that takes no arguments}}
29+
30+
// FIXME: Why does 'init()' show up in the generated interface if it can't be
31+
// called?
32+
_ = D5_OnlyUnknownInitDisappears() // expected-error {{cannot be constructed because it has no accessible initializers}}
33+
_ = D5_OnlyUnknownInitDisappears(value: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
34+
}
35+
36+
func testSubclassInitializers() {
37+
class DesignatedInitDisappearsSub : D1_DesignatedInitDisappears {}
38+
_ = DesignatedInitDisappearsSub()
39+
_ = DesignatedInitDisappearsSub(value: 0) // expected-error {{argument passed to call that takes no arguments}}
40+
_ = DesignatedInitDisappearsSub(convenience: 0) // expected-error {{argument passed to call that takes no arguments}}
41+
42+
class OnlyDesignatedInitDisappearsSub : D2_OnlyDesignatedInitDisappears {}
43+
_ = OnlyDesignatedInitDisappearsSub(value: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
44+
_ = OnlyDesignatedInitDisappearsSub(convenience: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
45+
46+
class ConvenienceInitDisappearsSub : D3_ConvenienceInitDisappears {}
47+
_ = ConvenienceInitDisappearsSub()
48+
_ = ConvenienceInitDisappearsSub(value: 0) // expected-error {{incorrect argument label in call}}
49+
_ = ConvenienceInitDisappearsSub(convenience: 0) // still inheritable
50+
51+
class UnknownInitDisappearsSub : D4_UnknownInitDisappears {}
52+
_ = UnknownInitDisappearsSub()
53+
_ = UnknownInitDisappearsSub(value: 0) // expected-error {{argument passed to call that takes no arguments}}
54+
55+
class OnlyUnknownInitDisappearsSub : D5_OnlyUnknownInitDisappears {}
56+
_ = OnlyUnknownInitDisappearsSub() // expected-error {{cannot be constructed because it has no accessible initializers}}
57+
_ = OnlyUnknownInitDisappearsSub(value: 0) // expected-error {{cannot be constructed because it has no accessible initializers}}
58+
}
59+
60+
#else // TEST
61+
1062
import Overrides
1163

1264
// Please use prefixes to keep the printed parts of this file in alphabetical
@@ -111,3 +163,74 @@ public class C4_GenericKeyedSubscriptDisappears : GenericKeyedSubscriptDisappear
111163
// CHECK-RECOVERY-LABEL: class C4_GenericKeyedSubscriptDisappears : GenericKeyedSubscriptDisappearsBase<Base> {
112164
// CHECK-RECOVERY-NEXT: init()
113165
// CHECK-RECOVERY-NEXT: {{^}$}}
166+
167+
168+
open class D1_DesignatedInitDisappears : DesignatedInitDisappearsBase {
169+
public override init() { fatalError() }
170+
public override init(value: Int) { fatalError() }
171+
}
172+
173+
// CHECK-LABEL: class D1_DesignatedInitDisappears : DesignatedInitDisappearsBase {
174+
// CHECK-NEXT: init()
175+
// CHECK-NEXT: init(value: Int)
176+
// CHECK-NEXT: {{^}$}}
177+
178+
// CHECK-RECOVERY-LABEL: class D1_DesignatedInitDisappears : DesignatedInitDisappearsBase {
179+
// CHECK-RECOVERY-NEXT: init()
180+
// CHECK-RECOVERY-NEXT: {{^}$}}
181+
182+
183+
open class D2_OnlyDesignatedInitDisappears : OnlyDesignatedInitDisappearsBase {
184+
public override init(value: Int) { fatalError() }
185+
}
186+
187+
// CHECK-LABEL: class D2_OnlyDesignatedInitDisappears : OnlyDesignatedInitDisappearsBase {
188+
// CHECK-NEXT: init(value: Int)
189+
// CHECK-NEXT: {{^}$}}
190+
191+
// CHECK-RECOVERY-LABEL: class D2_OnlyDesignatedInitDisappears : OnlyDesignatedInitDisappearsBase {
192+
// CHECK-RECOVERY-NEXT: {{^}$}}
193+
194+
195+
open class D3_ConvenienceInitDisappears : ConvenienceInitDisappearsBase {
196+
public override init() { fatalError() }
197+
}
198+
199+
// CHECK-LABEL: class D3_ConvenienceInitDisappears : ConvenienceInitDisappearsBase {
200+
// CHECK-NEXT: init()
201+
// CHECK-NEXT: {{^}$}}
202+
203+
// CHECK-RECOVERY-LABEL: class D3_ConvenienceInitDisappears : ConvenienceInitDisappearsBase {
204+
// CHECK-RECOVERY-NEXT: init()
205+
// CHECK-RECOVERY-NEXT: {{^}$}}
206+
207+
208+
open class D4_UnknownInitDisappears : UnknownInitDisappearsBase {
209+
public override init() { fatalError() }
210+
public override init(value: Int) { fatalError() }
211+
}
212+
213+
// CHECK-LABEL: class D4_UnknownInitDisappears : UnknownInitDisappearsBase {
214+
// CHECK-NEXT: init()
215+
// CHECK-NEXT: init(value: Int)
216+
// CHECK-NEXT: {{^}$}}
217+
218+
// CHECK-RECOVERY-LABEL: class D4_UnknownInitDisappears : UnknownInitDisappearsBase {
219+
// CHECK-RECOVERY-NEXT: init()
220+
// CHECK-RECOVERY-NEXT: {{^}$}}
221+
222+
223+
open class D5_OnlyUnknownInitDisappears : OnlyUnknownInitDisappearsBase {
224+
public override init(value: Int) { fatalError() }
225+
}
226+
227+
// CHECK-LABEL: class D5_OnlyUnknownInitDisappears : OnlyUnknownInitDisappearsBase {
228+
// CHECK-NEXT: init(value: Int)
229+
// CHECK-NEXT: init()
230+
// CHECK-NEXT: {{^}$}}
231+
232+
// CHECK-RECOVERY-LABEL: class D5_OnlyUnknownInitDisappears : OnlyUnknownInitDisappearsBase {
233+
// CHECK-RECOVERY-NEXT: init()
234+
// CHECK-RECOVERY-NEXT: {{^}$}}
235+
236+
#endif // TEST

0 commit comments

Comments
 (0)