Skip to content

Commit 17669d7

Browse files
committed
[Trailing closures] Attempt both forward and backward scans.
To better preserve source compatibility, teach the constraint solver to try both the new forward scanning rule as well as the backward scanning rule when matching a single, unlabeled trailing closure. In the extreme case, where the unlabeled trailing closure matches different parameters with the different rules, and yet both produce a potential match, introduce a disjunction to explore both possibilities. Prefer solutions that involve forward scans to those that involve backward scans, so we only use the backward scan as a fallback.
1 parent fa83750 commit 17669d7

File tree

13 files changed

+554
-130
lines changed

13 files changed

+554
-130
lines changed

include/swift/AST/Attr.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ class GenericFunctionType;
5555
class LazyConformanceLoader;
5656
class LazyMemberLoader;
5757
class PatternBindingInitializer;
58-
enum class TrailingClosureMatching: uint8_t;
5958
class TrailingWhereClause;
6059
class TypeExpr;
6160

lib/AST/Expr.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,15 +1278,29 @@ SourceRange TupleExpr::getSourceRange() const {
12781278
return { SourceLoc(), SourceLoc() };
12791279
} else {
12801280
// Scan backwards for a valid source loc.
1281+
bool hasSingleTrailingClosure = hasTrailingClosure();
12811282
for (Expr *expr : llvm::reverse(getElements())) {
12821283
// Default arguments are located at the start of their parent tuple, so
12831284
// skip over them.
12841285
if (isa<DefaultArgumentExpr>(expr))
12851286
continue;
1286-
end = expr->getEndLoc();
1287-
if (end.isValid()) {
1288-
break;
1287+
1288+
SourceLoc newEnd = expr->getEndLoc();
1289+
if (newEnd.isInvalid())
1290+
continue;
1291+
1292+
// There is a quirk with the backward scan logic for trailing
1293+
// closures that can cause arguments to be flipped. If there is a
1294+
// single trailing closure, only stop when the "end" point we hit comes
1295+
// after the close parenthesis (if there is one).
1296+
if (end.isInvalid() ||
1297+
end.getOpaquePointerValue() < newEnd.getOpaquePointerValue()) {
1298+
end = newEnd;
12891299
}
1300+
1301+
if (!hasSingleTrailingClosure || RParenLoc.isInvalid() ||
1302+
RParenLoc.getOpaquePointerValue() < end.getOpaquePointerValue())
1303+
break;
12901304
}
12911305
}
12921306
} else {

lib/Sema/CSApply.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5691,19 +5691,34 @@ Expr *ExprRewriter::coerceCallArguments(
56915691
AnyFunctionType::relabelParams(args, argLabels);
56925692

56935693
MatchCallArgumentListener listener;
5694-
SmallVector<ParamBinding, 4> parameterBindings;
56955694
auto unlabeledTrailingClosureIndex =
56965695
arg->getUnlabeledTrailingClosureIndexOfPackedArgument();
5697-
bool failed = constraints::matchCallArguments(args, params,
5698-
paramInfo,
5699-
unlabeledTrailingClosureIndex,
5700-
/*allowFixes=*/false, listener,
5701-
parameterBindings);
5702-
5703-
assert((matchCanFail || !failed) && "Call arguments did not match up?");
5704-
(void)failed;
5696+
5697+
// Determine the trailing closure matching rule that was applied. This
5698+
// is only relevant for explicit calls and subscripts.
5699+
auto trailingClosureMatching = TrailingClosureMatching::Forward;
5700+
{
5701+
SmallVector<LocatorPathElt, 4> path;
5702+
auto anchor = locator.getLocatorParts(path);
5703+
if (!path.empty() && path.back().is<LocatorPathElt::ApplyArgument>() &&
5704+
(anchor.isExpr(ExprKind::Call) || anchor.isExpr(ExprKind::Subscript))) {
5705+
auto locatorPtr = cs.getConstraintLocator(locator);
5706+
assert(solution.trailingClosureMatchingChoices.count(locatorPtr) == 1);
5707+
trailingClosureMatching = solution.trailingClosureMatchingChoices.find(
5708+
locatorPtr)->second;
5709+
}
5710+
}
5711+
5712+
auto callArgumentMatch = constraints::matchCallArguments(
5713+
args, params, paramInfo, unlabeledTrailingClosureIndex,
5714+
/*allowFixes=*/false, listener, trailingClosureMatching);
5715+
5716+
assert((matchCanFail || callArgumentMatch) &&
5717+
"Call arguments did not match up?");
57055718
(void)matchCanFail;
57065719

5720+
auto parameterBindings = std::move(callArgumentMatch->parameterBindings);
5721+
57075722
// We should either have parentheses or a tuple.
57085723
auto *argTuple = dyn_cast<TupleExpr>(arg);
57095724
auto *argParen = dyn_cast<ParenExpr>(arg);

lib/Sema/CSRanking.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
4848
llvm::errs() << "use of an unavailable declaration";
4949
break;
5050

51+
case SK_BackwardTrailingClosure:
52+
llvm::errs() << "backward scan when matching a trailing closure";
53+
break;
54+
5155
case SK_Fix:
5256
llvm::errs() << "attempting to fix the source";
5357
break;

0 commit comments

Comments
 (0)