Skip to content

Commit 9d00c7b

Browse files
committed
[CSApply] Don't attempt operator devirtualization
This appears to only support synthesized conformances because operators in such cases use different names - `__derived_*`. These days devirtualization like this is performed as part of mandatory inlining. And this approach doesn't stack well with features like `MemberImportVisibility` because there is change to check whether witness is available or not.
1 parent 5373530 commit 9d00c7b

File tree

6 files changed

+46
-147
lines changed

6 files changed

+46
-147
lines changed

lib/Sema/CSApply.cpp

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -588,66 +588,6 @@ namespace {
588588
}
589589
}
590590

591-
// Returns None if the AST does not contain enough information to recover
592-
// substitutions; this is different from an Optional(SubstitutionMap()),
593-
// indicating a valid call to a non-generic operator.
594-
std::optional<SubstitutionMap> getOperatorSubstitutions(ValueDecl *witness,
595-
Type refType) {
596-
// We have to recover substitutions in this hacky way because
597-
// the AST does not retain enough information to devirtualize
598-
// calls like this.
599-
auto witnessType = witness->getInterfaceType();
600-
601-
// Compute the substitutions.
602-
auto *gft = witnessType->getAs<GenericFunctionType>();
603-
if (gft == nullptr) {
604-
if (refType->isEqual(witnessType))
605-
return SubstitutionMap();
606-
return std::nullopt;
607-
}
608-
609-
auto sig = gft->getGenericSignature();
610-
auto *env = sig.getGenericEnvironment();
611-
612-
witnessType = FunctionType::get(gft->getParams(),
613-
gft->getResult(),
614-
gft->getExtInfo());
615-
witnessType = env->mapTypeIntoContext(witnessType);
616-
617-
TypeSubstitutionMap subs;
618-
auto substType = witnessType->substituteBindingsTo(
619-
refType,
620-
[&](ArchetypeType *origType, CanType substType) -> CanType {
621-
if (auto gpType = dyn_cast<GenericTypeParamType>(
622-
origType->getInterfaceType()->getCanonicalType()))
623-
subs[gpType] = substType;
624-
625-
return substType;
626-
});
627-
628-
// If substitution failed, it means that the protocol requirement type
629-
// and the witness type did not match up. The only time that this
630-
// should happen is when the witness is defined in a base class and
631-
// the actual call uses a derived class. For example,
632-
//
633-
// protocol P { func +(lhs: Self, rhs: Self) }
634-
// class Base : P { func +(lhs: Base, rhs: Base) {} }
635-
// class Derived : Base {}
636-
//
637-
// If we enter this code path with two operands of type Derived,
638-
// we know we're calling the protocol requirement P.+, with a
639-
// substituted type of (Derived, Derived) -> (). But the type of
640-
// the witness is (Base, Base) -> (). Just bail out and make a
641-
// witness method call in this rare case; SIL mandatory optimizations
642-
// will likely devirtualize it anyway.
643-
if (!substType)
644-
return std::nullopt;
645-
646-
return SubstitutionMap::get(sig,
647-
QueryTypeSubstitutionMap{subs},
648-
LookUpConformanceInModule());
649-
}
650-
651591
/// Determine whether the given reference is to a method on
652592
/// a remote distributed actor in the given context.
653593
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
@@ -674,65 +614,6 @@ namespace {
674614

675615
auto baseTy = getBaseType(adjustedFullType->castTo<FunctionType>());
676616

677-
// Handle operator requirements found in protocols.
678-
if (auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
679-
bool isCurried = shouldBuildCurryThunk(choice, /*baseIsInstance=*/false);
680-
681-
// If we have a concrete conformance, build a call to the witness.
682-
//
683-
// FIXME: This is awful. We should be able to handle this as a call to
684-
// the protocol requirement with Self == the concrete type, and SILGen
685-
// (or later) can devirtualize as appropriate.
686-
auto conformance = checkConformance(baseTy, proto);
687-
if (conformance.isConcrete()) {
688-
if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) {
689-
bool isMemberOperator = witness->getDeclContext()->isTypeContext();
690-
691-
if (!isMemberOperator || !isCurried) {
692-
// The fullType was computed by substituting the protocol
693-
// requirement so it always has a (Self) -> ... curried
694-
// application. Strip it off if the witness was a top-level
695-
// function.
696-
Type refType;
697-
if (isMemberOperator)
698-
refType = adjustedFullType;
699-
else
700-
refType = adjustedFullType->castTo<AnyFunctionType>()->getResult();
701-
702-
// Build the AST for the call to the witness.
703-
auto subMap = getOperatorSubstitutions(witness, refType);
704-
if (subMap) {
705-
ConcreteDeclRef witnessRef(witness, *subMap);
706-
auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc,
707-
/*Implicit=*/false);
708-
declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
709-
cs.setType(declRefExpr, refType);
710-
711-
Expr *refExpr;
712-
if (isMemberOperator) {
713-
// If the operator is a type member, add the implicit
714-
// (Self) -> ... call.
715-
Expr *base =
716-
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy,
717-
ctx);
718-
cs.setType(base, MetatypeType::get(baseTy));
719-
720-
refExpr =
721-
DotSyntaxCallExpr::create(ctx, declRefExpr, SourceLoc(),
722-
Argument::unlabeled(base));
723-
auto refType = adjustedFullType->castTo<FunctionType>()->getResult();
724-
cs.setType(refExpr, refType);
725-
} else {
726-
refExpr = declRefExpr;
727-
}
728-
729-
return forceUnwrapIfExpected(refExpr, locator);
730-
}
731-
}
732-
}
733-
}
734-
}
735-
736617
// Build a reference to the member.
737618
Expr *base =
738619
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx);

