Skip to content

Commit da1922f

Browse files
committed
Sema: Never record argument label mismatches for unlabeled trailing closures
Fixes a crash on invalid. The previous logic was causing a label mismatch constraint fix to be recorded for an unlabeled trailing closure argument matching a variadic paramater after a late recovery argument claim in `matchCallArgumentsImpl`, because the recovery claiming skips arguments matching defaulted parameters, but not variadic ones. We may want to reconsider that last part, but currently it regresses the quality of some diagnostics, and this is a targeted fix. The previous behavior is fine because the diagnosis routine associate with the constraint fix (`diagnoseArgumentLabelError`) skips unlabeled trailing closures when tallying labeling issues — *unless* there are no other issues and the tally is zero, which we assert it is not. Fixes rdar://152313388. (cherry picked from commit f2e1420)
1 parent 3780aba commit da1922f

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ static bool matchCallArgumentsImpl(
377377
assert(argIdx != numArgs && "Must have a valid index to claim");
378378
assert(!claimedArgs[argIdx] && "Argument already claimed");
379379

380+
// Prevent recording of an argument label mismatche for an unlabeled
381+
// trailing closure. An unlabeled trailing closure is necessarily the first
382+
// one and vice versa, per language syntax.
383+
if (unlabeledTrailingClosureArgIndex == argIdx) {
384+
expectedName = Identifier();
385+
}
386+
380387
if (!actualArgNames.empty()) {
381388
// We're recording argument names; record this one.
382389
actualArgNames[argIdx] = expectedName;

lib/Sema/MiscDiagnostics.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2697,8 +2697,10 @@ bool swift::diagnoseArgumentLabelError(ASTContext &ctx,
26972697
}
26982698
}
26992699

2700+
assert((numMissing + numExtra + numWrong > 0) &&
2701+
"Should not call this function with nothing to diagnose");
2702+
27002703
// Emit the diagnostic.
2701-
assert(numMissing > 0 || numExtra > 0 || numWrong > 0);
27022704
llvm::SmallString<16> haveBuffer; // note: diagOpt has references to this
27032705
llvm::SmallString<16> expectedBuffer; // note: diagOpt has references to this
27042706

test/expr/closure/trailing.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,39 @@ func rdar92521618() {
488488
if let _ = { foo {} }() {}
489489
guard let _ = { foo {} }() else { return }
490490
}
491+
492+
// Argument matching never binds trailing closure arguments to
493+
// defaulted/variadic parameters of non-function type.
494+
do {
495+
// Trailing closure not considered fulfilled by 'arg'.
496+
// Note: Used to crash.
497+
do {
498+
func variadic(arg: Int...) {} // expected-note@:10 {{'variadic(arg:)' declared here}}{{none}}
499+
func defaulted(arg: Int = 0) {}
500+
501+
let _ = variadic { return () }
502+
// expected-error@-1:22 {{trailing closure passed to parameter of type 'Int' that does not accept a closure}}{{none}}
503+
let _ = defaulted { return () }
504+
// expected-error@-1:23 {{extra trailing closure passed in call}}{{none}}
505+
}
506+
// Trailing closure considered fulfilled by 'x' instead of 'arg'.
507+
do {
508+
func variadic(arg: Int..., x: String) {} // expected-note@:10 {{'variadic(arg:x:)' declared here}}{{none}}
509+
func defaulted(arg: Int = 0, x: String) {} // expected-note@:10 {{'defaulted(arg:x:)' declared here}}{{none}}
510+
511+
let _ = variadic { return () }
512+
// expected-error@-1:22 {{trailing closure passed to parameter of type 'String' that does not accept a closure}}{{none}}
513+
let _ = defaulted { return () }
514+
// expected-error@-1:23 {{trailing closure passed to parameter of type 'String' that does not accept a closure}}{{none}}
515+
}
516+
// Trailing closure considered fulfilled by 'arg'; has function type.
517+
do {
518+
func variadic(arg: ((Int) -> Void)...) {}
519+
func defaulted(arg: ((Int) -> Void) = { _ in }) {}
520+
521+
let _ = variadic { return () }
522+
// expected-error@-1:22 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}}{{23-23= _ in}}
523+
let _ = defaulted { return () }
524+
// expected-error@-1:23 {{contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored}}{{24-24= _ in}}
525+
}
526+
}

0 commit comments

Comments
 (0)