Skip to content

Commit 0eb649d

Browse files
authored
Merge pull request #59435 from slavapestov/devirtualize-class-witness-method-fix-5.7
SILOptimizer: Fix invariant violation in getWitnessMethodSubstitutions() with class witness methods [5.7]
2 parents e12b1b5 + 14ac4be commit 0eb649d

File tree

2 files changed

+112
-33
lines changed

2 files changed

+112
-33
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,8 @@ getWitnessMethodSubstitutions(
900900
bool isSelfAbstract,
901901
ClassDecl *classWitness) {
902902

903+
auto &ctx = mod->getASTContext();
904+
903905
if (witnessThunkSig.isNull())
904906
return SubstitutionMap();
905907

@@ -909,46 +911,100 @@ getWitnessMethodSubstitutions(
909911
assert(!conformanceRef.isAbstract());
910912
auto conformance = conformanceRef.getConcrete();
911913

914+
auto selfType = conformance->getProtocol()->getSelfInterfaceType();
915+
912916
// If `Self` maps to a bound generic type, this gives us the
913917
// substitutions for the concrete type's generic parameters.
914918
auto baseSubMap = conformance->getSubstitutions(mod);
915919

916920
unsigned baseDepth = 0;
917921
auto *rootConformance = conformance->getRootConformance();
918-
if (auto witnessSig = rootConformance->getGenericSignature())
919-
baseDepth = witnessSig.getGenericParams().back()->getDepth() + 1;
920-
921-
// If the witness has a class-constrained 'Self' generic parameter,
922-
// we have to build a new substitution map that shifts all generic
923-
// parameters down by one.
924-
if (classWitness != nullptr) {
925-
auto *proto = conformance->getProtocol();
926-
auto selfType = proto->getSelfInterfaceType();
927-
928-
auto selfSubMap = SubstitutionMap::getProtocolSubstitutions(
929-
proto, selfType.subst(origSubMap), conformanceRef);
930-
if (baseSubMap.empty()) {
931-
assert(baseDepth == 0);
932-
baseSubMap = selfSubMap;
933-
} else {
934-
baseSubMap = SubstitutionMap::combineSubstitutionMaps(
935-
selfSubMap,
936-
baseSubMap,
937-
CombineSubstitutionMaps::AtDepth,
938-
/*firstDepth=*/1,
939-
/*secondDepth=*/0,
940-
witnessThunkSig);
941-
}
942-
baseDepth += 1;
943-
}
922+
if (auto conformingTypeSig = rootConformance->getGenericSignature())
923+
baseDepth = conformingTypeSig.getGenericParams().back()->getDepth() + 1;
944924

945-
return SubstitutionMap::combineSubstitutionMaps(
946-
baseSubMap,
947-
origSubMap,
948-
CombineSubstitutionMaps::AtDepth,
949-
/*firstDepth=*/baseDepth,
950-
/*secondDepth=*/1,
951-
witnessThunkSig);
925+
// witnessThunkSig begins with the optional class 'Self', followed by the
926+
// generic parameters of the concrete conforming type, followed by the
927+
// generic parameters of the protocol requirement, if any.
928+
//
929+
// - The 'Self' parameter is replaced with the conforming type.
930+
// - The conforming type's generic parameters are replaced by the
931+
// conformance substitutions.
932+
// - The protocol requirement's generic parameters are replaced from the
933+
// substitution map at the call site.
934+
return SubstitutionMap::get(
935+
witnessThunkSig,
936+
[&](SubstitutableType *type) {
937+
auto *paramType = type->castTo<GenericTypeParamType>();
938+
unsigned depth = paramType->getDepth();
939+
940+
if (classWitness != nullptr) {
941+
if (depth == 0) {
942+
assert(paramType->getIndex() == 0);
943+
return selfType.subst(origSubMap);
944+
}
945+
946+
--depth;
947+
}
948+
949+
if (depth < baseDepth) {
950+
paramType = GenericTypeParamType::get(
951+
paramType->isTypeSequence(),
952+
depth, paramType->getIndex(), ctx);
953+
954+
return Type(paramType).subst(baseSubMap);
955+
}
956+
957+
depth = depth - baseDepth + 1;
958+
959+
paramType = GenericTypeParamType::get(
960+
paramType->isTypeSequence(),
961+
depth, paramType->getIndex(), ctx);
962+
return Type(paramType).subst(origSubMap);
963+
},
964+
[&](CanType type, Type substType, ProtocolDecl *proto) {
965+
auto *paramType = type->getRootGenericParam();
966+
unsigned depth = paramType->getDepth();
967+
968+
if (classWitness != nullptr) {
969+
if (depth == 0) {
970+
assert(type->isEqual(paramType));
971+
assert(paramType->getIndex() == 0);
972+
return conformanceRef;
973+
}
974+
975+
--depth;
976+
}
977+
978+
if (depth < baseDepth) {
979+
type = CanType(type.transform([&](Type t) -> Type {
980+
if (t->isEqual(paramType)) {
981+
return GenericTypeParamType::get(
982+
paramType->isTypeSequence(),
983+
depth, paramType->getIndex(), ctx);
984+
}
985+
986+
assert(!t->is<GenericTypeParamType>());
987+
return t;
988+
}));
989+
990+
return baseSubMap.lookupConformance(type, proto);
991+
}
992+
993+
depth = depth - baseDepth + 1;
994+
995+
type = CanType(type.transform([&](Type t) -> Type {
996+
if (t->isEqual(paramType)) {
997+
return GenericTypeParamType::get(
998+
paramType->isTypeSequence(),
999+
depth, paramType->getIndex(), ctx);
1000+
}
1001+
1002+
assert(!t->is<GenericTypeParamType>());
1003+
return t;
1004+
}));
1005+
1006+
return origSubMap.lookupConformance(type, proto);
1007+
});
9521008
}
9531009

9541010
SubstitutionMap
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend -emit-sil -O %s | %FileCheck %s
2+
3+
public protocol P {}
4+
5+
public protocol Q {
6+
func foo<T: P>(t: T)
7+
func bar<T: P>(t: T)
8+
}
9+
extension Q {
10+
@inline(__always)
11+
public func foo<T: P>(t: T) {
12+
bar(t: t)
13+
}
14+
15+
@_optimize(none)
16+
public func bar<T: P>(t: T) {}
17+
}
18+
19+
public class C<T>: Q {}
20+
21+
// CHECK-LABEL: sil shared [transparent] [thunk] @$s35devirt_class_witness_method_generic1CCyqd__GAA1QA2aEP3bar1tyqd___tAA1PRd__lFTW : $@convention(witness_method: Q) <τ_0_0><τ_1_0 where τ_0_0 : C<τ_1_0>><τ_2_0 where τ_2_0 : P> (@in_guaranteed τ_2_0, @in_guaranteed τ_0_0) -> () {
22+
// CHECK: [[FN:%.*]] = function_ref @$s35devirt_class_witness_method_generic1QPAAE3bar1tyqd___tAA1PRd__lF : $@convention(method) <τ_0_0 where τ_0_0 : Q><τ_1_0 where τ_1_0 : P> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
23+
// CHECK: apply [[FN]]<τ_0_0, τ_2_0>(%0, %1) : $@convention(method) <τ_0_0 where τ_0_0 : Q><τ_1_0 where τ_1_0 : P> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()

0 commit comments

Comments
 (0)