Skip to content

Commit 761ee54

Browse files
committed
[Constraint system] Use a visitor for solution application to closures.
Introduce a statement visitor that applies a particular solution to the body of a closure. This matches the mechanism used by function builders (and is similar to how we handle expressions in general), simplifying the logic for handling conversion-to-void-returning-closures and conversion-from-Never-returning-bodies. It is a stepping stone for type inference of multi-statement closures.
1 parent af048d6 commit 761ee54

File tree

3 files changed

+157
-113
lines changed

3 files changed

+157
-113
lines changed

lib/AST/Expr.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,16 +1889,23 @@ FORWARD_SOURCE_LOCS_TO(ClosureExpr, Body.getPointer())
18891889
Expr *ClosureExpr::getSingleExpressionBody() const {
18901890
assert(hasSingleExpressionBody() && "Not a single-expression body");
18911891
auto body = getBody()->getFirstElement();
1892-
if (body.is<Stmt *>())
1893-
return cast<ReturnStmt>(body.get<Stmt *>())->getResult();
1892+
if (auto stmt = body.dyn_cast<Stmt *>()) {
1893+
if (auto braceStmt = dyn_cast<BraceStmt>(stmt))
1894+
return braceStmt->getFirstElement().get<Expr *>();
1895+
1896+
return cast<ReturnStmt>(stmt)->getResult();
1897+
}
18941898
return body.get<Expr *>();
18951899
}
18961900

