Skip to content

Commit 29ec113

Browse files
committed
[CSClosures] Model return statements as solution application targets
Each return statement gets a contextual type from outer context, so the best model for that is via solution application target.
1 parent 1dd76d8 commit 29ec113

File tree

1 file changed

+45
-28
lines changed

1 file changed

+45
-28
lines changed

lib/Sema/CSClosure.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -769,30 +769,57 @@ class ClosureConstraintGenerator
769769
}
770770

771771
void visitReturnStmt(ReturnStmt *returnStmt) {
772-
Type resultType;
772+
auto contextualTy = cs.getClosureType(closure)->getResult();
773773

774-
if (returnStmt->hasResult()) {
774+
// Single-expression closures are effectively a `return` statement,
775+
// so let's give them a special locator as to indicate that.
776+
if (closure->hasSingleExpressionBody()) {
775777
auto *expr = returnStmt->getResult();
776-
assert(expr && "non-empty result without expression?");
778+
assert(expr && "single expression closure without expression?");
777779

778-
// FIXME: Use SolutionApplicationTarget?
779780
expr = cs.generateConstraints(expr, closure, /*isInputExpression=*/false);
780781
if (!expr) {
781782
hadError = true;
782783
return;
783784
}
784785

785-
resultType = cs.getType(expr);
786+
cs.addConstraint(
787+
ConstraintKind::Conversion, cs.getType(expr), contextualTy,
788+
cs.getConstraintLocator(
789+
closure, LocatorPathElt::ClosureBody(
790+
/*hasReturn=*/!returnStmt->isImplicit())));
791+
return;
792+
}
793+
794+
Expr *resultExpr;
795+
796+
if (returnStmt->hasResult()) {
797+
resultExpr = returnStmt->getResult();
798+
assert(resultExpr && "non-empty result without expression?");
786799
} else {
787-
resultType = cs.getASTContext().getVoidDecl()->getDeclaredInterfaceType();
800+
auto &ctx = closure->getASTContext();
801+
// If this is simplify `return`, let's create an empty tuple
802+
// which is also useful if contextual turns out to be e.g. `Void?`.
803+
resultExpr = TupleExpr::createEmpty(ctx,
804+
/*LParenLoc=*/SourceLoc(),
805+
/*RParenLoc=*/SourceLoc(),
806+
/*Implicit=*/true);
807+
resultExpr->setType(ctx.TheEmptyTupleType);
788808
}
789809

790-
// FIXME: Locator should point at the return statement?
791-
bool hasReturn = hasExplicitResult(closure);
792-
cs.addConstraint(ConstraintKind::Conversion, resultType,
793-
cs.getClosureType(closure)->getResult(),
794-
cs.getConstraintLocator(
795-
closure, LocatorPathElt::ClosureBody(hasReturn)));
810+
SolutionApplicationTarget target(resultExpr, closure, CTP_ReturnStmt,
811+
contextualTy,
812+
/*isDiscarded=*/false);
813+
814+
// FIXME: Use SolutionApplicationTarget?
815+
if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) {
816+
hadError = true;
817+
return;
818+
}
819+
820+
cs.setContextualType(target.getAsExpr(), TypeLoc::withoutLoc(contextualTy),
821+
CTP_ReturnStmt);
822+
cs.setSolutionApplicationTarget(returnStmt, target);
796823
}
797824

798825
bool isSupportedMultiStatementClosure() const {
@@ -1205,33 +1232,23 @@ class ClosureConstraintApplication
12051232

12061233
ASTNode visitReturnStmt(ReturnStmt *returnStmt) {
12071234
if (!returnStmt->hasResult()) {
1235+
// If contextual is not optional, there is nothing to do here.
1236+
if (resultType->isVoid())
1237+
return returnStmt;
1238+
12081239
// It's possible to infer e.g. `Void?` for cases where
12091240
// `return` doesn't have an expression. If contextual
12101241
// type is `Void` wrapped into N optional types, let's
12111242
// add an implicit `()` expression and let it be injected
12121243
// into optional required number of times.
1213-
auto &ctx = closure->getASTContext();
1214-
1215-
// If contextual is not optional, there is nothing to do here.
1216-
if (resultType->isVoid())
1217-
return returnStmt;
12181244

12191245
assert(resultType->getOptionalObjectType() &&
12201246
resultType->lookThroughAllOptionalTypes()->isVoid());
12211247

12221248
auto &cs = solution.getConstraintSystem();
12231249

1224-
// Build an implicit empty tuple to represent `Void` return
1225-
auto *emptyTuple = TupleExpr::createEmpty(ctx,
1226-
/*LParenLoc=*/SourceLoc(),
1227-
/*RParenLoc=*/SourceLoc(),
1228-
/*Implicit=*/true);
1229-
emptyTuple->setType(ctx.TheEmptyTupleType);
1230-
// Cache the type of this new expression in the constraint system
1231-
// for the future reference.
1232-
cs.cacheExprTypes(emptyTuple);
1233-
1234-
returnStmt->setResult(emptyTuple);
1250+
auto target = *cs.getSolutionApplicationTarget(returnStmt);
1251+
returnStmt->setResult(target.getAsExpr());
12351252
}
12361253

12371254
auto *resultExpr = returnStmt->getResult();

0 commit comments

Comments
 (0)