Skip to content

Commit 88c477b

Browse files
committed
[ConstraintSystem] Accept trailing closure if the last parameter is defaulted
If the last parameter is defaulted, there might be an attempt to use a trailing closure with previous parameter that accepts a function type e.g. ```swift func foo(_: () -> Int, _ x: Int = 0) {} foo { 42 } ``` Resolves: rdar://problem/55102498
1 parent ddb7bbc commit 88c477b

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,19 +430,38 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
430430

431431
// If we have a trailing closure, it maps to the last parameter.
432432
if (hasTrailingClosure && numParams > 0) {
433-
const auto &param = params[numParams - 1];
433+
unsigned lastParamIdx = numParams - 1;
434+
bool lastAcceptsTrailingClosure =
435+
acceptsTrailingClosure(params[lastParamIdx]);
436+
437+
// If the last parameter is defaulted, this might be
438+
// an attempt to use a trailing closure with previous
439+
// parameter that accepts a function type e.g.
434440
//
441+
// func foo(_: () -> Int, _ x: Int = 0) {}
442+
// foo { 42 }
443+
if (!lastAcceptsTrailingClosure && numParams > 1 &&
444+
paramInfo.hasDefaultArgument(lastParamIdx)) {
445+
auto paramType = params[lastParamIdx - 1].getPlainType();
446+
// If the parameter before defaulted last accepts.
447+
if (paramType->is<AnyFunctionType>()) {
448+
lastAcceptsTrailingClosure = true;
449+
lastParamIdx -= 1;
450+
}
451+
}
452+
435453
bool isExtraClosure = false;
436454
// If there is no suitable last parameter to accept the trailing closure,
437455
// notify the listener and bail if we need to.
438-
if (!acceptsTrailingClosure(param)) {
456+
if (!lastAcceptsTrailingClosure) {
439457
if (numArgs > numParams) {
440458
// Argument before the trailing closure.
441459
unsigned prevArg = numArgs - 2;
442460
auto &arg = args[prevArg];
443461
// If the argument before trailing closure matches
444462
// last parameter, this is just a special case of
445463
// an extraneous argument.
464+
const auto param = params[numParams - 1];
446465
if (param.hasLabel() && param.getLabel() == arg.getLabel()) {
447466
isExtraClosure = true;
448467
if (listener.extraArgument(numArgs - 1))
@@ -451,7 +470,7 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
451470
}
452471

453472
if (!isExtraClosure &&
454-
listener.trailingClosureMismatch(numParams - 1, numArgs - 1))
473+
listener.trailingClosureMismatch(lastParamIdx, numArgs - 1))
455474
return true;
456475
}
457476

@@ -460,7 +479,7 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
460479
++numClaimedArgs;
461480
// Let's claim the trailing closure unless it's an extra argument.
462481
if (!isExtraClosure)
463-
parameterBindings[numParams - 1].push_back(numArgs - 1);
482+
parameterBindings[lastParamIdx].push_back(numArgs - 1);
464483
}
465484

466485
// Mark through the parameters, binding them to their arguments.

test/Constraints/closures.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,3 +907,10 @@ do {
907907
// via the 'T -> U => T -> ()' implicit conversion.
908908
let badResult = { (fn: () -> ()) in fn }
909909
// expected-error@-1 {{expression resolves to an unused function}}
910+
911+
// rdar://problem/55102498 - closure's result type can't be inferred if the last parameter has a default value
912+
func test_trailing_closure_with_defaulted_last() {
913+
func foo<T>(fn: () -> T, value: Int = 0) {}
914+
foo { 42 } // Ok
915+
foo(fn: { 42 }) // Ok
916+
}

0 commit comments

Comments
 (0)