Skip to content

Commit 1b9946b

Browse files
committed
[Devirtualizer] Handle default witnesses for generic requirements.
The witness thunks for default witnesses are different from the witness thunks for normal witnesses, because default witnesses take 'Self' (the whole conforming type) rather than having it substituted away. Cope with this difference while still substituting the innermost generic parameters for a generic requirement.
1 parent b316b56 commit 1b9946b

File tree

2 files changed

+67
-20
lines changed

2 files changed

+67
-20
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ static void getWitnessMethodSubstitutions(
791791
GenericSignature *requirementSig,
792792
GenericSignature *witnessThunkSig,
793793
ArrayRef<Substitution> origSubs,
794+
bool isDefaultWitness,
794795
SmallVectorImpl<Substitution> &newSubs) {
795796

796797
if (witnessThunkSig == nullptr)
@@ -806,19 +807,28 @@ static void getWitnessMethodSubstitutions(
806807
SubstitutionMap subMap;
807808

808809
// Take apart substitutions from the conforming type.
809-
//
810-
// If `Self` maps to a bound generic type, this gives us the
811-
// substitutions for the concrete type's generic parameters.
812-
auto witnessSubs = getSubstitutionsForProtocolConformance(conformanceRef);
813-
810+
ArrayRef<Substitution> witnessSubs;
811+
auto *rootConformance = conformance->getRootNormalConformance();
812+
auto *witnessSig = rootConformance->getGenericSignature();
814813
unsigned depth = 0;
815-
if (!witnessSubs.empty()) {
816-
auto *rootConformance = conformance->getRootNormalConformance();
817-
depth = rootConformance->getGenericSignature()->getGenericParams().back()
818-
->getDepth() + 1;
819-
auto *witnessSig = rootConformance->getGenericSignature();
814+
if (isDefaultWitness) {
815+
// For default witnesses, we substitute all of Self.
816+
auto gp = witnessThunkSig->getGenericParams().front()->getCanonicalType();
817+
subMap.addSubstitution(gp, origSubs.front().getReplacement());
818+
subMap.addConformances(gp, origSubs.front().getConformances());
819+
820+
// For default witnesses, innermost generic parameters are always at
821+
// depth 1.
822+
depth = 1;
823+
} else {
824+
// If `Self` maps to a bound generic type, this gives us the
825+
// substitutions for the concrete type's generic parameters.
826+
witnessSubs = getSubstitutionsForProtocolConformance(conformanceRef);
820827

821-
witnessSig->getSubstitutionMap(witnessSubs, subMap);
828+
if (!witnessSubs.empty()) {
829+
witnessSig->getSubstitutionMap(witnessSubs, subMap);
830+
depth = witnessSig->getGenericParams().back()->getDepth() + 1;
831+
}
822832
}
823833

824834
// Next, take apart caller-side substitutions.
@@ -890,17 +900,15 @@ static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F,
890900

891901
ArrayRef<Substitution> origSubs = AI.getSubstitutions();
892902

893-
if (F->getLoweredFunctionType()->getRepresentation()
894-
== SILFunctionTypeRepresentation::WitnessMethod &&
895-
F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol(
896-
*Module.getSwiftModule())) {
897-
// Default witness thunks use the generic signature of the requirement.
898-
NewSubs.append(origSubs.begin(), origSubs.end());
899-
return;
900-
}
903+
bool isDefaultWitness =
904+
F->getLoweredFunctionType()->getRepresentation()
905+
== SILFunctionTypeRepresentation::WitnessMethod &&
906+
F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol(
907+
*Module.getSwiftModule())
908+
== CRef.getRequirement();
901909

902910
getWitnessMethodSubstitutions(Module, CRef, requirementSig, witnessThunkSig,
903-
origSubs, NewSubs);
911+
origSubs, isDefaultWitness, NewSubs);
904912
}
905913

906914
/// Check if an upcast is legal.

test/SILOptimizer/devirt_default_witness_method.sil

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import Builtin
55
import Swift
66
import SwiftShims
77

8+
public protocol P { }
9+
public protocol Q : P { }
10+
extension Int : P, Q { }
11+
812
public protocol ResilientProtocol {
913
func defaultA()
1014
}
@@ -35,6 +39,28 @@ sil_witness_table <T> ConformingGenericStruct<T> : ResilientProtocol module prot
3539
method #ResilientProtocol.defaultA!1: @defaultA
3640
}
3741

42+
public protocol ResilientProtocolWithGeneric {
43+
func defaultB<T: Q>(_: T)
44+
}
45+
46+
sil @defaultB : $@convention(witness_method) <Self where Self : ResilientProtocolWithGeneric><T where T : P> (@in T, @in_guaranteed Self) -> () {
47+
bb0(%0 : $*T, %1 : $*Self):
48+
%result = tuple ()
49+
return %result : $()
50+
}
51+
52+
sil_default_witness_table ResilientProtocolWithGeneric {
53+
method #ResilientProtocolWithGeneric.defaultB!1: @defaultB
54+
}
55+
56+
extension ConformingGenericStruct : ResilientProtocolWithGeneric {
57+
func defaultB<T>(_: T)
58+
}
59+
60+
sil_witness_table <T> ConformingGenericStruct<T> : ResilientProtocolWithGeneric module protocol_resilience {
61+
method #ResilientProtocolWithGeneric.defaultB!1: @defaultB
62+
}
63+
3864
// CHECK-LABEL: sil hidden @test_devirt_of_default_witness_method : $@convention(thin) (@in_guaranteed ConformingStruct) -> ()
3965
// CHECK: bb0(%0 : $*ConformingStruct):
4066
// CHECK: [[FN:%.*]] = function_ref @defaultA : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocol> (@in_guaranteed τ_0_0) -> ()
@@ -60,3 +86,16 @@ bb0(%0 : $*ConformingGenericStruct<Int>):
6086
%result = apply %fn<ConformingGenericStruct<Int>>(%0) : $@convention(witness_method) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
6187
return %result : $()
6288
}
89+
90+
// CHECK-LABEL: test_devirt_of_inner_generic_default_witness_method
91+
// CHECK: bb0(%0 : $*ConformingGenericStruct<Int>, %1 : $*Int):
92+
// CHECK: [[FN:%[0-9]+]] = function_ref @defaultB : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : P> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
93+
// CHECK: [[RESULT:%[0-9]+]] = apply [[FN]]<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : P> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
94+
// CHECK: return [[RESULT]] : $()
95+
// CHECK: }
96+
sil hidden @test_devirt_of_inner_generic_default_witness_method : $@convention(thin) (@in_guaranteed ConformingGenericStruct<Int>, @in Int) -> () {
97+
bb0(%0 : $*ConformingGenericStruct<Int>, %1 : $*Int):
98+
%fn = witness_method $ConformingGenericStruct<Int>, #ResilientProtocolWithGeneric.defaultB!1 : $@convention(witness_method) <T where T : ResilientProtocolWithGeneric><U where U : Q> (@in U, @in_guaranteed T) -> ()
99+
%result = apply %fn<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method) <T where T : ResilientProtocolWithGeneric><U where U : Q> (@in U, @in_guaranteed T) -> ()
100+
return %result : $()
101+
}

0 commit comments

Comments
 (0)