Skip to content

Commit 4e86c8f

Browse files
authored
Merge pull request #24403 from DougGregor/constraint-solver-trailing-closure-pruning
[Constraint solver] Reject trailing closures matching non-closure-parameters
2 parents 3b8e71e + a1af0e4 commit 4e86c8f

File tree

8 files changed

+83
-7
lines changed

8 files changed

+83
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,9 @@ ERROR(extra_argument_to_nullary_call,none,
10301030
"argument passed to call that takes no arguments", ())
10311031
ERROR(extra_trailing_closure_in_call,none,
10321032
"extra trailing closure passed in call", ())
1033+
ERROR(trailing_closure_bad_param,none,
1034+
"trailing closure passed to parameter of type %0 that does not "
1035+
"accept a closure", (Type))
10331036
ERROR(no_accessible_initializers,none,
10341037
"%0 cannot be constructed because it has no accessible initializers",
10351038
(Type))

lib/Sema/CSDiag.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3517,6 +3517,28 @@ class ArgumentMatcher : public MatchCallArgumentListener {
35173517
return true;
35183518
}
35193519

3520+
bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override {
3521+
Expr *arg = ArgExpr;
3522+
3523+
auto tuple = dyn_cast<TupleExpr>(ArgExpr);
3524+
if (tuple)
3525+
arg = tuple->getElement(argIdx);
3526+
3527+
auto &param = Parameters[paramIdx];
3528+
TC.diagnose(arg->getLoc(), diag::trailing_closure_bad_param,
3529+
param.getPlainType())
3530+
.highlight(arg->getSourceRange());
3531+
3532+
auto candidate = CandidateInfo[0];
3533+
if (candidate.getDecl())
3534+
TC.diagnose(candidate.getDecl(), diag::decl_declared_here,
3535+
candidate.getDecl()->getFullName());
3536+
3537+
Diagnosed = true;
3538+
3539+
return true;
3540+
}
3541+
35203542
bool diagnose() {
35213543
// Use matchCallArguments to determine how close the argument list is (in
35223544
// shape) to the specified candidates parameters. This ignores the

lib/Sema/CSSimplify.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ bool MatchCallArgumentListener::relabelArguments(ArrayRef<Identifier> newNames){
5454
return true;
5555
}
5656

57+
bool MatchCallArgumentListener::trailingClosureMismatch(
58+
unsigned paramIdx, unsigned argIdx) {
59+
return true;
60+
}
61+
5762
/// Produce a score (smaller is better) comparing a parameter name and
5863
/// potentially-typo'd argument name.
5964
///
@@ -205,6 +210,21 @@ static ConstraintSystem::TypeMatchOptions getDefaultDecompositionOptions(
205210
return flags | ConstraintSystem::TMF_GenerateConstraints;
206211
}
207212

213+
/// Determine whether the given parameter can accept a trailing closure.
214+
static bool acceptsTrailingClosure(const AnyFunctionType::Param &param) {
215+
Type paramTy = param.getPlainType();
216+
if (!paramTy)
217+
return true;
218+
219+
paramTy = paramTy->lookThroughAllOptionalTypes();
220+
return paramTy->isTypeParameter() ||
221+
paramTy->is<ArchetypeType>() ||
222+
paramTy->is<AnyFunctionType>() ||
223+
paramTy->isTypeVariableOrMember() ||
224+
paramTy->is<UnresolvedType>() ||
225+
paramTy->isAny();
226+
}
227+
208228
// FIXME: This should return ConstraintSystem::TypeMatchResult instead
209229
// to give more information to the solver about the failure.
210230
bool constraints::
@@ -425,6 +445,14 @@ matchCallArguments(ArrayRef<AnyFunctionType::Param> args,
425445

426446
// If we have a trailing closure, it maps to the last parameter.
427447
if (hasTrailingClosure && numParams > 0) {
448+
// If there is no suitable last parameter to accept the trailing closure,
449+
// notify the listener and bail if we need to.
450+
if (!acceptsTrailingClosure(params[numParams - 1])) {
451+
if (listener.trailingClosureMismatch(numParams - 1, numArgs - 1))
452+
return true;
453+
}
454+
455+
// Claim the parameter/argument pair.
428456
claimedArgs[numArgs-1] = true;
429457
++numClaimedArgs;
430458
parameterBindings[numParams-1].push_back(numArgs-1);

lib/Sema/CalleeCandidateInfo.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ CalleeCandidateInfo::ClosenessResultTy CalleeCandidateInfo::evaluateCloseness(
355355
result = CC_ArgumentLabelMismatch;
356356
return true;
357357
}
358+
bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx) override {
359+
result = CC_ArgumentMismatch;
360+
return true;
361+
}
358362
} listener;
359363

360364
// Use matchCallArguments to determine how close the argument list is (in

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3700,6 +3700,13 @@ class MatchCallArgumentListener {
37003700
/// \returns true to indicate that this should cause a failure, false
37013701
/// otherwise.
37023702
virtual bool relabelArguments(ArrayRef<Identifier> newNames);
3703+
3704+
/// Indicates that the trailing closure argument at the given \c argIdx
3705+
/// cannot be passed to the last parameter at \c paramIdx.
3706+
///
3707+
/// \returns true to indicate that this should cause a failure, false
3708+
/// otherwise.
3709+
virtual bool trailingClosureMismatch(unsigned paramIdx, unsigned argIdx);
37033710
};
37043711

37053712
/// Match the call arguments (as described by the given argument type) to

test/Constraints/diag_missing_arg.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@ trailingClosureSingle1() { 1 } // expected-error {{missing argument for paramete
3535

3636
func trailingClosureSingle2(x: () -> Int, y: Int) {} // expected-note * {{here}}
3737
// FIXME: Bad diagnostics.
38-
trailingClosureSingle2 { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=(x: <#() -> Int#>)}}
39-
trailingClosureSingle2() { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{24-24=x: <#() -> Int#>}}
38+
trailingClosureSingle2 { 1 } // expected-error {{cannot convert value of type '() -> Int' to expected argument type '(x: () -> Int, y: Int)'}}
39+
trailingClosureSingle2() { 1 } // expected-error {{cannot convert value of type '() -> Int' to expected argument type '(x: () -> Int, y: Int)'}}
4040

4141
func trailingClosureMulti1(x: Int, y: Int, z: () -> Int) {} // expected-note * {{here}}
4242
trailingClosureMulti1(y: 1) { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=x: <#Int#>, }}
4343
trailingClosureMulti1(x: 1) { 1 } // expected-error {{missing argument for parameter 'y' in call}} {{27-27=, y: <#Int#>}}
4444
trailingClosureMulti1(x: 1, y: 1) // expected-error {{missing argument for parameter 'z' in call}} {{33-33=, z: <#() -> Int#>}}
4545

4646
func trailingClosureMulti2(x: Int, y: () -> Int, z: Int) {} // expected-note * {{here}}
47-
trailingClosureMulti2 { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{22-22=(x: <#Int#>)}}
47+
trailingClosureMulti2 { 1 } // expected-error {{cannot convert value of type '() -> Int' to expected argument type '(x: Int, y: () -> Int, z: Int)'}}
4848
// FIXME: Bad diagnostics.
49-
trailingClosureMulti2() { 1 } // expected-error {{missing argument for parameter 'x' in call}} {{23-23=x: <#Int#>}}
50-
trailingClosureMulti2(x: 1) { 1 } // expected-error {{missing argument for parameter 'y' in call}} {{27-27=, y: <#() -> Int#>}}
49+
trailingClosureMulti2() { 1 } // expected-error {{cannot convert value of type '() -> Int' to expected argument type '(x: Int, y: () -> Int, z: Int)'}}
50+
trailingClosureMulti2(x: 1) { 1 } // expected-error {{cannot invoke 'trailingClosureMulti2' with an argument list of type '(x: Int, @escaping () -> Int)'}}
51+
// expected-note@-1{{expected an argument list of type '(x: Int, y: () -> Int, z: Int)'}}
5152

5253
func param2Func(x: Int, y: Int) {} // expected-note * {{here}}
5354
param2Func(x: 1) // expected-error {{missing argument for parameter 'y' in call}} {{16-16=, y: <#Int#>}}

test/Constraints/overload_filtering.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@ func testUnresolvedMember(i: Int) -> X {
3939
// CHECK-NEXT: introducing single enabled disjunction term {{.*}} bound to decl overload_filtering.(file).X.init(_:_:)
4040
return .init(i, i)
4141
}
42+
43+
func trailing(x: Int = 0, y: () -> Void) { }
44+
func trailing(x: Int = 0, z: Float) { }
45+
46+
func testTrailing() {
47+
// CHECK: disabled disjunction term {{.*}} bound to decl overload_filtering.(file).trailing(x:z:)
48+
trailing() { }
49+
50+
// CHECK: disabled disjunction term {{.*}} bound to decl overload_filtering.(file).trailing(x:z:)
51+
trailing(x: 5) { }
52+
}

test/expr/closure/trailing.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ func rdar17965209_test() {
132132
func limitXY(_ xy:Int, toGamut gamut: [Int]) {}
133133
let someInt = 0
134134
let intArray = [someInt]
135-
limitXY(someInt, toGamut: intArray) {} // expected-error {{extra argument 'toGamut' in call}}
136-
135+
limitXY(someInt, toGamut: intArray) {} // expected-error{{cannot invoke 'limitXY' with an argument list of type '(Int, toGamut: [Int], @escaping () -> ())'}}
136+
// expected-note@-1{{expected an argument list of type '(Int, toGamut: [Int])'}}
137137

138138
// <rdar://problem/23036383> QoI: Invalid trailing closures in stmt-conditions produce lowsy diagnostics
139139
func retBool(x: () -> Int) -> Bool {}

0 commit comments

Comments
 (0)