Skip to content

Commit f97cf53

Browse files
authored
Merge pull request #26131 from DougGregor/requestify-type-check-function-body
[Type checker] Requestify type checking of a function body
2 parents 3601cc0 + ff2cb33 commit f97cf53

13 files changed

+267
-313
lines changed

Diff for: include/swift/AST/Decl.h

+5-8
Original file line numberDiff line numberDiff line change
@@ -5773,6 +5773,11 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
57735773
setBodyKind(BodyKind::Unparsed);
57745774
}
57755775

5776+
/// Provide the parsed body for the function.
5777+
void setBodyParsed(BraceStmt *S) {
5778+
setBody(S, BodyKind::Parsed);
5779+
}
5780+
57765781
/// Note that parsing for the body was delayed.
57775782
///
57785783
/// The function should return the body statement and a flag indicating
@@ -5793,14 +5798,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
57935798
setBodyKind(BodyKind::MemberwiseInitializer);
57945799
}
57955800

5796-
/// If a body has been loaded, flag that it's been type-checked.
5797-
/// This is kindof a hacky operation, but it avoids some unnecessary
5798-
/// duplication of work.
5799-
void setBodyTypeCheckedIfPresent() {
5800-
if (getBodyKind() == BodyKind::Parsed)
5801-
setBodyKind(BodyKind::TypeChecked);
5802-
}
5803-
58045801
/// Gets the body of this function, stripping the unused portions of #if
58055802
/// configs inside the body. If this function was not deserialized from a
58065803
/// .swiftmodule, this body is reconstructed from the original

Diff for: include/swift/AST/TypeCheckRequests.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -689,13 +689,14 @@ class LazyStoragePropertyRequest :
689689
bool isCached() const { return true; }
690690
};
691691

692-
/// Request to type check the body of the given function.
692+
/// Request to type check the body of the given function up to the given
693+
/// source location.
693694
///
694695
/// Produces true if an error occurred, false otherwise.
695696
/// FIXME: it would be far better to return the type-checked body.
696-
class TypeCheckFunctionBodyRequest :
697-
public SimpleRequest<TypeCheckFunctionBodyRequest,
698-
bool(AbstractFunctionDecl *),
697+
class TypeCheckFunctionBodyUntilRequest :
698+
public SimpleRequest<TypeCheckFunctionBodyUntilRequest,
699+
bool(AbstractFunctionDecl *, SourceLoc),
699700
CacheKind::Cached> {
700701
public:
701702
using SimpleRequest::SimpleRequest;
@@ -705,7 +706,8 @@ class TypeCheckFunctionBodyRequest :
705706

706707
// Evaluation.
707708
llvm::Expected<bool>
708-
evaluate(Evaluator &evaluator, AbstractFunctionDecl *func) const;
709+
evaluate(Evaluator &evaluator, AbstractFunctionDecl *func,
710+
SourceLoc endTypeCheckLoc) const;
709711

710712
public:
711713
bool isCached() const { return true; }

Diff for: include/swift/AST/TypeCheckerTypeIDZone.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ SWIFT_TYPEID(IsGetterMutatingRequest)
4040
SWIFT_TYPEID(IsSetterMutatingRequest)
4141
SWIFT_TYPEID(OpaqueReadOwnershipRequest)
4242
SWIFT_TYPEID(LazyStoragePropertyRequest)
43-
SWIFT_TYPEID(TypeCheckFunctionBodyRequest)
43+
SWIFT_TYPEID(TypeCheckFunctionBodyUntilRequest)

Diff for: include/swift/Basic/SourceLoc.h

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class SourceLoc {
7878
}
7979

8080
void dump(const SourceManager &SM) const;
81+
82+
friend size_t hash_value(SourceLoc loc) {
83+
return reinterpret_cast<uintptr_t>(loc.getOpaquePointerValue());
84+
}
85+
86+
friend void simple_display(raw_ostream &OS, const SourceLoc &loc) {
87+
// Nothing meaningful to print.
88+
}
8189
};
8290

8391
/// SourceRange in swift is a pair of locations. However, note that the end

Diff for: lib/AST/Decl.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -3775,6 +3775,14 @@ DestructorDecl *ClassDecl::getDestructor() {
37753775
return cast<DestructorDecl>(results.front());
37763776
}
37773777