test/Constraints/anyhashable_and_operator_filtering.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ func test(arr: [any P]) {
2121
}
2222
}
2323

24+
// Make sure that we don't pick a concrete `(AnyHashable, AnyHashable) -> Bool` overload.
25+
2426
// CHECK: sil private [ossa] @$s34anyhashable_and_operator_filtering4test3arrySayAA1P_pG_tFyAaD_pXEfU_
2527
// CHECK: [[LHS_ARG:%.*]] = alloc_stack $E
2628
// CHECK: [[RHS_ARG:%.*]] = alloc_stack $E
27-
// CHECK: function_ref == infix<A>(_:_:)
28-
// CHECK-NEXT: [[GENERIC_OP:%.*]] = function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> Bool
29-
// CHECK-NEXT: apply [[GENERIC_OP]]<E>([[LHS_ARG]], [[RHS_ARG]])
29+
// CHECK: [[GENERIC_OP:%.*]] = witness_method $E, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
30+
// CHECK-NEXT: apply [[GENERIC_OP]]<E>([[LHS_ARG]], [[RHS_ARG]], {{.*}})

test/Constraints/rdar158063151.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ struct Value: Equatable, ExpressibleByNilLiteral {
88
}
99

1010
// CHECK-LABEL: sil hidden [ossa] @$s13rdar1580631514test1vyAA5ValueV_tF : $@convention(thin) (Value) -> ()
11-
// function_ref static Value.__derived_struct_equals(_:_:)
12-
// CHECK: [[EQUALS_REF:%.*]] = function_ref @$s13rdar1580631515ValueV23__derived_struct_equalsySbAC_ACtFZ
13-
// CHECK-NEXT: apply [[EQUALS_REF]](%0, {{.*}})
11+
// CHECK: [[EQUALS_REF:%.*]] = witness_method $Value, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
12+
// CHECK-NEXT: apply [[EQUALS_REF]]<Value>({{.*}})
1413
func test(v: Value) {
1514
_ = v == nil
1615
}

test/Index/index_refs_to_synthesized_decls.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ func testFoo() {
1212
// CHECK: [[@LINE+1]]:7 | instance-method/Swift | hash(into:) | s:14swift_ide_test9CustomFooV4hash4intoys6HasherVz_tF | {{.*}}Ref
1313
f.hash(into: &hasher)
1414
hasher.finalize()
15-
// CHECK: [[@LINE+1]]:11 | static-method/Swift | __derived_struct_equals(_:_:) | s:14swift_ide_test9CustomFooV23__derived_struct_equalsySbAC_ACtFZ | {{.*}}Ref
15+
// CHECK: [[@LINE+1]]:11 | static-method/infix-operator/Swift | ==(_:_:) | s:SQ2eeoiySbx_xtFZ | {{.*}}Ref
1616
_ = f == CustomFoo(a: 0, b: "b")
1717
}

