diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index f85d0799fe7..ed81e28c9b2 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -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(reqtOrigTy.subst(reqtSubMap)->getCanonicalType()); + auto reqtSubstTy = cast( + 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, diff --git a/test/SILGen/conditional_conformance.swift b/test/SILGen/conditional_conformance.swift index 9f781a96f2c..cc3dfd39028 100644 --- a/test/SILGen/conditional_conformance.swift +++ b/test/SILGen/conditional_conformance.swift @@ -16,6 +16,13 @@ extension Conformance: P1 where A: P2 { func normal() {} func generic(_: 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) -> () + + // CHECK-LABEL: sil_witness_table hidden Conformance: P1 module conditional_conformance { // CHECK-NEXT: method #P1.normal!1: (Self) -> () -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance Conformance // CHECK-NEXT: method #P1.generic!1: (Self) -> (T) -> () : @$s23conditional_conformance11ConformanceVyxGAA2P1A2A2P2RzlAaEP7genericyyqd__AA2P3Rd__lFTW // protocol witness for P1.generic(_:) in conformance Conformance @@ -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 {} extension SameTypeConcrete: P1 where B == Int { func normal() {} func generic(_: T) {} } +// CHECK-LABEL: sil_witness_table hidden SameTypeConcrete: P1 module conditional_conformance { +// CHECK-NEXT: method #P1.normal!1: (Self) -> () -> () : @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP6normalyyFTW // protocol witness for P1.normal() in conformance SameTypeConcrete +// CHECK-NEXT: method #P1.generic!1: (Self) -> (T) -> () : @$s23conditional_conformance16SameTypeConcreteVyxGAA2P1AASiRszlAaEP7genericyyqd__AA2P3Rd__lFTW // protocol witness for P1.generic(_:) in conformance SameTypeConcrete +// CHECK-NEXT: } + struct SameTypeGeneric {} 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) {} } -*/ struct IsP2: P2 {} struct IsNotP2 {} diff --git a/test/SILOptimizer/devirt_conditional_conformance.swift b/test/SILOptimizer/devirt_conditional_conformance.swift index 71c678239b7..b1437b869b3 100644 --- a/test/SILOptimizer/devirt_conditional_conformance.swift +++ b/test/SILOptimizer/devirt_conditional_conformance.swift @@ -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(_ 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 +// +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(_ 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)) +} \ No newline at end of file