Skip to content

Commit f8eee50

Browse files
committed
[Constraint solver] Fix function builders with single-expression closures
Fix a few related issues involving the interaction with single-expression closures: * A single-expression closure can have a "return" in it; in such cases, disable the function-builder transform. * Have the function builder constraint generator look through the "return" statement in a single-expression closure the same way as solution application does Fixes rdar://problem/59045763, where we rejected some well-formed code involving a single-expression closure with a "return" keyword.
1 parent 77da6bd commit f8eee50

File tree

4 files changed

+39
-22
lines changed

4 files changed

+39
-22
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,15 @@ class BuilderClosureVisitor
244244
expressions.push_back(buildVarRef(childVar, childVar->getLoc()));
245245
};
246246

247-
for (const auto &node : braceStmt->getElements()) {
247+
for (auto node : braceStmt->getElements()) {
248+
// Implicit returns in single-expression function bodies are treated
249+
// as the expression.
250+
if (auto returnStmt =
251+
dyn_cast_or_null<ReturnStmt>(node.dyn_cast<Stmt *>())) {
252+
assert(returnStmt->isImplicit());
253+
node = returnStmt->getResult();
254+
}
255+
248256
if (auto stmt = node.dyn_cast<Stmt *>()) {
249257
addChild(visit(stmt));
250258
continue;
@@ -291,14 +299,9 @@ class BuilderClosureVisitor
291299
}
292300

293301
VarDecl *visitReturnStmt(ReturnStmt *stmt) {
294-
// Allow implicit returns due to 'return' elision.
295-
if (!stmt->isImplicit() || !stmt->hasResult()) {
296-
if (!unhandledNode)
297-
unhandledNode = stmt;
298-
return nullptr;
299-
}
300-
301-
return captureExpr(stmt->getResult(), /*oneWay=*/true);
302+
if (!unhandledNode)
303+
unhandledNode = stmt;
304+
return nullptr;
302305
}
303306

304307
VarDecl *visitDoStmt(DoStmt *doStmt) {
@@ -1117,7 +1120,8 @@ Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
11171120
return nullptr;
11181121
}
11191122

1120-
ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder(
1123+
Optional<ConstraintSystem::TypeMatchResult>
1124+
ConstraintSystem::matchFunctionBuilder(
11211125
AnyFunctionRef fn, Type builderType, Type bodyResultType,
11221126
ConstraintKind bodyResultConstraintKind,
11231127
ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) {
@@ -1141,7 +1145,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder(
11411145
case FunctionBuilderBodyPreCheck::HasReturnStmt:
11421146
// If the body has a return statement, suppress the transform but
11431147
// continue solving the constraint system.
1144-
return getTypeMatchSuccess();
1148+
return None;
11451149
}
11461150

11471151
// Check the form of this body to see if we can apply the
@@ -1287,12 +1291,6 @@ class PreCheckFunctionBuilderApplication : public ASTWalker {
12871291
llvm::Expected<FunctionBuilderBodyPreCheck>
12881292
PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval,
12891293
AnyFunctionRef fn) const {
1290-
// Single-expression closures should already have been pre-checked.
1291-
if (auto closure = fn.getAbstractClosureExpr()) {
1292-
if (closure->hasSingleExpressionBody())
1293-
return FunctionBuilderBodyPreCheck::Okay;
1294-
}
1295-
12961294
return PreCheckFunctionBuilderApplication(fn, false).run();
12971295
}
12981296

lib/Sema/CSSimplify.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6635,10 +6635,11 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
66356635
auto *calleeLocator = getCalleeLocator(getConstraintLocator(locator));
66366636
if (auto functionBuilderType = getFunctionBuilderTypeFor(
66376637
*this, argToParam->getParamIdx(), calleeLocator)) {
6638-
auto result = matchFunctionBuilder(
6639-
closure, functionBuilderType, closureType->getResult(),
6640-
ConstraintKind::Conversion, calleeLocator, locator);
6641-
return result.isSuccess();
6638+
if (auto result = matchFunctionBuilder(
6639+
closure, functionBuilderType, closureType->getResult(),
6640+
ConstraintKind::Conversion, calleeLocator, locator)) {
6641+
return result->isSuccess();
6642+
}
66426643
}
66436644
}
66446645
}

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3726,7 +3726,10 @@ class ConstraintSystem {
37263726
void simplifyDisjunctionChoice(Constraint *choice);
37273727

37283728
/// Apply the given function builder to the closure expression.
3729-
TypeMatchResult matchFunctionBuilder(
3729+
///
3730+
/// \returns \c None when the function builder cannot be applied at all,
3731+
/// otherwise the result of applying the function builder.
3732+
Optional<TypeMatchResult> matchFunctionBuilder(
37303733
AnyFunctionRef fn, Type builderType, Type bodyResultType,
37313734
ConstraintKind bodyResultConstraintKind,
37323735
ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator);

test/Constraints/function_builder_diags.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,18 @@ func checkConditions(cond: Bool) {
284284
}
285285
}
286286
}
287+
288+
// Check that a closure with a single "return" works with function builders.
289+
func checkSingleReturn(cond: Bool) {
290+
tuplify(cond) { value in
291+
return (value, 17)
292+
}
293+
294+
tuplify(cond) { value in
295+
(value, 17)
296+
}
297+
298+
tuplify(cond) {
299+
($0, 17)
300+
}
301+
}

0 commit comments

Comments
 (0)