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.
auto *genericEnv = witness.getSyntheticEnvironment();
CanGenericSignature genericSig;
if (genericEnv)
genericSig = genericEnv->getGenericSignature()->getCanonicalSignature();
// The type of the witness thunk.
auto substReqtTy =
cast<AnyFunctionType>(reqtOrigTy.subst(reqtSubMap)->getCanonicalType());
auto reqtSubstTy = cast<AnyFunctionType>(
reqtOrigTy->substGenericArgs(reqtSubMap)
->getCanonicalType(genericSig));
// If there's something to map to for the witness thunk, the conformance
// should be phrased in the same terms. This particularly applies to classes
// where a thunk for a method in a conformance like `extension Class: P where
// T: Q` will go from its native signature of `<τ_0_0 where τ_0_0: Q>` (with T
// canonicalised to τ_0_0), to `<τ_0_0, τ_1_0 where τ_0_0: Class<τ_1_0>,
// τ_1_0: Q>` (with T now represented by τ_1_0). Find the right conformance by
// looking for the conformance of 'Self'.
// Generic signatures where all parameters are concrete are lowered away
// at the SILFunctionType level.
if (genericSig && genericSig->areAllParamsConcrete()) {
genericSig = nullptr;
genericEnv = nullptr;
}
// 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) {
auto requirement = conformance.getRequirement();
auto self = requirement->getSelfInterfaceType()->getCanonicalType();
@@ -627,12 +639,10 @@ SILFunction *SILGenModule::emitProtocolWitness(
conformance = *reqtSubMap.lookupConformance(self, requirement);
}
CanAnyFunctionType reqtSubstTy =
CanAnyFunctionType::get(genericEnv ? genericEnv->getGenericSignature()
->getCanonicalSignature()
: nullptr,
substReqtTy->getParams(),
substReqtTy.getResult(),
reqtSubstTy =
CanAnyFunctionType::get(genericSig,
reqtSubstTy->getParams(),
reqtSubstTy.getResult(),
reqtOrigTy->getExtInfo());
// 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.
auto witnessSILFnType = getNativeSILFunctionType(
M, AbstractionPattern(reqtOrigTy), reqtSubstTy,

View File

@@ -16,6 +16,13 @@ extension Conformance: P1 where A: P2 {
func normal() {}
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-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>
@@ -34,14 +41,17 @@ extension ConformanceAssoc: P1 where A: P4, A.AT: P2 {
// CHECK-NEXT: conditional_conformance (A.AT: P2): dependent
// CHECK-NEXT: }
/*
FIXME: same type constraints are modelled incorrectly.
struct SameTypeConcrete<B> {}
extension SameTypeConcrete: P1 where B == Int {
func normal() {}
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> {}
extension SameTypeGeneric: P1 where C == D {
func normal() {}
@@ -59,7 +69,6 @@ extension Everything: P1 where G: P2, H == Int, I == J, K == [L] {
func normal() {}
func generic<T: P3>(_: T) {}
}
*/
struct IsP2: P2 {}
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 {
@@ -44,6 +44,7 @@ func genericLayer<T: Bar>(_ x: T) {
// CHECK-LABEL: sil @$s30devirt_conditional_conformance12throughLayeryyF : $@convention(thin) () -> ()
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
// CHECK: return
public func throughLayer() {
genericLayer(Inner())
}
@@ -51,6 +52,33 @@ public func throughLayer() {
// CHECK-LABEL: sil @$s30devirt_conditional_conformance6directyyF : $@convention(thin) () -> ()
// CHECK: function_ref @$s30devirt_conditional_conformance10foo_markeryyF
// CHECK: function_ref @$s30devirt_conditional_conformance10bar_markeryyF
// CHECK: return
public func direct() {
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))
}