18971901
void ClosureExpr::setSingleExpressionBody(Expr *NewBody) {
18981902
assert(hasSingleExpressionBody() && "Not a single-expression body");
18991903
auto body = getBody()->getFirstElement();
1900-
if (body.is<Stmt *>()) {
1901-
cast<ReturnStmt>(body.get<Stmt *>())->setResult(NewBody);
1904+
if (auto stmt = body.dyn_cast<Stmt *>()) {
1905+
if (auto braceStmt = dyn_cast<BraceStmt>(stmt))
1906+
braceStmt->getFirstElement() = NewBody;
1907+
else
1908+
cast<ReturnStmt>(stmt)->setResult(NewBody);
19021909
return;
19031910
}
19041911
getBody()->setFirstElement(NewBody);

lib/Sema/CSClosure.cpp

Lines changed: 141 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -133,95 +133,160 @@ bool ConstraintSystem::generateConstraints(
133133

134134
// MARK: Solution application
135135

136-
/// Coerce the body of the given closure expression so that it returns
137-
/// Void rather than the value in it.
138-
static void coerceClosureExprToVoid(
139-
Solution &solution, ClosureExpr *closure) {
140-
auto &cs = solution.getConstraintSystem();
141-
auto &ctx = cs.getASTContext();
142-
143-
// Re-write the single-expression closure to return '()'
144-
assert(closure->hasSingleExpressionBody());
145-
146-
// A single-expression body contains a single return statement
147-
// prior to this transformation.
148-
auto member = closure->getBody()->getFirstElement();
149-
150-
if (member.is<Stmt *>()) {
151-
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
152-
auto singleExpr = returnStmt->getResult();
153-
auto voidExpr = cs.cacheType(TupleExpr::createEmpty(
154-
ctx, singleExpr->getStartLoc(), singleExpr->getEndLoc(),
155-
/*implicit*/ true));
156-
returnStmt->setResult(voidExpr);
157-
158-
// For l-value types, reset to the object type. This might not be strictly
159-
// necessary any more, but it's probably still a good idea.
160-
if (cs.getType(singleExpr)->is<LValueType>())
161-
cs.setType(singleExpr,
162-
cs.getType(singleExpr)->getWithoutSpecifierType());
163-
164-
solution.setExprTypes(singleExpr);
165-
TypeChecker::checkIgnoredExpr(singleExpr);
166-
167-
SmallVector<ASTNode, 2> elements;
168-
elements.push_back(singleExpr);
169-
elements.push_back(returnStmt);
170-
171-
auto braceStmt = BraceStmt::create(ctx, closure->getStartLoc(),
172-
elements, closure->getEndLoc(),
173-
/*implicit*/ true);
174-
175-
closure->setImplicit();
176-
closure->setBody(braceStmt, /*isSingleExpression*/true);
136+
namespace {
137+
138+
/// Statement visitor that applies constraints for a given closure body.
139+
class ClosureConstraintApplication
140+
: public StmtVisitor<ClosureConstraintApplication, ASTNode> {
141+
friend StmtVisitor<ClosureConstraintApplication, ASTNode>;
142+
143+
Solution &solution;
144+
ClosureExpr *closure;
145+
Type resultType;
146+
RewriteTargetFn rewriteTarget;
147+
bool isSingleExpression;
148+
149+
public:
150+
/// Whether an error was encountered while generating constraints.
151+
bool hadError = false;
152+
153+
ClosureConstraintApplication(
154+
Solution &solution, ClosureExpr *closure, Type resultType,
155+
RewriteTargetFn rewriteTarget)
156+
: solution(solution), closure(closure), resultType(resultType),
157+
rewriteTarget(rewriteTarget),
158+
isSingleExpression(closure->hasSingleExpressionBody()) { }
159+
160+
private:
161+
/// Rewrite an expression without any particularly special context.
162+
Expr *rewriteExpr(Expr *expr) {
163+
auto result = rewriteTarget(
164+
SolutionApplicationTarget(expr, closure, CTP_Unused, Type(),
165+
/*isDiscarded=*/false));
166+
if (result)
167+
return result->getAsExpr();
168+
169+
return nullptr;
177170
}
178171

179-
// Finally, compute the proper type for the closure.
180-
auto fnType = cs.getType(closure)->getAs<FunctionType>();
181-
auto newClosureType = FunctionType::get(
182-
fnType->getParams(), ctx.TheEmptyTupleType, fnType->getExtInfo());
183-
cs.setType(closure, newClosureType);
184-
}
172+
void visitDecl(Decl *decl) {
173+
llvm_unreachable("Declarations not supported");
174+
}
185175

186-
/// Coerce a closure whose body produces \c Never into one that does not
187-
/// return its result.
188-
static void coerceClosureExprFromNever(
189-
Solution &solution, ClosureExpr *closure) {
190-
auto &cs = solution.getConstraintSystem();
176+
ASTNode visitBraceStmt(BraceStmt *braceStmt) {
177+
for (auto &node : braceStmt->getElements()) {
178+
if (auto expr = node.dyn_cast<Expr *>()) {
179+
// Rewrite the expression.
180+
if (auto rewrittenExpr = rewriteExpr(expr))
181+
node = expr;
182+
else
183+
hadError = true;
184+
} else if (auto stmt = node.dyn_cast<Stmt *>()) {
185+
node = visit(stmt);
186+
} else {
187+
visitDecl(node.get<Decl *>());
188+
}
189+
}
190+
191+
return braceStmt;
192+
}
191193

192-
// Re-write the single-expression closure to drop the 'return'.
193-
assert(closure->hasSingleExpressionBody());
194+
ASTNode visitReturnStmt(ReturnStmt *returnStmt) {
195+
auto resultExpr = returnStmt->getResult();
196+
if (!resultExpr)
197+
return returnStmt;
194198

195-
// A single-expression body contains a single return statement
196-
// prior to this transformation.
197-
auto member = closure->getBody()->getFirstElement();
199+
enum {
200+
convertToResult,
201+
coerceToVoid,
202+
coerceFromNever,
203+
} mode;
198204

199-
if (member.is<Stmt *>()) {
200-
auto returnStmt = cast<ReturnStmt>(member.get<Stmt *>());
201-
auto singleExpr = returnStmt->getResult();
205+
auto resultExprType =
206+
solution.simplifyType(solution.getType(resultExpr))->getRValueType();
207+
// A closure with a non-void return expression can coerce to a closure
208+
// that returns Void.
209+
if (resultType->isVoid() && !resultExprType->isVoid()) {
210+
mode = coerceToVoid;
202211

203-
solution.setExprTypes(singleExpr);
204-
TypeChecker::checkIgnoredExpr(singleExpr);
212+
// A single-expression closure with a Never expression type
213+
// coerces to any other function type.
214+
} else if (isSingleExpression && resultExprType->isUninhabited()) {
215+
mode = coerceFromNever;
216+
217+
// Normal rule is to coerce to the return expression to the closure type.
218+
} else {
219+
mode = convertToResult;
220+
}
205221

206-
SmallVector<ASTNode, 1> elements;
207-
elements.push_back(singleExpr);
222+
SolutionApplicationTarget resultTarget(
223+
resultExpr, closure,
224+
mode == convertToResult ? CTP_ReturnStmt : CTP_Unused,
225+
mode == convertToResult ? resultType : Type(),
226+
/*isDiscarded=*/false);
227+
if (auto newResultTarget = rewriteTarget(resultTarget))
228+
resultExpr = newResultTarget->getAsExpr();
229+
230+
switch (mode) {
231+
case convertToResult:
232+
// Record the coerced expression.
233+
returnStmt->setResult(resultExpr);
234+
return returnStmt;
235+
236+
case coerceToVoid: {
237+
// Evaluate the expression, then produce a return statement that
238+
// returns nothing.
239+
TypeChecker::checkIgnoredExpr(resultExpr);
240+
auto &ctx = solution.getConstraintSystem().getASTContext();
241+
auto newReturnStmt =
242+
new (ctx) ReturnStmt(
243+
returnStmt->getStartLoc(), nullptr, /*implicit=*/true);
244+
ASTNode elements[2] = { resultExpr, newReturnStmt };
245+
return BraceStmt::create(ctx, returnStmt->getStartLoc(),
246+
elements, returnStmt->getEndLoc(),
247+
/*implicit*/ true);
248+
}
208249

209-
auto braceStmt =
210-
BraceStmt::create(cs.getASTContext(), closure->getStartLoc(),
211-
elements, closure->getEndLoc(),
212-
/*implicit*/ true);
250+
case coerceFromNever:
251+
// Replace the return statement with its expression, so that the
252+
// expression is evaluated directly. This only works because coercion
253+
// from never is limited to single-expression closures.
254+
return resultExpr;
255+
}
213256

214-
closure->setImplicit();
215-
closure->setBody(braceStmt, /*isSingleExpression*/true);
257+
return returnStmt;
216258
}
259+
260+
#define UNSUPPORTED_STMT(STMT) ASTNode visit##STMT##Stmt(STMT##Stmt *) { \
261+
llvm_unreachable("Unsupported statement kind " #STMT); \
262+
}
263+
UNSUPPORTED_STMT(Yield)
264+
UNSUPPORTED_STMT(Defer)
265+
UNSUPPORTED_STMT(If)
266+
UNSUPPORTED_STMT(Guard)
267+
UNSUPPORTED_STMT(While)
268+
UNSUPPORTED_STMT(Do)
269+
UNSUPPORTED_STMT(DoCatch)
270+
UNSUPPORTED_STMT(RepeatWhile)
271+
UNSUPPORTED_STMT(ForEach)
272+
UNSUPPORTED_STMT(Switch)
273+
UNSUPPORTED_STMT(Case)
274+
UNSUPPORTED_STMT(Break)
275+
UNSUPPORTED_STMT(Continue)
276+
UNSUPPORTED_STMT(Fallthrough)
277+
UNSUPPORTED_STMT(Fail)
278+
UNSUPPORTED_STMT(Throw)
279+
UNSUPPORTED_STMT(PoundAssert)
280+
#undef UNSUPPORTED_STMT
281+
282+
};
283+
217284
}
218285

219286
SolutionApplicationToFunctionResult ConstraintSystem::applySolution(
220287
Solution &solution, AnyFunctionRef fn,
221288
DeclContext *&currentDC,
222-
std::function<
223-
Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>
224-
rewriteTarget) {
289+
RewriteTargetFn rewriteTarget) {
225290
auto &cs = solution.getConstraintSystem();
226291
auto closure = dyn_cast_or_null<ClosureExpr>(fn.getAbstractClosureExpr());
227292
FunctionType *closureFnType = nullptr;
@@ -274,41 +339,9 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution(
274339

275340
// If there is a single-expression body, transform that body now.
276341
if (fn.hasSingleExpressionBody()) {
277-
// Rewrite the body.
278-
SolutionApplicationTarget originalBody(
279-
fn.getSingleExpressionBody(), fn.getAsDeclContext(), CTP_Unused,
280-
Type(), /*isDiscarded=*/false);
281-
auto rewrittenBody = rewriteTarget(originalBody);
282-
if (!rewrittenBody)
283-
return SolutionApplicationToFunctionResult::Failure;
284-
285-
auto body = rewrittenBody->getAsExpr();
286-
fn.setSingleExpressionBody(body);
287-
288-
// Closures with a single-expression body have special rules regarding
289-
// Void and Never returns. Handle them now.
290-
if (closure && closure->hasSingleExpressionBody()) {
291-
auto bodyType = body->getType();
292-
293-
// A single-expression closure with a non-Void expression type
294-
// coerces to a Void-returning function type.
295-
if (closureFnType->getResult()->isVoid() && !bodyType->isVoid()) {
296-
coerceClosureExprToVoid(solution, closure);
297-
// A single-expression closure with a Never expression type
298-
// coerces to any other function type.
299-
} else if (bodyType->isUninhabited()) {
300-
coerceClosureExprFromNever(solution, closure);
301-
} else {
302-
body = solution.coerceToType(body, closureFnType->getResult(),
303-
cs.getConstraintLocator(
304-
closure,
305-
ConstraintLocator::ClosureResult));
306-
if (!body)
307-
return SolutionApplicationToFunctionResult::Failure;
308-
309-
closure->setSingleExpressionBody(body);
310-
}
311-
}
342+
ClosureConstraintApplication application(
343+
solution, closure, closureFnType->getResult(), rewriteTarget);
344+
application.visit(fn.getBody());
312345

313346
return SolutionApplicationToFunctionResult::Success;
314347
}
@@ -317,4 +350,3 @@ SolutionApplicationToFunctionResult ConstraintSystem::applySolution(
317350
solution.setExprTypes(closure);
318351
return SolutionApplicationToFunctionResult::Delay;
319352
}
320-

lib/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,11 @@ class SolutionApplicationTarget {
16251625
SolutionApplicationTarget walk(ASTWalker &walker);
16261626
};
16271627

1628+
/// A function that rewrites a solution application target in the context
1629+
/// of solution application.
1630+
using RewriteTargetFn = std::function<
1631+
Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>;
1632+
16281633
enum class ConstraintSystemPhase {
16291634
ConstraintGeneration,
16301635
Solving,

0 commit comments

Comments
 (0)