Skip to content

Commit 0e370db

Browse files
authored
Merge pull request #40475 from xedin/se-0326-non-void-result-diag
[CSClosure] Support empty `return` when closure result is optional `Void`
2 parents be59474 + f287f7e commit 0e370db

File tree

2 files changed

+111
-8
lines changed

2 files changed

+111
-8
lines changed

lib/Sema/CSClosure.cpp

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ using namespace swift::constraints;
2525

2626
namespace {
2727

28+
// Produce an implicit empty tuple expression.
29+
Expr *getVoidExpr(ASTContext &ctx) {
30+
auto *voidExpr = TupleExpr::createEmpty(ctx,
31+
/*LParenLoc=*/SourceLoc(),
32+
/*RParenLoc=*/SourceLoc(),
33+
/*Implicit=*/true);
34+
voidExpr->setType(ctx.TheEmptyTupleType);
35+
return voidExpr;
36+
}
37+
2838
/// Find any type variable references inside of an AST node.
2939
class TypeVariableRefFinder : public ASTWalker {
3040
ConstraintSystem &CS;
@@ -829,14 +839,9 @@ class ClosureConstraintGenerator
829839
resultExpr = returnStmt->getResult();
830840
assert(resultExpr && "non-empty result without expression?");
831841
} else {
832-
auto &ctx = closure->getASTContext();
833842
// If this is simplify `return`, let's create an empty tuple
834843
// which is also useful if contextual turns out to be e.g. `Void?`.
835-
resultExpr = TupleExpr::createEmpty(ctx,
836-
/*LParenLoc=*/SourceLoc(),
837-
/*RParenLoc=*/SourceLoc(),
838-
/*Implicit=*/true);
839-
resultExpr->setType(ctx.TheEmptyTupleType);
844+
resultExpr = getVoidExpr(closure->getASTContext());
840845
}
841846

842847
SolutionApplicationTarget target(resultExpr, closure, CTP_ReturnStmt,
@@ -1283,9 +1288,66 @@ class ClosureConstraintApplication
12831288
}
12841289
}
12851290

1291+
// Source compatibility workaround.
1292+
//
1293+
// func test<T>(_: () -> T?) {
1294+
// ...
1295+
// }
1296+
//
1297+
// A multi-statement closure passed to `test` that has an optional
1298+
// `Void` result type inferred from the body allows:
1299+
// - empty `return`(s);
1300+
// - to skip `return nil` or `return ()` at the end.
1301+
//
1302+
// Implicit `return ()` has to be inserted as the last element
1303+
// of the body if there is none. This wasn't needed before SE-0326
1304+
// because result type was (incorrectly) inferred as `Void` due to
1305+
// the body being skipped.
1306+
if (!closure->hasSingleExpressionBody() &&
1307+
closure->getBody() == braceStmt) {
1308+
if (resultType->getOptionalObjectType() &&
1309+
resultType->lookThroughAllOptionalTypes()->isVoid() &&
1310+
!braceStmt->getLastElement().isStmt(StmtKind::Return)) {
1311+
return addImplicitVoidReturn(braceStmt);
1312+
}
1313+
}
1314+
12861315
return braceStmt;
12871316
}
12881317

1318+
ASTNode addImplicitVoidReturn(BraceStmt *braceStmt) {
1319+
auto &ctx = closure->getASTContext();
1320+
auto &cs = solution.getConstraintSystem();
1321+
1322+
auto *resultExpr = getVoidExpr(ctx);
1323+
cs.cacheExprTypes(resultExpr);
1324+
1325+
auto *returnStmt = new (ctx) ReturnStmt(SourceLoc(), resultExpr,
1326+
/*implicit=*/true);
1327+
1328+
// For a target for newly created result and apply a solution
1329+
// to it, to make sure that optional injection happens required
1330+
// number of times.
1331+
{
1332+
SolutionApplicationTarget target(resultExpr, closure, CTP_ReturnStmt,
1333+
resultType,
1334+
/*isDiscarded=*/false);
1335+
cs.setSolutionApplicationTarget(returnStmt, target);
1336+
1337+
visitReturnStmt(returnStmt);
1338+
}
1339+
1340+
// Re-create brace statement with an additional `return` at the end.
1341+
1342+
SmallVector<ASTNode, 4> elements;
1343+
elements.append(braceStmt->getElements().begin(),
1344+
braceStmt->getElements().end());
1345+
elements.push_back(returnStmt);
1346+
1347+
return BraceStmt::create(ctx, braceStmt->getLBraceLoc(), elements,
1348+
braceStmt->getRBraceLoc());
1349+
}
1350+
12891351
ASTNode visitReturnStmt(ReturnStmt *returnStmt) {
12901352
if (!returnStmt->hasResult()) {
12911353
// If contextual is not optional, there is nothing to do here.
@@ -1473,11 +1535,13 @@ bool ConstraintSystem::applySolutionToBody(Solution &solution,
14731535
auto closureType = cs.getType(closure)->castTo<FunctionType>();
14741536
ClosureConstraintApplication application(
14751537
solution, closure, closureType->getResult(), rewriteTarget);
1476-
application.visit(closure->getBody());
1538+
auto body = application.visit(closure->getBody());
14771539

1478-
if (application.hadError)
1540+
if (!body || application.hadError)
14791541
return true;
14801542

1543+
closure->setBody(cast<BraceStmt>(body.get<Stmt *>()),
1544+
closure->hasSingleExpressionBody());
14811545
closure->setBodyState(ClosureExpr::BodyState::TypeCheckedWithSignature);
14821546
return false;
14831547
}

test/expr/closure/multi_statement.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,42 @@ let _ = {
139139
print(i)
140140
}
141141
}
142+
143+
func test_workaround_for_optional_void_result() {
144+
func test<T>(_: (Int?) -> T?) {}
145+
146+
test {
147+
guard let x = $0 else {
148+
return // Ok
149+
}
150+
151+
print(x)
152+
}
153+
154+
test {
155+
if $0! > 0 {
156+
return
157+
}
158+
159+
let _ = $0
160+
}
161+
162+
func test_concrete(_: (Int) -> Void?) {
163+
}
164+
165+
test_concrete {
166+
guard let x = Optional($0) else {
167+
return // Ok
168+
}
169+
170+
print(x)
171+
}
172+
173+
test_concrete {
174+
if $0 > 0 {
175+
return // Ok
176+
}
177+
178+
let _ = $0
179+
}
180+
}

0 commit comments

Comments
 (0)