Skip to content

Commit bf3c807

Browse files
committed
[Sema] Introduce PreCheckFunctionBodyRequest
Split out the part of TypeCheckFunctionBodyRequest that inserts and removes implicit returns.
1 parent bd0aff4 commit bf3c807

File tree

3 files changed

+87
-37
lines changed

3 files changed

+87
-37
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,6 +3868,30 @@ class PreCheckReturnStmtRequest
38683868
bool isCached() const { return true; }
38693869
};
38703870

3871+
/// Performs some pre-checking of a function body, including inserting and
3872+
/// removing implict returns where needed. This request is currently
3873+
/// side-effectful, as it will mutate the body in-place.
3874+
///
3875+
/// Note this request is currently uncached as:
3876+
/// - The result will ultimately be cached by TypeCheckFunctionBodyRequest.
3877+
/// - When re-parsing function bodies in SourceKit, we can set a previously
3878+
/// type-checked function body back to being parsed, so caching the result of
3879+
/// this request would result in incorrectly attempting to restore the
3880+
/// previous body. We either need to eliminate the mutation of the AST, or
3881+
/// implement request cache invalidation through request dependencies.
3882+
class PreCheckFunctionBodyRequest
3883+
: public SimpleRequest<PreCheckFunctionBodyRequest,
3884+
BraceStmt *(AbstractFunctionDecl *),
3885+
RequestFlags::Uncached> {
3886+
public:
3887+
using SimpleRequest::SimpleRequest;
3888+
3889+
private:
3890+
friend SimpleRequest;
3891+
3892+
BraceStmt *evaluate(Evaluator &evaluator, AbstractFunctionDecl *AFD) const;
3893+
};
3894+
38713895
/// The result of the query for whether a statement can produce a single value.
38723896
class IsSingleValueStmtResult {
38733897
public:

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ SWIFT_REQUEST(TypeChecker, SynthesizeAccessorRequest,
325325
SeparatelyCached, NoLocationInfo)
326326
SWIFT_REQUEST(TypeChecker, TangentStoredPropertyRequest,
327327
llvm::Expected<VarDecl *>(VarDecl *, CanType), Cached, NoLocationInfo)
328+
SWIFT_REQUEST(TypeChecker, PreCheckFunctionBodyRequest,
329+
BraceStmt *(AbstractFunctionDecl *),
330+
Uncached, NoLocationInfo)
328331
SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyRequest,
329332
BraceStmt *(AbstractFunctionDecl *), SeparatelyCached,
330333
NoLocationInfo)

lib/Sema/TypeCheckStmt.cpp

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,7 +2641,60 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
26412641
}
26422642

