Skip to content

Commit e08e9a9

Browse files
committed
SILGen: Drop generic signature from witness thunk if all parameters are concrete
SIL functions for AST declarations do this, and the SIL verifier enforces this, so let's do it for witness thunks too, fixing a devirtualizer crash. Fixes <rdar://problem/46571799>.
1 parent c14e1a3 commit e08e9a9

File tree

3 files changed

+67
-24
lines changed

3 files changed

+67
-24
lines changed

lib/SILGen/SILGenType.cpp

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -608,31 +608,41 @@ SILFunction *SILGenModule::emitProtocolWitness(
608608

609609
// The generic environment for the witness thunk.
610610
auto *genericEnv = witness.getSyntheticEnvironment();
611+
CanGenericSignature genericSig;
612+
if (genericEnv)
613+
genericSig = genericEnv->getGenericSignature()->getCanonicalSignature();
611614

612615
// The type of the witness thunk.
613-
auto substReqtTy =
614-
cast<AnyFunctionType>(reqtOrigTy.subst(reqtSubMap)->getCanonicalType());
615-
616-
// If there's something to map to for the witness thunk, the conformance
617-
// should be phrased in the same terms. This particularly applies to classes
618-
// where a thunk for a method in a conformance like `extension Class: P where
619-
// T: Q` will go from its native signature of `<τ_0_0 where τ_0_0: Q>` (with T
620-
// canonicalised to τ_0_0), to `<τ_0_0, τ_1_0 where τ_0_0: Class<τ_1_0>,
621-
// τ_1_0: Q>` (with T now represented by τ_1_0). Find the right conformance by
622-
// looking for the conformance of 'Self'.
616+
auto reqtSubstTy = cast<AnyFunctionType>(
617+
reqtOrigTy->substGenericArgs(reqtSubMap)
618+
->getCanonicalType(genericSig));
619+
620+
// Generic signatures where all parameters are concrete are lowered away
621+
// at the SILFunctionType level.
622+
if (genericSig && genericSig->areAllParamsConcrete()) {
623+
genericSig = nullptr;
624+
genericEnv = nullptr;
625+
}
626+
627+
// Rewrite the conformance in terms of the requirement environment's Self
628+
// type, which might have a different generic signature than the type
629+
// itself.
630+
//
631+
// For example, if the conforming type is a class and the witness is defined
632+
// in a protocol extension, the generic signature will have an additional
633+
// generic parameter representing Self, so the generic parameters of the
634+
// class will all be shifted down by one.
623635
if (reqtSubMap) {
624636
auto requirement = conformance.getRequirement();
625637
auto self = requirement->getSelfInterfaceType()->getCanonicalType();
626638

627639
conformance = *reqtSubMap.lookupConformance(self, requirement);
628640
}
629641

630-
CanAnyFunctionType reqtSubstTy =
631-
CanAnyFunctionType::get(genericEnv ? genericEnv->getGenericSignature()
632-
->getCanonicalSignature()
633-
: nullptr,
634-
substReqtTy->getParams(),
635-
substReqtTy.getResult(),
642+
reqtSubstTy =
643+
CanAnyFunctionType::get(genericSig,
644+
reqtSubstTy->getParams(),
645+
reqtSubstTy.getResult(),
636646
reqtOrigTy->getExtInfo());
637647

638648
// Coroutine lowering requires us to provide these substitutions
@@ -649,10 +659,6 @@ SILFunction *SILGenModule::emitProtocolWitness(
649659
}
650660
}
651661

652-
// FIXME: this needs to pull out the conformances/witness-tables for any
653-
// conditional requirements from the witness table and pass them to the
654-
// underlying function in the thunk.
655-
656662
// Lower the witness thunk type with the requirement's abstraction level.
657663
auto witnessSILFnType = getNativeSILFunctionType(
658664
M, AbstractionPattern(reqtOrigTy), reqtSubstTy,

test/SILGen/conditional_conformance.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ extension Conformance: P1 where A: P2 {
1616
func normal() {}
1717
func generic<T: P3>(_: T) {}
1818
}
19+
20+
// This is defined below but is emitted before any witness tables.
21+
// Just make sure it does not have a generic signature.
22+
//
23+
// CHECK-LABEL: sil private [transparent] [thunk] @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP6normalyyFTW : $@convention(witness_method: P1) (@in_guaranteed SameTypeConcrete<Int>) -> ()
24+
25+
1926
// CHECK-LABEL: sil_witness_table hidden <A where A : P2> Conformance<A>: P1 module conditional_conformance {
2027
// CHECK-NEXT: method #P1.normal!1: <Self where Self : P1> (Self) -> () -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance <A> Conformance<A>
2128
// CHECK-NEXT: method #P1.generic!1: <Self where Self : P1><T where T : P3> (Self) -> (T) -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP7genericyyqd__AA2P3Rd__lFTW // protocol witness for P1.generic<A>(_:) in conformance <A> Conformance<A>
@@ -34,14 +41,17 @@ extension ConformanceAssoc: P1 where A: P4, A.AT: P2 {
3441
// CHECK-NEXT: conditional_conformance (A.AT: P2): dependent
3542
// CHECK-NEXT: }
3643

37-
/*
38-
FIXME: same type constraints are modelled incorrectly.
3944
struct SameTypeConcrete<B> {}
4045
extension SameTypeConcrete: P1 where B == Int {
4146
func normal() {}
4247
func generic<T: P3>(_: T) {}
4348
}
4449

50+
// CHECK-LABEL: sil_witness_table hidden <B where B == Int> SameTypeConcrete<B>: P1 module conditional_conformance {
51+
// CHECK-NEXT: method #P1.normal!1: <Self where Self : P1> (Self) -> () -> () : @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance <A> SameTypeConcrete<A>
52+
// CHECK-NEXT: method #P1.generic!1: <Self where Self : P1><T where T : P3> (Self) -> (T) -> () : @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP7genericyyqd__AA2P3Rd__lFTW // protocol witness for P1.generic<A>(_:) in conformance <A> SameTypeConcrete<A>
53+
// CHECK-NEXT: }
54+
4555
struct SameTypeGeneric<C, D> {}
4656
extension SameTypeGeneric: P1 where C == D {
4757
func normal() {}
@@ -59,7 +69,6 @@ extension Everything: P1 where G: P2, H == Int, I == J, K == [L] {
5969
func normal() {}
6070
func generic<T: P3>(_: T) {}
6171
}
62-
*/
6372

6473
struct IsP2: P2 {}
6574
struct IsNotP2 {}

test/SILOptimizer/devirt_conditional_conformance.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -O -Xllvm -sil-inline-generics=false -Xllvm -sil-disable-pass=GlobalOpt %s -emit-sil -sil-verify-all | %FileCheck %s
1+
// RUN: %target-swift-frontend -O -Xllvm -sil-inline-generics=false -Xllvm -sil-disable-pass=GlobalOpt %s -emit-sil -sil-verify-all | tee /tmp/xxx | %FileCheck %s
22

33

44
public protocol Foo {
@@ -44,13 +44,41 @@ func genericLayer<T: Bar>(_ x: T) {
4444
// CHECK-LABEL: sil @$s30devirt_conditional_conformance12throughLayeryyF : $@convention(thin) () -> ()
4545
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
4646
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
47+
// CHECK: return
4748
public func throughLayer() {
4849
genericLayer(Inner())
4950
}
5051

5152
// CHECK-LABEL: sil @$s30devirt_conditional_conformance6directyyF : $@convention(thin) () -> ()
5253
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
5354
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
55+
// CHECK: return
5456
public func direct() {
5557
callFoo(Outer(x: Inner()))
5658
}
59+
60+
// Conditional conformance that constraints all generic parameters completely
61+
// <rdar://problem/46571799>
62+
public protocol Fish {
63+
func fish_method()
64+
}
65+
66+
@inline(never)
67+
func fish_marker() {}
68+
69+
extension Outer: Fish where T == Int {
70+
public func fish_method() {
71+
fish_marker()
72+
}
73+
}
74+
75+
func callFish<T : Fish>(_ x: T) {
76+
x.fish_method()
77+
}
78+
79+
// CHECK-LABEL: sil @$s30devirt_conditional_conformance8testFishyyF : $@convention(thin) () -> ()
80+
// CHECK: function_ref @$s30devirt_conditional_conformance11fish_markeryyF : $@convention(thin) () -> ()
81+
// CHECK: return
82+
public func testFish() {
83+
callFish(Outer(x: 0))
84+
}

0 commit comments

Comments
 (0)