mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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.
This commit is contained in:
@@ -588,66 +588,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns None if the AST does not contain enough information to recover
|
||||
// substitutions; this is different from an Optional(SubstitutionMap()),
|
||||
// indicating a valid call to a non-generic operator.
|
||||
std::optional<SubstitutionMap> getOperatorSubstitutions(ValueDecl *witness,
|
||||
Type refType) {
|
||||
// We have to recover substitutions in this hacky way because
|
||||
// the AST does not retain enough information to devirtualize
|
||||
// calls like this.
|
||||
auto witnessType = witness->getInterfaceType();
|
||||
|
||||
// Compute the substitutions.
|
||||
auto *gft = witnessType->getAs<GenericFunctionType>();
|
||||
if (gft == nullptr) {
|
||||
if (refType->isEqual(witnessType))
|
||||
return SubstitutionMap();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sig = gft->getGenericSignature();
|
||||
auto *env = sig.getGenericEnvironment();
|
||||
|
||||
witnessType = FunctionType::get(gft->getParams(),
|
||||
gft->getResult(),
|
||||
gft->getExtInfo());
|
||||
witnessType = env->mapTypeIntoContext(witnessType);
|
||||
|
||||
TypeSubstitutionMap subs;
|
||||
auto substType = witnessType->substituteBindingsTo(
|
||||
refType,
|
||||
[&](ArchetypeType *origType, CanType substType) -> CanType {
|
||||
if (auto gpType = dyn_cast<GenericTypeParamType>(
|
||||
origType->getInterfaceType()->getCanonicalType()))
|
||||
subs[gpType] = substType;
|
||||
|
||||
return substType;
|
||||
});
|
||||
|
||||
// If substitution failed, it means that the protocol requirement type
|
||||
// and the witness type did not match up. The only time that this
|
||||
// should happen is when the witness is defined in a base class and
|
||||
// the actual call uses a derived class. For example,
|
||||
//
|
||||
// protocol P { func +(lhs: Self, rhs: Self) }
|
||||
// class Base : P { func +(lhs: Base, rhs: Base) {} }
|
||||
// class Derived : Base {}
|
||||
//
|
||||
// If we enter this code path with two operands of type Derived,
|
||||
// we know we're calling the protocol requirement P.+, with a
|
||||
// substituted type of (Derived, Derived) -> (). But the type of
|
||||
// the witness is (Base, Base) -> (). Just bail out and make a
|
||||
// witness method call in this rare case; SIL mandatory optimizations
|
||||
// will likely devirtualize it anyway.
|
||||
if (!substType)
|
||||
return std::nullopt;
|
||||
|
||||
return SubstitutionMap::get(sig,
|
||||
QueryTypeSubstitutionMap{subs},
|
||||
LookUpConformanceInModule());
|
||||
}
|
||||
|
||||
/// Determine whether the given reference is to a method on
|
||||
/// a remote distributed actor in the given context.
|
||||
bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);
|
||||
@@ -674,65 +614,6 @@ namespace {
|
||||
|
||||
auto baseTy = getBaseType(adjustedFullType->castTo<FunctionType>());
|
||||
|
||||
// Handle operator requirements found in protocols.
|
||||
if (auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
|
||||
bool isCurried = shouldBuildCurryThunk(choice, /*baseIsInstance=*/false);
|
||||
|
||||
// If we have a concrete conformance, build a call to the witness.
|
||||
//
|
||||
// FIXME: This is awful. We should be able to handle this as a call to
|
||||
// the protocol requirement with Self == the concrete type, and SILGen
|
||||
// (or later) can devirtualize as appropriate.
|
||||
auto conformance = checkConformance(baseTy, proto);
|
||||
if (conformance.isConcrete()) {
|
||||
if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) {
|
||||
bool isMemberOperator = witness->getDeclContext()->isTypeContext();
|
||||
|
||||
if (!isMemberOperator || !isCurried) {
|
||||
// The fullType was computed by substituting the protocol
|
||||
// requirement so it always has a (Self) -> ... curried
|
||||
// application. Strip it off if the witness was a top-level
|
||||
// function.
|
||||
Type refType;
|
||||
if (isMemberOperator)
|
||||
refType = adjustedFullType;
|
||||
else
|
||||
refType = adjustedFullType->castTo<AnyFunctionType>()->getResult();
|
||||
|
||||
// Build the AST for the call to the witness.
|
||||
auto subMap = getOperatorSubstitutions(witness, refType);
|
||||
if (subMap) {
|
||||
ConcreteDeclRef witnessRef(witness, *subMap);
|
||||
auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc,
|
||||
/*Implicit=*/false);
|
||||
declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
|
||||
cs.setType(declRefExpr, refType);
|
||||
|
||||
Expr *refExpr;
|
||||
if (isMemberOperator) {
|
||||
// If the operator is a type member, add the implicit
|
||||
// (Self) -> ... call.
|
||||
Expr *base =
|
||||
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy,
|
||||
ctx);
|
||||
cs.setType(base, MetatypeType::get(baseTy));
|
||||
|
||||
refExpr =
|
||||
DotSyntaxCallExpr::create(ctx, declRefExpr, SourceLoc(),
|
||||
Argument::unlabeled(base));
|
||||
auto refType = adjustedFullType->castTo<FunctionType>()->getResult();
|
||||
cs.setType(refExpr, refType);
|
||||
} else {
|
||||
refExpr = declRefExpr;
|
||||
}
|
||||
|
||||
return forceUnwrapIfExpected(refExpr, locator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a reference to the member.
|
||||
Expr *base =
|
||||
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx);
|
||||
|
||||
@@ -21,9 +21,10 @@ func test(arr: [any P]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that we don't pick a concrete `(AnyHashable, AnyHashable) -> Bool` overload.
|
||||
|
||||
// CHECK: sil private [ossa] @$s34anyhashable_and_operator_filtering4test3arrySayAA1P_pG_tFyAaD_pXEfU_
|
||||
// CHECK: [[LHS_ARG:%.*]] = alloc_stack $E
|
||||
// CHECK: [[RHS_ARG:%.*]] = alloc_stack $E
|
||||
// CHECK: function_ref == infix<A>(_:_:)
|
||||
// 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
|
||||
// CHECK-NEXT: apply [[GENERIC_OP]]<E>([[LHS_ARG]], [[RHS_ARG]])
|
||||
// CHECK: [[GENERIC_OP:%.*]] = witness_method $E, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// CHECK-NEXT: apply [[GENERIC_OP]]<E>([[LHS_ARG]], [[RHS_ARG]], {{.*}})
|
||||
|
||||
@@ -8,9 +8,8 @@ struct Value: Equatable, ExpressibleByNilLiteral {
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s13rdar1580631514test1vyAA5ValueV_tF : $@convention(thin) (Value) -> ()
|
||||
// function_ref static Value.__derived_struct_equals(_:_:)
|
||||
// CHECK: [[EQUALS_REF:%.*]] = function_ref @$s13rdar1580631515ValueV23__derived_struct_equalsySbAC_ACtFZ
|
||||
// CHECK-NEXT: apply [[EQUALS_REF]](%0, {{.*}})
|
||||
// CHECK: [[EQUALS_REF:%.*]] = witness_method $Value, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// CHECK-NEXT: apply [[EQUALS_REF]]<Value>({{.*}})
|
||||
func test(v: Value) {
|
||||
_ = v == nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ func testFoo() {
|
||||
// CHECK: [[@LINE+1]]:7 | instance-method/Swift | hash(into:) | s:14swift_ide_test9CustomFooV4hash4intoys6HasherVz_tF | {{.*}}Ref
|
||||
f.hash(into: &hasher)
|
||||
hasher.finalize()
|
||||
// CHECK: [[@LINE+1]]:11 | static-method/Swift | __derived_struct_equals(_:_:) | s:14swift_ide_test9CustomFooV23__derived_struct_equalsySbAC_ACtFZ | {{.*}}Ref
|
||||
// CHECK: [[@LINE+1]]:11 | static-method/infix-operator/Swift | ==(_:_:) | s:SQ2eeoiySbx_xtFZ | {{.*}}Ref
|
||||
_ = f == CustomFoo(a: 0, b: "b")
|
||||
}
|
||||
|
||||
@@ -48,15 +48,15 @@ public enum Alphabet : String {
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s4main14check_alphabetySiAA8AlphabetOF : $@convention(thin) (Alphabet) -> Int {
|
||||
public func check_alphabet(_ state : Alphabet) -> Int {
|
||||
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// FRAGILE: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// RESILIENT: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
return state == .E ? 1 : 0
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA8AlphabetO_ADtF : $@convention(thin) (Alphabet, Alphabet) -> Bool {
|
||||
public func compareIt(_ state : Alphabet, _ rhs: Alphabet) -> Bool {
|
||||
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// FRAGILE: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// RESILIENT: witness_method $Alphabet, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
return state == rhs
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ public enum AlphabetInt : Int {
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s4main18check_alphabet_intySiAA11AlphabetIntOF : $@convention(thin) (AlphabetInt) -> Int {
|
||||
public func check_alphabet_int(_ state : AlphabetInt) -> Int {
|
||||
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// FRAGILE: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// RESILIENT: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
return state == .E ? 1 : 0
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @$s4main9compareItySbAA11AlphabetIntO_ADtF : $@convention(thin) (AlphabetInt, AlphabetInt) -> Bool {
|
||||
public func compareIt(_ state : AlphabetInt, _ rhs: AlphabetInt) -> Bool {
|
||||
// FRAGILE: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// RESILIENT: function_ref @$ss2eeoiySbx_xtSYRzSQ8RawValueRpzlF
|
||||
// FRAGILE: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
// RESILIENT: witness_method $AlphabetInt, #Equatable."==" : <Self where Self : Equatable> (Self.Type) -> (Self, Self) -> Bool
|
||||
return state == rhs
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s --check-prefix=SILGEN
|
||||
// RUN: %target-swift-frontend -emit-sil %s | %FileCheck %s --check-prefix=OPTIMIZED
|
||||
|
||||
// Operators are no longer devirtualized at AST level, it's done during SIL optimization.
|
||||
|
||||
infix operator +++
|
||||
|
||||
@@ -11,9 +14,13 @@ struct Branch : Twig {
|
||||
static func doIt(_: Branch, _: Branch) {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
|
||||
// CHECK: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ : $@convention(method) (Branch, Branch, @thin Branch.Type) -> ()
|
||||
// CHECK: return
|
||||
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
|
||||
// SILGEN: witness_method $Branch, #Twig."+++" : <Self where Self : Twig> (Self.Type) -> (Self, Self) -> ()
|
||||
// SILGEN: return
|
||||
|
||||
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators9useBranchyyAA0D0VF : $@convention(thin) (Branch) -> () {
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators6BranchV4doItyyAC_ACtFZ
|
||||
// OPTIMIZED: return
|
||||
func useBranch(_ b: Branch) {
|
||||
b +++ b
|
||||
}
|
||||
@@ -28,11 +35,17 @@ class Stuck : Stick, ExpressibleByIntegerLiteral {
|
||||
required init(integerLiteral: Int) {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
|
||||
// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// CHECK: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// 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) -> ()
|
||||
// CHECK: return
|
||||
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
|
||||
// SILGEN: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// SILGEN: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// 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) -> ()
|
||||
// SILGEN: return
|
||||
|
||||
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators8useStickyyAA5StuckC_AA0D0CtF : $@convention(thin) (@guaranteed Stuck, @guaranteed Stick) -> () {
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5StickC3pppoiyyAC_ACtFZ : $@convention(method) (@guaranteed Stick, @guaranteed Stick, @thick Stick.Type) -> ()
|
||||
// OPTIMIZED: return
|
||||
func useStick(_ a: Stuck, _ b: Stick) {
|
||||
_ = a +++ b
|
||||
_ = b +++ b
|
||||
@@ -49,10 +62,15 @@ class Rope : Twine<Int>, ExpressibleByIntegerLiteral {
|
||||
required init(integerLiteral: Int) {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
|
||||
// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// CHECK: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// 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) -> ()
|
||||
// SILGEN-LABEL: sil hidden [ossa] @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
|
||||
// SILGEN: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// SILGEN: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// 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) -> ()
|
||||
|
||||
// OPTIMIZED-LABEL: sil hidden @$s18protocol_operators7useRopeyyAA0D0C_ADtF : $@convention(thin) (@guaranteed Rope, @guaranteed Rope) -> () {
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
// OPTIMIZED: function_ref @$s18protocol_operators5TwineC3pppoiyyACyxG_AEtFZ : $@convention(method) <τ_0_0> (@guaranteed Twine<τ_0_0>, @guaranteed Twine<τ_0_0>, @thick Twine<τ_0_0>.Type) -> ()
|
||||
func useRope(_ r: Rope, _ s: Rope) {
|
||||
_ = r +++ s
|
||||
_ = s +++ s
|
||||
|
||||
Reference in New Issue
Block a user