test/SILGen/enum_equatable_witness.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ public enum Alphabet : String {
4848

4949
// CHECK-LABEL: sil [ossa] @$s4main14check_alphabetySiAA8AlphabetOF : $@convention(thin) (Alphabet) -> Int {
5050
public func check_alphabet(_ state : Alphabet) -> Int {
51-
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
52-
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
51+
// FRAGILE: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
52+
// RESILIENT: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
5353
return state == .E ? 1 : 0
5454
}
5555

5656
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA8AlphabetO_ADtF : $@convention(thin) (Alphabet, Alphabet) -> Bool {
5757
public func compareIt(_ state : Alphabet, _ rhs: Alphabet) -> Bool {
58-
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
59-
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
58+
// FRAGILE: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
59+
// RESILIENT: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
6060
return state == rhs
6161
}
6262

@@ -67,14 +67,14 @@ public enum AlphabetInt : Int {
6767

6868
// CHECK-LABEL: sil [ossa] @$s4main18check_alphabet_intySiAA11AlphabetIntOF : $@convention(thin) (AlphabetInt) -> Int {
6969
public func check_alphabet_int(_ state : AlphabetInt) -> Int {
70-
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
71-
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
70+
// FRAGILE: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
71+
// RESILIENT: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
7272
return state == .E ? 1 : 0
7373
}
7474

7575
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA11AlphabetIntO_ADtF : $@convention(thin) (AlphabetInt, AlphabetInt) -> Bool {
7676
public func compareIt(_ state : AlphabetInt, _ rhs: AlphabetInt) -> Bool {
77-
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
78-
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
77+
// FRAGILE: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
78+
// RESILIENT: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
7979
return state == rhs
8080
}

test/SILGen/protocol_operators.swift

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s --check-prefix=SILGEN
2+
// RUN: %target-swift-frontend -emit-sil %s | %FileCheck %s --check-prefix=OPTIMIZED
3+
4+
// Operators are no longer devirtualized at AST level, it's done during SIL optimization.
25

36
infix operator +++
47

@@ -11,9 +14,13 @@ struct Branch : Twig {
1114
static func doIt(_: Branch, _: Branch) {}
1215
}
1316

14-
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
15-
// CHECK: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ : $@convention(method) (Branch, Branch, @thin Branch.Type) -> ()
16-
// CHECK: return
17+
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
18+
// SILGEN: witness_method $Branch, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> ()
19+
// SILGEN: return
20+
21+
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
22+
// OPTIMIZED: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ
23+
// OPTIMIZED: return
1724
func useBranch(_ b: Branch) {
1825
b +++ b
1926
}
@@ -28,11 +35,17 @@ class Stuck : Stick, ExpressibleByIntegerLiteral {
2835
required init(integerLiteral: Int) {}
2936
}
3037

31-
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
32-
// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
33-
// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
34-
// CHECK: witness_method $Stuck, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> ()
35-
// CHECK: return
38+
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
39+
// SILGEN: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
40+
// SILGEN: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
41+
// SILGEN: witness_method $Stuck, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> ()
42+
// SILGEN: return
43+
44+
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
45+
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
46+
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
47+
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
48+
// OPTIMIZED: return
3649
func useStick(_ a: Stuck, _ b: Stick) {
3750
_ = a +++ b
3851
_ = b +++ b
@@ -49,10 +62,15 @@ class Rope : Twine<Int>, ExpressibleByIntegerLiteral {
4962
required init(integerLiteral: Int) {}
5063
}
5164

52-
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
53-
// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
54-
// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
55-
// CHECK: witness_method $Rope, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> ()
65+
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
66+
// SILGEN: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
67+
// SILGEN: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
68+
// SILGEN: witness_method $Rope, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> () : $@convention(witness_method: Twig) <τ_0_0 where τ_0_0 : Twig> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> ()
69+
70+
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
71+
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
72+
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
73+
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
5674
func useRope(_ r: Rope, _ s: Rope) {
5775
_ = r +++ s
5876
_ = s +++ s

0 commit comments

Comments
 (0)