Skip to content

Commit 665440f

Browse files
authored
[CS] A couple of fixes for static and dynamic callables (#31107)
[CS] A couple of fixes for static and dynamic callables
2 parents 8357c18 + a1cf598 commit 665440f

File tree

3 files changed

+137
-66
lines changed

3 files changed

+137
-66
lines changed

lib/Sema/CSApply.cpp

Lines changed: 63 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -491,9 +491,7 @@ namespace {
491491

492492
// Handle operator requirements found in protocols.
493493
if (auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
494-
bool isCurried = shouldBuildCurryThunk(choice,
495-
/*baseIsInstance=*/false,
496-
/*extraUncurryLevel=*/false);
494+
bool isCurried = shouldBuildCurryThunk(choice, /*baseIsInstance=*/false);
497495

498496
// If we have a concrete conformance, build a call to the witness.
499497
//
@@ -558,8 +556,7 @@ namespace {
558556
cs.cacheExprTypes(base);
559557

560558
return buildMemberRef(base, SourceLoc(), overload, loc, locator,
561-
locator, implicit, /*extraUncurryLevel=*/false,
562-
semantics);
559+
locator, implicit, semantics);
563560
}
564561

565562
if (isa<TypeDecl>(decl) && !isa<ModuleDecl>(decl)) {
@@ -669,6 +666,10 @@ namespace {
669666

670667
/// Calculates the nesting depth of the current application.
671668
unsigned getArgCount(unsigned maxArgCount) {
669+
// FIXME: Walking over the ExprStack to figure out the number of argument
670+
// lists being applied is brittle. We should instead be checking
671+
// hasAppliedSelf to figure out if the self param is applied, and looking
672+
// at the FunctionRefKind to see if the parameter list is applied.
672673
unsigned e = ExprStack.size();
673674
unsigned argCount;
674675

@@ -812,8 +813,7 @@ namespace {
812813
/// converted into a fully-applied member reference with a pair of
813814
/// closures.
814815
bool shouldBuildCurryThunk(OverloadChoice choice,
815-
bool baseIsInstance,
816-
bool extraUncurryLevel) {
816+
bool baseIsInstance) {
817817
ValueDecl *member = choice.getDecl();
818818
auto isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
819819

@@ -848,29 +848,20 @@ namespace {
848848
isa<CallExpr>(prev) &&
849849
isa<TypeExpr>(cast<CallExpr>(prev)->getFn())) {
850850
assert(maxArgCount == 2);
851-
return 1;
851+
return 2;
852852
}
853853

854854
// Similarly, ".foo(...)" really applies two argument lists.
855855
if (auto *unresolvedMemberExpr = dyn_cast<UnresolvedMemberExpr>(prev)) {
856856
if (unresolvedMemberExpr->hasArguments() ||
857857
unresolvedMemberExpr->hasTrailingClosure())
858-
return 1;
859-
return 0;
858+
return 2;
859+
return 1;
860860
}
861861

862862
return getArgCount(maxArgCount);
863863
}();
864864

865-
// Sometimes we build a member reference that has an implicit
866-
// level of function application in the AST. For example,
867-
// @dynamicCallable and callAsFunction are handled this way.
868-
//
869-
// FIXME: It would be nice to simplify this and the argCount
870-
// computation above somehow.
871-
if (extraUncurryLevel)
872-
argCount++;
873-
874865
// If we have fewer argument lists than expected, build a thunk.
875866
if (argCount < maxArgCount)
876867
return true;
@@ -1062,7 +1053,7 @@ namespace {
10621053
SelectedOverload overload, DeclNameLoc memberLoc,
10631054
ConstraintLocatorBuilder locator,
10641055
ConstraintLocatorBuilder memberLocator, bool Implicit,
1065-
bool extraUncurryLevel, AccessSemantics semantics) {
1056+
AccessSemantics semantics) {
10661057
auto choice = overload.choice;
10671058
auto openedType = overload.openedType;
10681059
auto openedFullType = overload.openedFullType;
@@ -1116,8 +1107,7 @@ namespace {
11161107

11171108
bool isUnboundInstanceMember =
11181109
(!baseIsInstance && member->isInstanceMember());
1119-
bool isPartialApplication =
1120-
shouldBuildCurryThunk(choice, baseIsInstance, extraUncurryLevel);
1110+
bool isPartialApplication = shouldBuildCurryThunk(choice, baseIsInstance);
11211111

11221112
auto refTy = simplifyType(openedFullType);
11231113

@@ -1549,12 +1539,11 @@ namespace {
15491539
ConstraintLocatorBuilder locator,
15501540
ConstraintLocatorBuilder calleeLocator);
15511541

1552-
// Resolve `@dynamicCallable` applications.
1553-
Expr *finishApplyDynamicCallable(ApplyExpr *apply,
1554-
SelectedOverload selected,
1555-
FuncDecl *method,
1556-
AnyFunctionType *methodType,
1557-
ConstraintLocatorBuilder applyFunctionLoc);
1542+
/// Build the function and argument for a `@dynamicCallable` application.
1543+
std::pair</*fn*/ Expr *, /*arg*/ Expr *>
1544+
buildDynamicCallable(ApplyExpr *apply, SelectedOverload selected,
1545+
FuncDecl *method, AnyFunctionType *methodType,
1546+
ConstraintLocatorBuilder applyFunctionLoc);
15581547

15591548
private:
15601549
/// Simplify the given type by substituting all occurrences of
@@ -2761,7 +2750,7 @@ namespace {
27612750
return buildMemberRef(
27622751
expr->getBase(), expr->getDotLoc(), selected, expr->getNameLoc(),
27632752
cs.getConstraintLocator(expr), memberLocator, expr->isImplicit(),
2764-
/*extraUncurryLevel=*/false, expr->getAccessSemantics());
2753+
expr->getAccessSemantics());
27652754
}
27662755

27672756
Expr *visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) {
@@ -2804,8 +2793,7 @@ namespace {
28042793
auto *exprLoc = cs.getConstraintLocator(expr);
28052794
auto result = buildMemberRef(
28062795
base, expr->getDotLoc(), selected, expr->getNameLoc(), exprLoc,
2807-
memberLocator, expr->isImplicit(), /*extraUncurryLevel=*/true,
2808-
AccessSemantics::Ordinary);
2796+
memberLocator, expr->isImplicit(), AccessSemantics::Ordinary);
28092797
if (!result)
28102798
return nullptr;
28112799

@@ -2953,8 +2941,7 @@ namespace {
29532941
if (cs.getType(base)->is<AnyMetatypeType>()) {
29542942
return buildMemberRef(
29552943
base, dotLoc, overload, nameLoc, cs.getConstraintLocator(expr),
2956-
ctorLocator, implicit, /*extraUncurryLevel=*/true,
2957-
AccessSemantics::Ordinary);
2944+
ctorLocator, implicit, AccessSemantics::Ordinary);
29582945
}
29592946

29602947
// The subexpression must be either 'self' or 'super'.
@@ -3127,8 +3114,7 @@ namespace {
31273114
case OverloadChoiceKind::DeclViaDynamic:
31283115
return buildMemberRef(base, dotLoc, selected, nameLoc,
31293116
cs.getConstraintLocator(expr), memberLocator,
3130-
implicit, /*extraUncurryLevel=*/false,
3131-
AccessSemantics::Ordinary);
3117+
implicit, AccessSemantics::Ordinary);
31323118

31333119
case OverloadChoiceKind::TupleIndex: {
31343120
Type toType = simplifyType(cs.getType(expr));
@@ -7142,23 +7128,31 @@ static Expr *buildCallAsFunctionMethodRef(
71427128
// Create direct reference to `callAsFunction` method.
71437129
auto *fn = apply->getFn();
71447130
auto *arg = apply->getArg();
7131+
7132+
// HACK: Temporarily push the fn expr onto the expr stack to make sure we
7133+
// don't try to prematurely close an existential when applying the curried
7134+
// member ref. This can be removed once existential opening is refactored not
7135+
// to rely on the shape of the AST prior to rewriting.
7136+
rewriter.ExprStack.push_back(fn);
7137+
SWIFT_DEFER {
7138+
rewriter.ExprStack.pop_back();
7139+
};
7140+
71457141
auto *declRef = rewriter.buildMemberRef(
71467142
fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(arg->getStartLoc()),
7147-
calleeLoc, calleeLoc, /*implicit*/ true,
7148-
/*extraUncurryLevel=*/true, AccessSemantics::Ordinary);
7143+
calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary);
71497144
if (!declRef)
71507145
return nullptr;
71517146
declRef->setImplicit(apply->isImplicit());
71527147
return declRef;
71537148
}
71547149

71557150
// Resolve `@dynamicCallable` applications.
7156-
Expr *
7157-
ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply,
7158-
SelectedOverload selected,
7159-
FuncDecl *method,
7160-
AnyFunctionType *methodType,
7161-
ConstraintLocatorBuilder loc) {
7151+
std::pair<Expr *, Expr *>
7152+
ExprRewriter::buildDynamicCallable(ApplyExpr *apply, SelectedOverload selected,
7153+
FuncDecl *method,
7154+
AnyFunctionType *methodType,
7155+
ConstraintLocatorBuilder loc) {
71627156
auto &ctx = cs.getASTContext();
71637157
auto *fn = apply->getFn();
71647158

@@ -7176,11 +7170,19 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply,
71767170
auto argumentLabel = methodType->getParams()[0].getLabel();
71777171
bool useKwargsMethod = argumentLabel == ctx.Id_withKeywordArguments;
71787172

7173+
// HACK: Temporarily push the fn expr onto the expr stack to make sure we
7174+
// don't try to prematurely close an existential when applying the curried
7175+
// member ref. This can be removed once existential opening is refactored not
7176+
// to rely on the shape of the AST prior to rewriting.
7177+
ExprStack.push_back(fn);
7178+
SWIFT_DEFER {
7179+
ExprStack.pop_back();
7180+
};
7181+
71797182
// Construct expression referencing the `dynamicallyCall` method.
71807183
auto member = buildMemberRef(fn, SourceLoc(), selected,
71817184
DeclNameLoc(method->getNameLoc()), loc, loc,
7182-
/*implicit=*/true, /*extraUncurryLevel=*/true,
7183-
AccessSemantics::Ordinary);
7185+
/*implicit=*/true, AccessSemantics::Ordinary);
71847186

71857187
// Construct argument to the method (either an array or dictionary
71867188
// expression).
@@ -7210,8 +7212,7 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply,
72107212
handleStringLiteralExpr(cast<LiteralExpr>(labelExpr));
72117213

72127214
Expr *valueExpr = coerceToType(arg->getElement(i), valueType, loc);
7213-
if (!valueExpr)
7214-
return nullptr;
7215+
assert(valueExpr && "Failed to coerce?");
72157216
Expr *pair = TupleExpr::createImplicit(ctx, {labelExpr, valueExpr}, {});
72167217
auto eltTypes = { TupleTypeElt(keyType), TupleTypeElt(valueType) };
72177218
cs.setType(pair, TupleType::get(eltTypes, ctx));
@@ -7224,22 +7225,21 @@ ExprRewriter::finishApplyDynamicCallable(ApplyExpr *apply,
72247225
}
72257226
argument->setImplicit();
72267227

7227-
// Construct call to the `dynamicallyCall` method.
7228-
auto result = CallExpr::createImplicit(ctx, member, argument,
7229-
{ argumentLabel });
7230-
cs.setType(result->getArg(), AnyFunctionType::composeInput(ctx, params,
7231-
false));
7232-
cs.setType(result, methodType->getResult());
7233-
cs.cacheExprTypes(result);
7234-
return result;
7228+
// Build the argument list expr.
7229+
argument = TupleExpr::createImplicit(ctx, {argument}, {argumentLabel});
7230+
cs.setType(argument,
7231+
TupleType::get({TupleTypeElt(argumentType, argumentLabel)}, ctx));
7232+
7233+
return std::make_pair(member, argument);
72357234
}
72367235

72377236
Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
72387237
ConstraintLocatorBuilder locator,
72397238
ConstraintLocatorBuilder calleeLocator) {
72407239
auto &ctx = cs.getASTContext();
7241-
7242-
auto fn = apply->getFn();
7240+
7241+
auto *arg = apply->getArg();
7242+
auto *fn = apply->getFn();
72437243

72447244
bool hasTrailingClosure =
72457245
isa<CallExpr>(apply) && cast<CallExpr>(apply)->hasTrailingClosure();
@@ -7408,7 +7408,7 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
74087408
if (method && methodType) {
74097409
// Handle a call to a @dynamicCallable method.
74107410
if (isValidDynamicCallableMethod(method, methodType))
7411-
return finishApplyDynamicCallable(
7411+
std::tie(fn, arg) = buildDynamicCallable(
74127412
apply, *selected, method, methodType, applyFunctionLoc);
74137413
}
74147414
}
@@ -7465,13 +7465,11 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
74657465
// the function.
74667466
SmallVector<Identifier, 2> argLabelsScratch;
74677467
if (auto fnType = cs.getType(fn)->getAs<FunctionType>()) {
7468-
auto origArg = apply->getArg();
7469-
Expr *arg = coerceCallArguments(origArg, fnType, callee,
7470-
apply,
7471-
apply->getArgumentLabels(argLabelsScratch),
7472-
hasTrailingClosure,
7473-
locator.withPathElement(
7474-
ConstraintLocator::ApplyArgument));
7468+
arg = coerceCallArguments(arg, fnType, callee, apply,
7469+
apply->getArgumentLabels(argLabelsScratch),
7470+
hasTrailingClosure,
7471+
locator.withPathElement(
7472+
ConstraintLocator::ApplyArgument));
74757473
if (!arg) {
74767474
return nullptr;
74777475
}
@@ -7542,7 +7540,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
75427540
Expr *declRef = buildMemberRef(fn, /*dotLoc=*/SourceLoc(), *selected,
75437541
DeclNameLoc(fn->getEndLoc()), locator,
75447542
ctorLocator, /*implicit=*/true,
7545-
/*extraUncurryLevel=*/true,
75467543
AccessSemantics::Ordinary);
75477544
if (!declRef)
75487545
return nullptr;

test/SILGen/call_as_function.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
struct S {
4+
func callAsFunction(_ x: Int) -> Int! { nil }
5+
}
6+
7+
protocol P1 {
8+
func callAsFunction()
9+
}
10+
11+
protocol P2 {
12+
func callAsFunction() -> Self
13+
}
14+
15+
class C {
16+
func callAsFunction(_ x: String) -> Self { return self }
17+
}
18+
19+
// CHECK-LABEL: sil hidden [ossa] @$s16call_as_function05test_a1_b1_C0yyAA1SV_AA2P1_pAA2P2_pxtAA1CCRbzlF : $@convention(thin) <T where T : C> (S, @in_guaranteed P1, @in_guaranteed P2, @guaranteed T) -> ()
20+
func test_call_as_function<T : C>(_ s: S, _ p1: P1, _ p2: P2, _ t: T) {
21+
// CHECK: function_ref @$s16call_as_function1SV0A10AsFunctionySiSgSiF : $@convention(method) (Int, S) -> Optional<Int>
22+
// CHECK: switch_enum %{{.+}} : $Optional<Int>
23+
let _: Int = s(0)
24+
25+
// SR-12590: SILGen crash on existential callAsFunction.
26+
// CHECK: witness_method $@opened({{.+}}) P1, #P1.callAsFunction : <Self where Self : P1> (Self) -> () -> ()
27+
p1()
28+
29+
// CHECK: witness_method $@opened({{.+}}) P2, #P2.callAsFunction : <Self where Self : P2> (Self) -> () -> Self
30+
_ = p2()
31+
32+
// CHECK: class_method %{{.+}} : $C, #C.callAsFunction : (C) -> (String) -> @dynamic_self C, $@convention(method) (@guaranteed String, @guaranteed C) -> @owned C
33+
// CHECK: unchecked_ref_cast %{{.+}} : $C to $T
34+
_ = t("")
35+
}
36+

test/SILGen/dynamic_callable_attribute.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,41 @@ public struct Callable2 {
3838
public func keywordCoerceBug(a: Callable2, s: Int) {
3939
a(s)
4040
}
41+
42+
@dynamicCallable
43+
struct S {
44+
func dynamicallyCall(withArguments x: [Int]) -> Int! { nil }
45+
}
46+
47+
@dynamicCallable
48+
protocol P1 {
49+
func dynamicallyCall(withKeywordArguments: [String: Any])
50+
}
51+
52+
@dynamicCallable
53+
protocol P2 {
54+
func dynamicallyCall(withArguments x: [Int]) -> Self
55+
}
56+
57+
@dynamicCallable
58+
class C {
59+
func dynamicallyCall(withKeywordArguments x: [String: String]) -> Self { return self }
60+
}
61+
62+
// CHECK-LABEL: sil hidden [ossa] @$s26dynamic_callable_attribute05test_A10_callablesyyAA1SV_AA2P1_pAA2P2_pxtAA1CCRbzlF : $@convention(thin) <T where T : C> (S, @in_guaranteed P1, @in_guaranteed P2, @guaranteed T) -> ()
63+
func test_dynamic_callables<T : C>(_ s: S, _ p1: P1, _ p2: P2, _ t: T) {
64+
// SR-12615: Compiler crash on @dynamicCallable IUO.
65+
// CHECK: function_ref @$s26dynamic_callable_attribute1SV15dynamicallyCall13withArgumentsSiSgSaySiG_tF : $@convention(method) (@guaranteed Array<Int>, S) -> Optional<Int>
66+
// CHECK: switch_enum %{{.+}} : $Optional<Int>
67+
let _: Int = s(0)
68+
69+
// CHECK: witness_method $@opened({{.+}}) P1, #P1.dynamicallyCall : <Self where Self : P1> (Self) -> ([String : Any]) -> ()
70+
p1(x: 5)
71+
72+
// CHECK: witness_method $@opened({{.+}}) P2, #P2.dynamicallyCall : <Self where Self : P2> (Self) -> ([Int]) -> Self
73+
_ = p2()
74+
75+
// CHECK: class_method %{{.+}} : $C, #C.dynamicallyCall : (C) -> ([String : String]) -> @dynamic_self C, $@convention(method) (@guaranteed Dictionary<String, String>, @guaranteed C) -> @owned C
76+
// CHECK: unchecked_ref_cast %{{.+}} : $C to $T
77+
_ = t("")
78+
}

0 commit comments

Comments
 (0)