Skip to content

Commit 1b93736

Browse files
committed
[Trailing closures] Reinstate the "skip defaulted argument" heuristic.
My experiment to improve source compatibility by also performing a backward scan removed the SE-0286 heuristic that skipped binding the unlabeled trailing closure to a defaulted parameter when that would fail. Reinstate that heuristic, which makes more existing code work with the forward-scan behavior. This makes my source-compatibility improvements a quality-of-implementation
1 parent 859b638 commit 1b93736

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,36 @@ static bool backwardScanAcceptsTrailingClosure(
219219
paramTy->isAny();
220220
}
221221

222+
/// Determine whether any parameter from the given index up until the end
223+
/// requires an argument to be provided.
224+
///
225+
/// \param params The parameters themselves.
226+
/// \param paramInfo Declaration-provided information about the parameters.
227+
/// \param firstParamIdx The first parameter to examine to determine whether any
228+
/// parameter in the range \c [paramIdx, params.size()) requires an argument.
229+
/// \param beforeLabel If non-empty, stop examining parameters when we reach
230+
/// a parameter with this label.
231+
static bool anyParameterRequiresArgument(
232+
ArrayRef<AnyFunctionType::Param> params,
233+
const ParameterListInfo &paramInfo,
234+
unsigned firstParamIdx,
235+
Optional<Identifier> beforeLabel) {
236+
for (unsigned paramIdx : range(firstParamIdx, params.size())) {
237+
// If have been asked to stop when we reach a parameter with a particular
238+
// label, and we see a parameter with that label, we're done: no parameter
239+
// requires an argument.
240+
if (beforeLabel && *beforeLabel == params[paramIdx].getLabel())
241+
break;
242+
243+
// If this parameter requires an argument, tell the caller.
244+
if (parameterRequiresArgument(params, paramInfo, paramIdx))
245+
return true;
246+
}
247+
248+
// No parameters required arguments.
249+
return false;
250+
}
251+
222252
static bool matchCallArgumentsImpl(
223253
SmallVectorImpl<AnyFunctionType::Param> &args,
224254
ArrayRef<AnyFunctionType::Param> params,
@@ -406,6 +436,21 @@ static bool matchCallArgumentsImpl(
406436
return;
407437
}
408438

439+
// If this parameter does not require an argument, consider applying a
440+
// "fuzzy" match rule that skips this parameter if doing so is the only
441+
// way to successfully match arguments to parameters.
442+
if (!parameterRequiresArgument(params, paramInfo, paramIdx) &&
443+
param.getPlainType()->getASTContext().LangOpts
444+
.EnableFuzzyForwardScanTrailingClosureMatching &&
445+
anyParameterRequiresArgument(
446+
params, paramInfo, paramIdx + 1,
447+
nextArgIdx + 1 < numArgs
448+
? Optional<Identifier>(args[nextArgIdx + 1].getLabel())
449+
: Optional<Identifier>(None))) {
450+
haveUnfulfilledParams = true;
451+
return;
452+
}
453+
409454
// The argument is unlabeled, so mark the parameter as unlabeled as
410455
// well.
411456
paramLabel = Identifier();

test/expr/postfix/call/forward_trailing_closure_ambiguity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func testNotAmbiguous2() {
8888
}
8989

9090
// Not ambiguous because of a missing default argument.
91-
func notAmbiguous3( // expected-note{{'notAmbiguous3(x:a:y:b:_:c:)' declared here}}
91+
func notAmbiguous3(
9292
x: (Int) -> Int = { $0 },
9393
a: Int = 5,
9494
y: (Int) -> Int = { $0 },
@@ -98,5 +98,5 @@ func notAmbiguous3( // expected-note{{'notAmbiguous3(x:a:y:b:_:c:)' declared her
9898
) { }
9999

100100
func testNotAmbiguous3() {
101-
notAmbiguous3 { $0 } // expected-warning{{backward matching of the unlabeled trailing closure is deprecated; label the argument with '_' to suppress this warning}}
101+
notAmbiguous3 { $0 }
102102
}

test/expr/postfix/call/forward_trailing_closure_fuzzy.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
func doSomething(onError: ((Error) -> Void)? = nil, onCompletion: (Int) -> Void) { } // expected-note{{'doSomething(onError:onCompletion:)' declared here}}
3+
func doSomething(onError: ((Error) -> Void)? = nil, onCompletion: (Int) -> Void) { }
44

55
func testDoSomething() {
6-
doSomething { x in // expected-warning{{backward matching of the unlabeled trailing closure is deprecated; label the argument with 'onCompletion' to suppress this warning}}
6+
// Okay because we skip the onError.
7+
doSomething { x in
78
print(x)
89
}
910

0 commit comments

Comments
 (0)