Skip to content

Commit b12923a

Browse files
authored
Merge pull request swiftlang#28147 from slavapestov/devirt-outer-param-requirement
AST: Fix combineSubstitutionMaps() for requirements placed on outer generic parameters
2 parents 110b76e + 88cbcea commit b12923a

File tree

4 files changed

+106
-13
lines changed

4 files changed

+106
-13
lines changed

lib/AST/GenericSignature.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,7 @@ getBestRequirementSource(GenericSignatureBuilder &builder,
814814
}
815815
}
816816

817+
assert(bestSource && "All sources were self-recursive?");
817818
return bestSource;
818819
}
819820

lib/AST/SubstitutionMap.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -555,12 +555,8 @@ SubstitutionMap::getOverrideSubstitutions(const ClassDecl *baseClass,
555555
SubstitutionMap origSubMap;
556556
if (derivedSubs)
557557
origSubMap = *derivedSubs;
558-
else if (derivedSig) {
559-
origSubMap = get(
560-
derivedSig,
561-
[](SubstitutableType *type) -> Type { return type; },
562-
MakeAbstractConformanceForGenericType());
563-
}
558+
else if (derivedSig)
559+
origSubMap = derivedSig->getIdentitySubstitutionMap();
564560

565561
return combineSubstitutionMaps(baseSubMap, origSubMap,
566562
CombineSubstitutionMaps::AtDepth,
@@ -603,17 +599,50 @@ SubstitutionMap::combineSubstitutionMaps(SubstitutionMap firstSubMap,
603599
return get(
604600
genericSig,
605601
[&](SubstitutableType *type) {
606-
auto replacement = replaceGenericParameter(type);
607-
if (replacement)
602+
if (auto replacement = replaceGenericParameter(type))
608603
return Type(replacement).subst(secondSubMap);
609604
return Type(type).subst(firstSubMap);
610605
},
611-
[&](CanType type, Type substType, ProtocolDecl *conformedProtocol) {
612-
auto replacement = type.transform(replaceGenericParameter);
613-
if (replacement)
606+
[&](CanType type, Type substType, ProtocolDecl *proto) {
607+
if (auto replacement = type.transform(replaceGenericParameter))
614608
return secondSubMap.lookupConformance(replacement->getCanonicalType(),
615-
conformedProtocol);
616-
return firstSubMap.lookupConformance(type, conformedProtocol);
609+
proto);
610+
if (auto conformance = firstSubMap.lookupConformance(type, proto))
611+
return conformance;
612+
613+
// We might not have enough information in the substitution maps alone.
614+
//
615+
// Eg,
616+
//
617+
// class Base<T1> {
618+
// func foo<U1>(_: U1) where T1 : P {}
619+
// }
620+
//
621+
// class Derived<T2> : Base<Foo<T2>> {
622+
// override func foo<U2>(_: U2) where T2 : Q {}
623+
// }
624+
//
625+
// Suppose we're devirtualizing a call to Base.foo() on a value whose
626+
// type is known to be Derived<Bar>. We start with substitutions written
627+
// in terms of Base.foo()'s generic signature:
628+
//
629+
// <T1, U1 where T1 : P>
630+
// T1 := Foo<Bar>
631+
// T1 : P := Foo<Bar> : P
632+
//
633+
// We want to build substitutions in terms of Derived.foo()'s
634+
// generic signature:
635+
//
636+
// <T2, U2 where T2 : Q>
637+
// T2 := Bar
638+
// T2 : Q := Bar : Q
639+
//
640+
// The conformance Bar : Q is difficult to recover in the general case.
641+
//
642+
// Some combination of storing substitution maps in BoundGenericTypes
643+
// as well as for method overrides would solve this, but for now, just
644+
// punt to module lookup.
645+
return proto->getParentModule()->lookupConformance(substType, proto);
617646
});
618647
}
619648

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
// RUN: %target-swift-frontend -emit-sil -O %s | %FileCheck %s
3+
4+
public protocol P {}
5+
6+
public class C1<U> {
7+
// CHECK-LABEL: sil @$s25devirt_outer_requirements2C1C3fooyyqd__AA1PRzlF : $@convention(method) <U where U : P><V> (@in_guaranteed V, @guaranteed C1<U>) -> () {
8+
// CHECK: function_ref @$s25devirt_outer_requirements2C1C3baryyqd__AA1PRzlF : $@convention(method) <τ_0_0 where τ_0_0 : P><τ_1_0> (@in_guaranteed τ_1_0, @guaranteed C1<τ_0_0>) -> ()
9+
// CHECK: return
10+
public func foo<V>(_ z: V) where U : P {
11+
bar(z)
12+
}
13+
14+
@_optimize(none)
15+
public func bar<V>(_ z: V) where U : P {}
16+
}
17+
18+
public class Base<T> {
19+
public func foo<V>(_: V) where T : P {}
20+
}
21+
22+
public protocol Q {}
23+
24+
public struct Foo<T> {}
25+
extension Foo : P where T : Q {}
26+
27+
public class Derived<T> : Base<Foo<T>> {
28+
public override func foo<V>(_: V) where T : Q {}
29+
}
30+
31+
@_transparent
32+
public func takesBase<T, V>(_ b: Base<T>, _ v: V) where T : P {
33+
b.foo(v)
34+
}
35+
36+
// CHECK-LABEL: sil @$s25devirt_outer_requirements12takesDerivedyyAA0E0CyxG_q_tAA1QRzr0_lF : $@convention(thin) <T, V where T : Q> (@guaranteed Derived<T>, @in_guaranteed V) -> () {
37+
public func takesDerived<T, V>(_ d: Derived<T>, _ v: V) where T : Q {
38+
// CHECK: function_ref @$s25devirt_outer_requirements12takesDerivedyyAA0E0CyxG_q_tAA1QRzr0_lF : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Q> (@guaranteed Derived<τ_0_0>, @in_guaranteed τ_0_1) -> ()
39+
takesDerived(d, v)
40+
41+
// CHECK: return
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend -emit-ir %s
2+
3+
final class Baz {}
4+
5+
final class Bar {
6+
private let x: Baz
7+
init(x: Baz) {
8+
self.x = x
9+
}
10+
}
11+
12+
final class Foo {
13+
private var bar: Bar?
14+
15+
private func navigate(with baz: Baz?) {
16+
bar = nil
17+
guard let baz = baz else { return }
18+
let bar = Bar(x: baz)
19+
self.bar = bar
20+
}
21+
}

0 commit comments

Comments
 (0)