Skip to content

Commit 3518089

Browse files
committed
SIL: Don't include convenience initializers in vtables.
They should never be dynamically dispatched (unless `required`), so this entry should never be used. We were accidentally dynamically dispatching to them in convenience-to-convenience `self.init` delegations; fix that.
1 parent 7c0bb8c commit 3518089

File tree

3 files changed

+113
-5
lines changed

3 files changed

+113
-5
lines changed

include/swift/SIL/SILVTableVisitor.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ template <class T> class SILVTableVisitor {
100100
maybeAddEntry(constant, constant.requiresNewVTableEntry());
101101
}
102102

103-
// All constructors have their initializing constructor in the
104-
// vtable, which can be used by a convenience initializer.
105-
SILDeclRef constant(cd, SILDeclRef::Kind::Initializer);
106-
maybeAddEntry(constant, constant.requiresNewVTableEntry());
103+
// Designated and/or required initializers have their initializing
104+
// constructor in the vtable, which can be used by a convenience
105+
// initializer.
106+
if (cd->isDesignatedInit() || cd->isRequired()) {
107+
SILDeclRef constant(cd, SILDeclRef::Kind::Initializer);
108+
maybeAddEntry(constant, constant.requiresNewVTableEntry());
109+
}
107110
}
108111

109112
void maybeAddEntry(SILDeclRef declRef, bool needsNewEntry) {

lib/SIL/SILDeclRef.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,15 @@ swift::getMethodDispatch(AbstractFunctionDecl *method) {
4949
return MethodDispatch::Static;
5050

5151
// Members defined directly inside a class are dynamically dispatched.
52-
if (isa<ClassDecl>(dc))
52+
if (isa<ClassDecl>(dc)) {
53+
// Convenience initializers are not dynamically dispatched unless
54+
// required.
55+
if (auto ctor = dyn_cast<ConstructorDecl>(method)) {
56+
if (!ctor->isRequired() && ctor->isConvenienceInit())
57+
return MethodDispatch::Static;
58+
}
5359
return MethodDispatch::Class;
60+
}
5461

5562
// Imported class methods are dynamically dispatched.
5663
if (method->isObjC() && method->hasClangNode())
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
class X {
4+
init() {
5+
}
6+
7+
// Convenience inits must dynamically dispatch designated inits...
8+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfC
9+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfc
10+
// CHECK: class_method %7 : $X, #X.init!initializer.1
11+
convenience init(convenience: ()) {
12+
self.init()
13+
}
14+
15+
// ...but can statically invoke peer convenience inits
16+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC17doubleConvenienceACyt_tcfC
17+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC17doubleConvenienceACyt_tcfc
18+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfc
19+
convenience init(doubleConvenience: ()) {
20+
self.init(convenience: ())
21+
}
22+
23+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC8requiredACyt_tcfC
24+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC8requiredACyt_tcfc
25+
required init(required: ()) {
26+
}
27+
28+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC19requiredConvenienceACyt_tcfC
29+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC19requiredConvenienceACyt_tcfc
30+
required convenience init(requiredConvenience: ()) {
31+
self.init(required: ())
32+
}
33+
34+
// Convenience inits must dynamically dispatch required peer convenience inits
35+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC25requiredDoubleConvenienceACyt_tcfC
36+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation1XC25requiredDoubleConvenienceACyt_tcfc
37+
// CHECK: class_method %7 : $X, #X.init!initializer.1
38+
required convenience init(requiredDoubleConvenience: ()) {
39+
self.init(requiredDoubleConvenience: ())
40+
}
41+
}
42+
43+
class Y: X {
44+
// This is really a designated initializer. Ensure that we don't try to
45+
// treat it as an override of the base class convenience initializer (and
46+
// override a nonexistent vtable entry) just because it has the same name.
47+
init(convenience: ()) {
48+
super.init()
49+
}
50+
51+
required init(required: ()) { super.init() }
52+
required init(requiredConvenience: ()) { super.init() }
53+
required init(requiredDoubleConvenience: ()) { super.init() }
54+
}
55+
56+
// CHECK-LABEL: sil hidden @$S32convenience_init_peer_delegation11invocations2xtyAA1XCm_tF
57+
func invocations(xt: X.Type) {
58+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XCACycfC
59+
_ = X()
60+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfC
61+
_ = X(convenience: ())
62+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC17doubleConvenienceACyt_tcfC
63+
_ = X(doubleConvenience: ())
64+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC8requiredACyt_tcfC
65+
_ = X(required: ())
66+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC19requiredConvenienceACyt_tcfC
67+
_ = X(requiredConvenience: ())
68+
// CHECK: function_ref @$S32convenience_init_peer_delegation1XC25requiredDoubleConvenienceACyt_tcfC
69+
_ = X(requiredDoubleConvenience: ())
70+
71+
// CHECK: class_method %0 : $@thick X.Type, #X.init!allocator.1
72+
_ = xt.init(required: ())
73+
// CHECK: class_method %0 : $@thick X.Type, #X.init!allocator.1
74+
_ = xt.init(requiredConvenience: ())
75+
// CHECK: class_method %0 : $@thick X.Type, #X.init!allocator.1
76+
_ = xt.init(requiredDoubleConvenience: ())
77+
}
78+
79+
// CHECK-LABEL: sil_vtable X
80+
// -- designated init()
81+
// CHECK-NOT: @$S32convenience_init_peer_delegation1XCACycfC
82+
// CHECK: @$S32convenience_init_peer_delegation1XCACycfc
83+
84+
// -- no unrequired convenience inits
85+
// CHECK-NOT: @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfC
86+
// CHECK-NOT: @$S32convenience_init_peer_delegation1XC0A0ACyt_tcfc
87+
// CHECK-NOT: @$S32convenience_init_peer_delegation1XC17doubleConvenienceACyt_tcfC
88+
// CHECK-NOT: @$S32convenience_init_peer_delegation1XC17doubleConvenienceACyt_tcfc
89+
90+
// -- designated init(required:)
91+
// CHECK: @$S32convenience_init_peer_delegation1XC8requiredACyt_tcfC
92+
// CHECK: @$S32convenience_init_peer_delegation1XC8requiredACyt_tcfc
93+
// -- convenience init(requiredConvenience:)
94+
// CHECK: @$S32convenience_init_peer_delegation1XC19requiredConvenienceACyt_tcfC
95+
// CHECK: @$S32convenience_init_peer_delegation1XC19requiredConvenienceACyt_tcfc
96+
// -- convenience init(requiredDoubleConvenience:)
97+
// CHECK: @$S32convenience_init_peer_delegation1XC25requiredDoubleConvenienceACyt_tcfC
98+
// CHECK: @$S32convenience_init_peer_delegation1XC25requiredDoubleConvenienceACyt_tcfc

0 commit comments

Comments
 (0)