3778+
/// Synthesizer callback for an empty implicit function body.
3779+
static std::pair<BraceStmt *, bool>
3780+
synthesizeEmptyFunctionBody(AbstractFunctionDecl *afd, void *context) {
3781+
ASTContext &ctx = afd->getASTContext();
3782+
return { BraceStmt::create(ctx, afd->getLoc(), { }, afd->getLoc(), true),
3783+
/*isTypeChecked=*/true };
3784+
}
3785+
37783786
void ClassDecl::addImplicitDestructor() {
37793787
if (hasDestructor() || isInvalid())
37803788
return;
@@ -3785,8 +3793,8 @@ void ClassDecl::addImplicitDestructor() {
37853793
DD->setImplicit();
37863794
DD->setValidationToChecked();
37873795

3788-
// Create an empty body for the destructor.
3789-
DD->setBody(BraceStmt::create(ctx, getLoc(), { }, getLoc(), true));
3796+
// Synthesize an empty body for the destructor as needed.
3797+
DD->setBodySynthesizer(synthesizeEmptyFunctionBody);
37903798
addMember(DD);
37913799

37923800
// Propagate access control and versioned-ness.

Diff for: lib/Parse/ParseDecl.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -4636,9 +4636,9 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags,
46364636
Indices, ElementTy, StaticLoc, Flags, AccessorKind::Get,
46374637
storage, this, /*AccessorKeywordLoc*/ SourceLoc());
46384638
CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
4639-
getter->setBody(BraceStmt::create(Context, Tok.getLoc(),
4640-
ASTNode(CCE), Tok.getLoc(),
4641-
/*implicit*/ true));
4639+
getter->setBodyParsed(BraceStmt::create(Context, Tok.getLoc(),
4640+
ASTNode(CCE), Tok.getLoc(),
4641+
/*implicit*/ true));
46424642
accessors.add(getter);
46434643
CodeCompletion->setParsedDecl(getter);
46444644
} else {
@@ -5763,7 +5763,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
57635763
ParserResult<BraceStmt> Body = parseBraceItemList(diag::invalid_diagnostic);
57645764
if (!Body.isNull()) {
57655765
BraceStmt * BS = Body.get();
5766-
AFD->setBody(BS);
5766+
AFD->setBodyParsed(BS);
57675767

57685768
// If the body consists of a single expression, turn it into a return
57695769
// statement.
@@ -5853,7 +5853,7 @@ bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
58535853
// FIXME: Should do some sort of error recovery here?
58545854
return true;
58555855
} else {
5856-
AFD->setBody(Body.get());
5856+
AFD->setBodyParsed(Body.get());
58575857
}
58585858

58595859
return false;

Diff for: lib/Parse/ParseStmt.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ ParserResult<Stmt> Parser::parseStmtDefer() {
988988
if (Body.isNull())
989989
return nullptr;
990990
Status |= Body;
991-
tempDecl->setBody(Body.get());
991+
tempDecl->setBodyParsed(Body.get());
992992
}
993993

994994
SourceLoc loc = tempDecl->getBodySourceRange().Start;

Diff for: lib/Sema/BuilderTransform.cpp

+9-31
Original file line numberDiff line numberDiff line change
@@ -476,47 +476,25 @@ static bool hasReturnStmt(Stmt *stmt) {
476476
return finder.hasReturnStmt;
477477
}
478478

479-
bool TypeChecker::typeCheckFunctionBuilderFuncBody(FuncDecl *FD,
480-
Type builderType) {
479+
BraceStmt *
480+
TypeChecker::applyFunctionBuilderBodyTransform(FuncDecl *FD,
481+
BraceStmt *body,
482+
Type builderType) {
481483
// Try to build a single result expression.
482484
BuilderClosureVisitor visitor(Context, nullptr,
483485
/*wantExpr=*/true, builderType);
484-
Expr *returnExpr = visitor.visit(FD->getBody());
486+
Expr *returnExpr = visitor.visit(body);
485487
if (!returnExpr)
486-
return true;
488+
return nullptr;
487489

488490
// Make sure we have a usable result type for the body.
489491
Type returnType = AnyFunctionRef(FD).getBodyResultType();
490492
if (!returnType || returnType->hasError())
491-
return true;
492-
493-
TypeCheckExprOptions options = {};
494-
if (auto opaque = returnType->getAs<OpaqueTypeArchetypeType>()) {
495-
if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(FD))
496-
options |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType;
497-
}
498-
499-
// If we are performing code-completion inside the functions body, supress
500-
// diagnostics to workaround typechecking performance problems.
501-
if (Context.SourceMgr.rangeContainsCodeCompletionLoc(
502-
FD->getBody()->getSourceRange()))
503-
options |= TypeCheckExprFlags::SuppressDiagnostics;
504-
505-
// Type-check the single result expression.
506-
Type returnExprType = typeCheckExpression(returnExpr, FD,
507-
TypeLoc::withoutLoc(returnType),
508-
CTP_ReturnStmt, options);
509-
if (!returnExprType)
510-
return true;
511-
assert(returnExprType->isEqual(returnType));
493+
return nullptr;
512494

513495
auto returnStmt = new (Context) ReturnStmt(SourceLoc(), returnExpr);
514-
auto origBody = FD->getBody();
515-
auto fakeBody = BraceStmt::create(Context, origBody->getLBraceLoc(),
516-
{ returnStmt },
517-
origBody->getRBraceLoc());
518-
FD->setBody(fakeBody);
519-
return false;
496+
return BraceStmt::create(Context, body->getLBraceLoc(), { returnStmt },
497+
body->getRBraceLoc());
520498
}
521499

522500
ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder(

Diff for: lib/Sema/MiscDiagnostics.cpp

+12-8
Original file line numberDiff line numberDiff line change
@@ -2235,23 +2235,26 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
22352235
TypeChecker &TC;
22362236
AbstractFunctionDecl *Implementation;
22372237
OpaqueTypeDecl *OpaqueDecl;
2238+
BraceStmt *Body;
22382239
SmallVector<std::pair<Expr*, Type>, 4> Candidates;
22392240

22402241
bool HasInvalidReturn = false;
22412242

22422243
public:
22432244
OpaqueUnderlyingTypeChecker(TypeChecker &TC,
22442245
AbstractFunctionDecl *Implementation,
2245-
OpaqueTypeDecl *OpaqueDecl)
2246+
OpaqueTypeDecl *OpaqueDecl,
2247+
BraceStmt *Body)
22462248
: TC(TC),
22472249
Implementation(Implementation),
2248-
OpaqueDecl(OpaqueDecl)
2250+
OpaqueDecl(OpaqueDecl),
2251+
Body(Body)
22492252
{
22502253

22512254
}
22522255

22532256
void check() {
2254-
Implementation->getBody()->walk(*this);
2257+
Body->walk(*this);
22552258

22562259
// If given function has any invalid returns in the body
22572260
// let's not try to validate the types, since it wouldn't
@@ -2835,26 +2838,27 @@ performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD) {
28352838

28362839
/// Perform diagnostics for func/init/deinit declarations.
28372840
void swift::performAbstractFuncDeclDiagnostics(TypeChecker &TC,
2838-
AbstractFunctionDecl *AFD) {
2839-
assert(AFD->getBody() && "Need a body to check");
2841+
AbstractFunctionDecl *AFD,
2842+
BraceStmt *body) {
2843+
assert(body && "Need a body to check");
28402844

28412845
// Don't produce these diagnostics for implicitly generated code.
28422846
if (AFD->getLoc().isInvalid() || AFD->isImplicit() || AFD->isInvalid())
28432847
return;
28442848

28452849
// Check for unused variables, as well as variables that are could be
28462850
// declared as constants.
2847-
AFD->getBody()->walk(VarDeclUsageChecker(TC, AFD));
2851+
body->walk(VarDeclUsageChecker(TC, AFD));
28482852

28492853
// If the function has an opaque return type, check the return expressions
28502854
// to determine the underlying type.
28512855
if (auto opaqueResultTy = AFD->getOpaqueResultTypeDecl()) {
2852-
OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy).check();
2856+
OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy, body).check();
28532857
} else if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
28542858
if (accessor->isGetter()) {
28552859
if (auto opaqueResultTy
28562860
= accessor->getStorage()->getOpaqueResultTypeDecl()) {
2857-
OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy).check();
2861+
OpaqueUnderlyingTypeChecker(TC, AFD, opaqueResultTy, body).check();
28582862
}
28592863
}
28602864
}

