Skip to content

Commit 7e4dfb7

Browse files
authored
Merge pull request #21154 from slavapestov/fully-concrete-generic-devirt-5.0
Fix devirtualization of conditional conformance where all generic parameters are concrete [5.0]
2 parents f83ec0c + b9cb4e8 commit 7e4dfb7

File tree

5 files changed

+72
-25
lines changed

5 files changed

+72
-25
lines changed

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4048,6 +4048,10 @@ SILFunctionType::SILFunctionType(GenericSignature *genericSig, ExtInfo ext,
40484048

40494049
// Make sure the interface types are sane.
40504050
if (genericSig) {
4051+
assert(!genericSig->areAllParamsConcrete() &&
4052+
"If all generic parameters are concrete, SILFunctionType should "
4053+
"not have a generic signature at all");
4054+
40514055
for (auto gparam : genericSig->getGenericParams()) {
40524056
(void)gparam;
40534057
assert(gparam->isCanonical() && "generic signature is not canonicalized");

lib/IRGen/GenProto.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3095,7 +3095,7 @@ GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM,
30953095
// We only need to do something here if the declaration context is
30963096
// somehow generic.
30973097
auto ncGenerics = typeDecl->getGenericSignatureOfContext();
3098-
if (!ncGenerics) return;
3098+
if (!ncGenerics || ncGenerics->areAllParamsConcrete()) return;
30993099

31003100
// Construct a representative function type.
31013101
auto generics = ncGenerics->getCanonicalSignature();

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)