26432643
BraceStmt *
2644-
TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
2644+
PreCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
2645+
AbstractFunctionDecl *AFD) const {
2646+
auto &ctx = AFD->getASTContext();
2647+
auto *body = AFD->getBody();
2648+
assert(body && "Expected body");
2649+
assert(!AFD->isBodyTypeChecked() && "Body already type-checked?");
2650+
2651+
if (auto *func = dyn_cast<FuncDecl>(AFD)) {
2652+
// Don't apply this pre-checking to functions with result builders.
2653+
if (getResultBuilderType(func))
2654+
return body;
2655+
2656+
if (func->hasSingleExpressionBody() &&
2657+
func->getResultInterfaceType()->isVoid()) {
2658+
// The function returns void. We don't need an explicit return, no
2659+
// matter what the type of the expression is. Take the inserted return
2660+
// back out.
2661+
body->setLastElement(func->getSingleExpressionBody());
2662+
}
2663+
// If there is a single statement in the body that can be turned into a
2664+
// single expression return, do so now.
2665+
if (!func->getResultInterfaceType()->isVoid()) {
2666+
if (auto *S = body->getSingleActiveStatement()) {
2667+
if (S->mayProduceSingleValue(evaluator)) {
2668+
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
2669+
ctx, S, /*DC*/ func, /*mustBeExpr*/ false);
2670+
auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE);
2671+
body->setLastElement(RS);
2672+
func->setHasSingleExpressionBody();
2673+
func->setSingleExpressionBody(SVE);
2674+
}
2675+
}
2676+
}
2677+
}
2678+
2679+
if (auto *ctor = dyn_cast<ConstructorDecl>(AFD)) {
2680+
if (body->empty() || !isKnownEndOfConstructor(body->getLastElement())) {
2681+
// For constructors, we make sure that the body ends with a "return"
2682+
// stmt, which we either implicitly synthesize, or the user can write.
2683+
// This simplifies SILGen.
2684+
SmallVector<ASTNode, 8> Elts(body->getElements().begin(),
2685+
body->getElements().end());
2686+
Elts.push_back(new (ctx) ReturnStmt(body->getRBraceLoc(),
2687+
/*value*/ nullptr,
2688+
/*implicit*/ true));
2689+
body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts,
2690+
body->getRBraceLoc(), body->isImplicit());
2691+
}
2692+
}
2693+
return body;
2694+
}
2695+
2696+
BraceStmt *
2697+
TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
26452698
AbstractFunctionDecl *AFD) const {
26462699
ASTContext &ctx = AFD->getASTContext();
26472700

@@ -2672,6 +2725,12 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
26722725
range.End);
26732726
};
26742727

2728+
// First do a pre-check of the body.
2729+
body = evaluateOrDefault(eval, PreCheckFunctionBodyRequest{AFD}, nullptr);
2730+
assert(body);
2731+
2732+
// Then apply a result builder if we have one, which if successful will
2733+
// produce a type-checked body.
26752734
bool alreadyTypeChecked = false;
26762735
if (auto *func = dyn_cast<FuncDecl>(AFD)) {
26772736
if (Type builderType = getResultBuilderType(func)) {
@@ -2686,42 +2745,6 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
26862745

26872746
body->walk(ContextualizeClosuresAndMacros(AFD));
26882747
}
2689-
} else {
2690-
if (func->hasSingleExpressionBody() &&
2691-
func->getResultInterfaceType()->isVoid()) {
2692-
// The function returns void. We don't need an explicit return, no
2693-
// matter what the type of the expression is. Take the inserted return
2694-
// back out.
2695-
body->setLastElement(func->getSingleExpressionBody());
2696-
}
2697-
// If there is a single statement in the body that can be turned into a
2698-
// single expression return, do so now.
2699-
if (!func->getResultInterfaceType()->isVoid()) {
2700-
if (auto *S = body->getSingleActiveStatement()) {
2701-
if (S->mayProduceSingleValue(evaluator)) {
2702-
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
2703-
ctx, S, /*DC*/ func, /*mustBeExpr*/ false);
2704-
auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE);
2705-
body->setLastElement(RS);
2706-
func->setHasSingleExpressionBody();
2707-
func->setSingleExpressionBody(SVE);
2708-
}
2709-
}
2710-
}
2711-
}
2712-
} else if (auto *ctor = dyn_cast<ConstructorDecl>(AFD)) {
2713-
if (body->empty() ||
2714-
!isKnownEndOfConstructor(body->getLastElement())) {
2715-
// For constructors, we make sure that the body ends with a "return" stmt,
2716-
// which we either implicitly synthesize, or the user can write. This
2717-
// simplifies SILGen.
2718-
SmallVector<ASTNode, 8> Elts(body->getElements().begin(),
2719-
body->getElements().end());
2720-
Elts.push_back(new (ctx) ReturnStmt(body->getRBraceLoc(),
2721-
/*value*/nullptr,
2722-
/*implicit*/true));
2723-
body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts,
2724-
body->getRBraceLoc(), body->isImplicit());
27252748
}
27262749
}
27272750

0 commit comments

Comments
 (0)