Diff for: lib/Sema/MiscDiagnostics.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ void performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E,
4343
void performStmtDiagnostics(TypeChecker &TC, const Stmt *S);
4444

4545
void performAbstractFuncDeclDiagnostics(TypeChecker &TC,
46-
AbstractFunctionDecl *AFD);
46+
AbstractFunctionDecl *AFD,
47+
BraceStmt *body);
4748

4849
/// Perform diagnostics on the top level code declaration.
4950
void performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD);

Diff for: lib/Sema/TypeCheckDecl.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -5599,6 +5599,16 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
55995599
}
56005600
}
56015601

5602+
/// Synthesizer callback for a function body consisting of "return".
5603+
static std::pair<BraceStmt *, bool>
5604+
synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) {
5605+
ASTContext &ctx = afd->getASTContext();
5606+
SmallVector<ASTNode, 1> stmts;
5607+
stmts.push_back(new (ctx) ReturnStmt(afd->getLoc(), nullptr));
5608+
return { BraceStmt::create(ctx, afd->getLoc(), stmts, afd->getLoc(), true),
5609+
/*isTypeChecked=*/true };
5610+
}
5611+
56025612
void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) {
56035613
FrontendStatsTracer StatsTracer(Context.Stats, "define-default-ctor", decl);
56045614
PrettyStackTraceDecl stackTrace("defining default constructor for",
@@ -5624,11 +5634,8 @@ void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) {
56245634
// Add the constructor.
56255635
decl->addMember(ctor);
56265636

5627-
// Create an empty body for the default constructor.
5628-
SmallVector<ASTNode, 1> stmts;
5629-
stmts.push_back(new (Context) ReturnStmt(decl->getLoc(), nullptr));
5630-
ctor->setBody(BraceStmt::create(Context, SourceLoc(), stmts, SourceLoc()),
5631-
AbstractFunctionDecl::BodyKind::TypeChecked);
5637+
// Lazily synthesize an empty body for the default constructor.
5638+
ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody);
56325639
}
56335640

56345641
static void validateAttributes(TypeChecker &TC, Decl *D) {

0 commit comments

Comments
 (0)