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>.
This commit is contained in:
Slava Pestov
2018-12-08 22:14:37 -05:00
parent 29e4c67a72
commit 11b1ce2ee7
3 changed files with 66 additions and 23 deletions

View File

@@ -608,18 +608,30 @@ SILFunction *SILGenModule::emitProtocolWitness(
// The generic environment for the witness thunk. // The generic environment for the witness thunk.
auto *genericEnv = witness.getSyntheticEnvironment(); auto *genericEnv = witness.getSyntheticEnvironment();
CanGenericSignature genericSig;
if (genericEnv)
genericSig = genericEnv->getGenericSignature()->getCanonicalSignature();
// The type of the witness thunk. // The type of the witness thunk.
auto substReqtTy = auto reqtSubstTy = cast<AnyFunctionType>(
cast<AnyFunctionType>(reqtOrigTy.subst(reqtSubMap)->getCanonicalType()); reqtOrigTy->substGenericArgs(reqtSubMap)
->getCanonicalType(genericSig));
// If there's something to map to for the witness thunk, the conformance // Generic signatures where all parameters are concrete are lowered away
// should be phrased in the same terms. This particularly applies to classes // at the SILFunctionType level.
// where a thunk for a method in a conformance like `extension Class: P where if (genericSig && genericSig->areAllParamsConcrete()) {
// T: Q` will go from its native signature of `<τ_0_0 where τ_0_0: Q>` (with T genericSig = nullptr;
// canonicalised to τ_0_0), to `<τ_0_0, τ_1_0 where τ_0_0: Class<τ_1_0>, genericEnv = nullptr;
// τ_1_0: Q>` (with T now represented by τ_1_0). Find the right conformance by }
// looking for the conformance of 'Self'.
// Rewrite the conformance in terms of the requirement environment's Self
// type, which might have a different generic signature than the type
// itself.
//
// For example, if the conforming type is a class and the witness is defined
// in a protocol extension, the generic signature will have an additional
// generic parameter representing Self, so the generic parameters of the
// class will all be shifted down by one.
if (reqtSubMap) { if (reqtSubMap) {
auto requirement = conformance.getRequirement(); auto requirement = conformance.getRequirement();
auto self = requirement->getSelfInterfaceType()->getCanonicalType(); auto self = requirement->getSelfInterfaceType()->getCanonicalType();
@@ -627,12 +639,10 @@ SILFunction *SILGenModule::emitProtocolWitness(
conformance = *reqtSubMap.lookupConformance(self, requirement); conformance = *reqtSubMap.lookupConformance(self, requirement);
} }
CanAnyFunctionType reqtSubstTy = reqtSubstTy =
CanAnyFunctionType::get(genericEnv ? genericEnv->getGenericSignature() CanAnyFunctionType::get(genericSig,
->getCanonicalSignature() reqtSubstTy->getParams(),
: nullptr, reqtSubstTy.getResult(),
substReqtTy->getParams(),
substReqtTy.getResult(),
reqtOrigTy->getExtInfo()); reqtOrigTy->getExtInfo());
// Coroutine lowering requires us to provide these substitutions // Coroutine lowering requires us to provide these substitutions
@@ -649,10 +659,6 @@ SILFunction *SILGenModule::emitProtocolWitness(
} }
} }
// FIXME: this needs to pull out the conformances/witness-tables for any
// conditional requirements from the witness table and pass them to the
// underlying function in the thunk.
// Lower the witness thunk type with the requirement's abstraction level. // Lower the witness thunk type with the requirement's abstraction level.
auto witnessSILFnType = getNativeSILFunctionType( auto witnessSILFnType = getNativeSILFunctionType(
M, AbstractionPattern(reqtOrigTy), reqtSubstTy, M, AbstractionPattern(reqtOrigTy), reqtSubstTy,

View File

@@ -16,6 +16,13 @@ extension Conformance: P1 where A: P2 {
func normal() {} func normal() {}
func generic<T: P3>(_: T) {} func generic<T: P3>(_: T) {}
} }
// This is defined below but is emitted before any witness tables.
// Just make sure it does not have a generic signature.
//
// CHECK-LABEL: sil private [transparent] [thunk] @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP6normalyyFTW : $@convention(witness_method: P1) (@in_guaranteed SameTypeConcrete<Int>) -> ()
// CHECK-LABEL: sil_witness_table hidden <A where A : P2> Conformance<A>: P1 module conditional_conformance { // CHECK-LABEL: sil_witness_table hidden <A where A : P2> Conformance<A>: P1 module conditional_conformance {
// CHECK-NEXT: method #P1.normal!1: <Self where Self : P1> (Self) -> () -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance <A> Conformance<A> // CHECK-NEXT: method #P1.normal!1: <Self where Self : P1> (Self) -> () -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance <A> Conformance<A>
// 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> // 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 {
// CHECK-NEXT: conditional_conformance (A.AT: P2): dependent // CHECK-NEXT: conditional_conformance (A.AT: P2): dependent
// CHECK-NEXT: } // CHECK-NEXT: }
/*
FIXME: same type constraints are modelled incorrectly.
struct SameTypeConcrete<B> {} struct SameTypeConcrete<B> {}
extension SameTypeConcrete: P1 where B == Int { extension SameTypeConcrete: P1 where B == Int {
func normal() {} func normal() {}
func generic<T: P3>(_: T) {} func generic<T: P3>(_: T) {}
} }
// CHECK-LABEL: sil_witness_table hidden <B where B == Int> SameTypeConcrete<B>: P1 module conditional_conformance {
// CHECK-NEXT: method #P1.normal!1: <Self where Self : P1> (Self) -> () -> () : @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance <A> SameTypeConcrete<A>
// 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>
// CHECK-NEXT: }
struct SameTypeGeneric<C, D> {} struct SameTypeGeneric<C, D> {}
extension SameTypeGeneric: P1 where C == D { extension SameTypeGeneric: P1 where C == D {
func normal() {} func normal() {}
@@ -59,7 +69,6 @@ extension Everything: P1 where G: P2, H == Int, I == J, K == [L] {
func normal() {} func normal() {}
func generic<T: P3>(_: T) {} func generic<T: P3>(_: T) {}
} }
*/
struct IsP2: P2 {} struct IsP2: P2 {}
struct IsNotP2 {} struct IsNotP2 {}

View File

@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -O -Xllvm -sil-inline-generics=false -Xllvm -sil-disable-pass=GlobalOpt %s -emit-sil -sil-verify-all | %FileCheck %s // 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
public protocol Foo { public protocol Foo {
@@ -44,6 +44,7 @@ func genericLayer<T: Bar>(_ x: T) {
// CHECK-LABEL: sil @$s30devirt_conditional_conformance12throughLayeryyF : $@convention(thin) () -> () // CHECK-LABEL: sil @$s30devirt_conditional_conformance12throughLayeryyF : $@convention(thin) () -> ()
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF // CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF // CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
// CHECK: return
public func throughLayer() { public func throughLayer() {
genericLayer(Inner()) genericLayer(Inner())
} }
@@ -51,6 +52,33 @@ public func throughLayer() {
// CHECK-LABEL: sil @$s30devirt_conditional_conformance6directyyF : $@convention(thin) () -> () // CHECK-LABEL: sil @$s30devirt_conditional_conformance6directyyF : $@convention(thin) () -> ()
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF // CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF // CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
// CHECK: return
public func direct() { public func direct() {
callFoo(Outer(x: Inner())) callFoo(Outer(x: Inner()))
} }
// Conditional conformance that constraints all generic parameters completely
// <rdar://problem/46571799>
public protocol Fish {
func fish_method()
}
@inline(never)
func fish_marker() {}
extension Outer: Fish where T == Int {
public func fish_method() {
fish_marker()
}
}
func callFish<T : Fish>(_ x: T) {
x.fish_method()
}
// CHECK-LABEL: sil @$s30devirt_conditional_conformance8testFishyyF : $@convention(thin) () -> ()
// CHECK: function_ref @$s30devirt_conditional_conformance11fish_markeryyF : $@convention(thin) () -> ()
// CHECK: return
public func testFish() {
callFish(Outer(x: 0))
}