Skip to content

Commit ea4ac2c

Browse files
committed
[CSSimplify] Fix trailing closure vs. defaulted parameter matching
If current parameter cannot accept a trailing closure, let's match current closure argument to it only if there is absolutely no possible other choice e.g. no other parameters (defaulted or not) that could accept the closure via a less restrictive backward scan.
1 parent 0909728 commit ea4ac2c

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,35 @@ static bool matchCallArgumentsImpl(
467467
return;
468468
}
469469

470+
// Let's consider current closure to be extraneous if:
471+
//
472+
// - current parameter has a default value and doesn't accept a trailing
473+
// closure; and
474+
// - no other free parameter after this one accepts a trailing closure via
475+
// forward or backward scan. This check makes sure that it's safe to
476+
// reject and push it to the next parameter without affecting backward
477+
// scan logic.
478+
//
479+
// In other words - let's push the closure argument through defaulted
480+
// parameters until it can be considered extraneous if no parameters
481+
// could possibly match it.
482+
if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) &&
483+
!parameterRequiresArgument(params, paramInfo, paramIdx)) {
484+
if (llvm::none_of(
485+
range(paramIdx + 1, params.size()), [&](unsigned idx) {
486+
return parameterBindings[idx].empty() &&
487+
(paramInfo.acceptsUnlabeledTrailingClosureArgument(
488+
idx) ||
489+
backwardScanAcceptsTrailingClosure(params[idx]));
490+
})) {
491+
haveUnfulfilledParams = true;
492+
return;
493+
}
494+
495+
// If one or more parameters can match the closure, let's check
496+
// whether backward scan is applicable here.
497+
}
498+
470499
// If this parameter does not require an argument, consider applying a
471500
// backward-match rule that skips this parameter if doing so is the only
472501
// way to successfully match arguments to parameters.

test/Constraints/callAsFunction.swift

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@
88
protocol View {}
99
struct EmptyView: View {}
1010

11+
enum Align {
12+
case top, center, bottom
13+
}
14+
1115
struct MyLayout {
16+
init(alignment: Align? = .center, spacing: Double? = 0.0) {}
17+
1218
func callAsFunction<V: View>(content: () -> V) -> MyLayout { .init() }
1319
func callAsFunction<V: View>(answer: () -> Int,
1420
content: () -> V) -> MyLayout { .init() }
1521
}
1622

1723
struct Test {
1824
var body1: MyLayout {
19-
MyLayout() {
25+
MyLayout(spacing: 1.0) {
2026
EmptyView() // Ok
2127
}
2228
}
@@ -28,6 +34,33 @@ struct Test {
2834
EmptyView() // Ok
2935
}
3036
}
37+
38+
var body3: MyLayout {
39+
MyLayout(alignment: .top) {
40+
let x = 42
41+
return x
42+
} content: {
43+
EmptyView() // Ok
44+
}
45+
}
46+
47+
var body4: MyLayout {
48+
MyLayout(spacing: 1.0) {
49+
let x = 42
50+
return x
51+
} content: {
52+
_ = 42
53+
return EmptyView() // Ok
54+
}
55+
}
56+
57+
var body5: MyLayout {
58+
MyLayout(alignment: .bottom, spacing: 1.0) {
59+
42
60+
} content: {
61+
EmptyView() // Ok
62+
}
63+
}
3164
}
3265

3366
// rdar://92912878 - filtering prevents disambiguation of `.callAsFunction`
@@ -51,3 +84,14 @@ func test_no_filtering_of_overloads() {
5184
}
5285
}
5386
}
87+
88+
func test_default_arguments_do_not_interfere() {
89+
struct S {
90+
init(a: Int? = 42, b: String = "") {}
91+
func callAsFunction(_: () -> Void) -> S { S() }
92+
}
93+
94+
_ = S { _ = 42 }
95+
_ = S(a: 42) { _ = 42 }
96+
_ = S(b: "") { _ = 42 }
97+
}

0 commit comments

Comments
 (0)