Skip to content

Commit a500390

Browse files
committed
SILGen: Fix invalid SIL emitted when protocol requirement witnessed by base class convenience init
If the derived class is final and the base class is not, the protocol requirement can be witnessed by a non-required initializer. Non-required initializers have initializing but not allocating entry points in the vtable. The fix here statically dispatches to the allocating initializer instead. The test verifies that the allocating entry point uses alloc_ref_dynamic, so you will get an instance of the correct subclass. This fix does not handle the resilient case where the conformance is defined in a different module, and the original module adds an override of the convenience init later. I filed <rdar://problem/40639124> to track the resilient case. Fixes <rdar://problem/40003840>.
1 parent 96c8f9b commit a500390

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

lib/SILGen/SILGenPoly.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3345,11 +3345,20 @@ static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness) {
33453345
// A natively ObjC method witness referenced this way will end up going
33463346
// through its native thunk, which will redispatch the method after doing
33473347
// bridging just like we want.
3348-
if (isFinal || isExtension || witness.isForeignToNativeThunk()
3349-
// Hack--We emit a static thunk for ObjC allocating constructors.
3350-
|| (decl->hasClangNode() && witness.kind == SILDeclRef::Kind::Allocator))
3348+
if (isFinal || isExtension || witness.isForeignToNativeThunk())
33513349
return WitnessDispatchKind::Static;
33523350

3351+
if (witness.kind == SILDeclRef::Kind::Allocator) {
3352+
// Non-required initializers can witness a protocol requirement if the class
3353+
// is final, so we can statically dispatch to them.
3354+
if (!cast<ConstructorDecl>(decl)->isRequired())
3355+
return WitnessDispatchKind::Static;
3356+
3357+
// We emit a static thunk for ObjC allocating constructors.
3358+
if (decl->hasClangNode())
3359+
return WitnessDispatchKind::Static;
3360+
}
3361+
33533362
// Otherwise emit a class method.
33543363
return WitnessDispatchKind::Class;
33553364
}

test/SILGen/witness-init-requirement-with-base-class-init.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,32 @@ class Dog: Animal, BestFriend {}
2020
// CHECK-LABEL: sil private [transparent] [thunk] @$S4main3DogCAA10BestFriendA2aDP6createxyFZTW
2121
// CHECK: [[SELF:%.*]] = apply
2222
// CHECK: unchecked_ref_cast [[SELF]] : $Animal to $Dog
23+
24+
class Base {
25+
init() {}
26+
27+
convenience init(x: Int) {
28+
self.init()
29+
}
30+
}
31+
32+
protocol Initable {
33+
init(x: Int)
34+
}
35+
36+
final class Derived : Base, Initable {}
37+
38+
// CHECK-LABEL: sil hidden @$S4main4BaseC1xACSi_tcfC : $@convention(method) (Int, @thick Base.Type) -> @owned Base
39+
// CHECK: [[SELF:%.*]] = alloc_ref_dynamic %1 : $@thick Base.Type, $Base
40+
// CHECK: [[METHOD:%.*]] = function_ref @$S4main4BaseC1xACSi_tcfc
41+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]](%0, [[SELF]])
42+
// CHECK-NEXT: return [[RESULT]]
43+
44+
// CHECK-LABEL: sil private [transparent] [thunk] @$S4main7DerivedCAA8InitableA2aDP1xxSi_tcfCTW : $@convention(witness_method: Initable) (Int, @thick Derived.Type) -> @out Derived
45+
// CHECK: [[SELF:%.*]] = upcast %2 : $@thick Derived.Type to $@thick Base.Type
46+
// CHECK: [[METHOD:%.*]] = function_ref @$S4main4BaseC1xACSi_tcfC
47+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]](%1, [[SELF]])
48+
// CHECK-NEXT: [[NEW_SELF:%.*]] = unchecked_ref_cast [[RESULT]] : $Base to $Derived
49+
// CHECK-NEXT: store [[NEW_SELF]] to [init] %0 : $*Derived
50+
// CHECK-NEXT: [[TUPLE:%.*]] = tuple ()
51+
// CHECK-NEXT: return [[TUPLE]]

0 commit comments

Comments
 (0)