Skip to content

Commit f6187e5

Browse files
committed
Sema: Don't devirtualize partially applied operator references
1 parent cdbd2cc commit f6187e5

File tree

2 files changed

+78
-37
lines changed

2 files changed

+78
-37
lines changed

lib/Sema/CSApply.cpp

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,10 @@ namespace {
483483

484484
// Handle operator requirements found in protocols.
485485
if (auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
486+
bool isCurried = shouldBuildCurryThunk(choice,
487+
/*baseIsInstance=*/false,
488+
/*extraUncurryLevel=*/false);
489+
486490
// If we have a concrete conformance, build a call to the witness.
487491
//
488492
// FIXME: This is awful. We should be able to handle this as a call to
@@ -494,49 +498,53 @@ namespace {
494498
ConformanceCheckFlags::InExpression);
495499
if (conformance.isConcrete()) {
496500
if (auto witness = conformance.getConcrete()->getWitnessDecl(decl)) {
497-
// The fullType was computed by substituting the protocol
498-
// requirement so it always has a (Self) -> ... curried
499-
// application. Strip it off if the witness was a top-level
500-
// function.
501-
Type refType;
502-
if (witness->getDeclContext()->isTypeContext())
503-
refType = fullType;
504-
else
505-
refType = fullType->castTo<AnyFunctionType>()->getResult();
506-
507-
// Build the AST for the call to the witness.
508-
auto subMap = getOperatorSubstitutions(witness, refType);
509-
if (subMap) {
510-
ConcreteDeclRef witnessRef(witness, *subMap);
511-
auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc,
512-
/*Implicit=*/false);
513-
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
514-
cs.setType(declRefExpr, refType);
515-
516-
Expr *refExpr;
517-
if (witness->getDeclContext()->isTypeContext()) {
518-
// If the operator is a type member, add the implicit
519-
// (Self) -> ... call.
520-
Expr *base =
521-
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy,
522-
ctx);
523-
cs.setType(base, MetatypeType::get(baseTy));
524-
525-
refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr,
526-
SourceLoc(), base);
527-
auto refType = fullType->castTo<FunctionType>()->getResult();
528-
cs.setType(refExpr, refType);
529-
} else {
530-
refExpr = declRefExpr;
501+
bool isMemberOperator = witness->getDeclContext()->isTypeContext();
502+
503+
if (!isMemberOperator || !isCurried) {
504+
// The fullType was computed by substituting the protocol
505+
// requirement so it always has a (Self) -> ... curried
506+
// application. Strip it off if the witness was a top-level
507+
// function.
508+
Type refType;
509+
if (isMemberOperator)
510+
refType = fullType;
511+
else
512+
refType = fullType->castTo<AnyFunctionType>()->getResult();
513+
514+
// Build the AST for the call to the witness.
515+
auto subMap = getOperatorSubstitutions(witness, refType);
516+
if (subMap) {
517+
ConcreteDeclRef witnessRef(witness, *subMap);
518+
auto declRefExpr = new (ctx) DeclRefExpr(witnessRef, loc,
519+
/*Implicit=*/false);
520+
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
521+
cs.setType(declRefExpr, refType);
522+
523+
Expr *refExpr;
524+
if (isMemberOperator) {
525+
// If the operator is a type member, add the implicit
526+
// (Self) -> ... call.
527+
Expr *base =
528+
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy,
529+
ctx);
530+
cs.setType(base, MetatypeType::get(baseTy));
531+
532+
refExpr = new (ctx) DotSyntaxCallExpr(declRefExpr,
533+
SourceLoc(), base);
534+
auto refType = fullType->castTo<FunctionType>()->getResult();
535+
cs.setType(refExpr, refType);
536+
} else {
537+
refExpr = declRefExpr;
538+
}
539+
540+
return forceUnwrapIfExpected(refExpr, choice, locator);
531541
}
532-
533-
return forceUnwrapIfExpected(refExpr, choice, locator);
534542
}
535543
}
536544
}
537545
}
538546

539-
// Build a reference to the protocol requirement.
547+
// Build a reference to the member.
540548
Expr *base =
541549
TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx);
542550
cs.cacheExprTypes(base);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
// RUN: %target-swift-emit-silgen -module-name partial_apply_protocol -primary-file %s | %FileCheck %s
3+
// RUN: %target-swift-emit-ir -module-name partial_apply_protocol -primary-file %s
4+
5+
protocol Group {
6+
static func +(_: Self, _: Self) -> Self
7+
static prefix func -(_: Self) -> Self
8+
}
9+
10+
extension Int : Group {}
11+
12+
func takesBinaryOp(_: (Int, Int) -> Int) {}
13+
func takesUnaryOp(_: (Int) -> Int) {}
14+
15+
func takesGenericBinaryOp<T : Group>(_: (T, T) -> T) {}
16+
func takesGenericUnaryOp<T : Group>(_: (T) -> T) {}
17+
18+
func passOp() {
19+
takesBinaryOp(+)
20+
takesUnaryOp(-)
21+
}
22+
23+
func passGenericOp<T : Group>(_: T) {
24+
takesGenericBinaryOp(+)
25+
26+
// FIXME: Rejected by the type checker? rdar://problem/60607396
27+
// takesGenericUnaryOp(-)
28+
}
29+
30+
// CHECK-LABEL: sil private [ossa] @$s22partial_apply_protocol6passOpyyFS2i_SitcSimcfu_S2i_Sitcfu0_ : $@convention(thin) (Int, Int, @thin Int.Type) -> Int
31+
// CHECK-LABEL: sil private [ossa] @$s22partial_apply_protocol6passOpyyFS2icSimcfu1_S2icfu2_ : $@convention(thin) (Int, @thin Int.Type) -> Int
32+
33+
// CHECK-LABEL: sil private [ossa] @$s22partial_apply_protocol13passGenericOpyyxAA5GroupRzlFS2i_SitcSimcfu_S2i_Sitcfu0_ : $@convention(thin) (Int, Int, @thin Int.Type) -> Int

0 commit comments

Comments
 (0)