Skip to content

Add a request to lazily parse function bodies. #26972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5594,6 +5594,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
SourceRange BodyRange;
};

friend class ParseAbstractFunctionBodyRequest;

CaptureInfo Captures;

/// Location of the 'throws' token.
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/ParseRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,28 @@ class ParseMembersRequest :
bool isCached() const { return true; }
};

/// Parse the body of a function, initializer, or deinitializer.
class ParseAbstractFunctionBodyRequest :
public SimpleRequest<ParseAbstractFunctionBodyRequest,
BraceStmt *(AbstractFunctionDecl *),
CacheKind::SeparatelyCached>
{
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
BraceStmt *evaluate(Evaluator &evaluator, AbstractFunctionDecl *afd) const;

public:
// Caching
bool isCached() const { return true; }
Optional<BraceStmt *> getCachedResult() const;
void cacheResult(BraceStmt *value) const;
};

/// The zone number for the parser.
#define SWIFT_TYPEID_ZONE Parse
#define SWIFT_TYPEID_HEADER "swift/AST/ParseTypeIDZone.def"
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ParseTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
//===----------------------------------------------------------------------===//

SWIFT_REQUEST(Parse, ParseMembersRequest)
SWIFT_REQUEST(Parse, ParseAbstractFunctionBodyRequest)
2 changes: 1 addition & 1 deletion include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ class Parser {
DeclAttributes &Attributes,
bool HasFuncKeyword = true);
void parseAbstractFunctionBody(AbstractFunctionDecl *AFD);
bool parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD);
ParserResult<ProtocolDecl> parseDeclProtocol(ParseDeclOptions Flags,
DeclAttributes &Attributes);

Expand Down
8 changes: 0 additions & 8 deletions include/swift/Parse/PersistentParserState.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,6 @@ class PersistentParserState {
PersistentParserState(ASTContext &ctx) : PersistentParserState() { }
~PersistentParserState();

void delayFunctionBodyParsing(AbstractFunctionDecl *AFD,
SourceRange BodyRange,
SourceLoc PreviousLoc);
std::unique_ptr<FunctionBodyState>
takeFunctionBodyState(AbstractFunctionDecl *AFD);

bool hasFunctionBodyState(AbstractFunctionDecl *AFD);

void delayDecl(DelayedDeclKind Kind, unsigned Flags,
DeclContext *ParentContext,
SourceRange BodyRange, SourceLoc PreviousLoc);
Expand Down
6 changes: 5 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,13 @@ PrintOptions PrintOptions::printParseableInterfaceFile(bool preferTypeRepr) {

result.FunctionBody = [](const ValueDecl *decl, ASTPrinter &printer) {
auto AFD = dyn_cast<AbstractFunctionDecl>(decl);
if (!AFD || !AFD->hasInlinableBodyText()) return;
if (!AFD)
return;
if (AFD->getResilienceExpansion() != ResilienceExpansion::Minimal)
return;
if (!AFD->hasInlinableBodyText())
return;

SmallString<128> scratch;
printer << " " << AFD->getInlinableBodyText(scratch);
};
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1272,7 +1272,7 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
// Create scope for the body.
// We create body scopes when there is no body for source kit to complete
// erroneous code in bodies. But don't let compiler synthesize one.
if (decl->getBody(false) && decl->getBodySourceRange().isValid()) {
if (decl->getBodySourceRange().isValid() && decl->getBody(false)) {
if (AbstractFunctionBodyScope::isAMethod(decl))
scopeCreator.constructExpandAndInsertUncheckable<MethodBodyScope>(leaf,
decl);
Expand Down Expand Up @@ -1771,10 +1771,10 @@ bool IterableTypeScope::isBodyCurrent() const {
}

void AbstractFunctionBodyScope::beCurrent() {
bodyWhenLastExpanded = decl->getBody();
bodyWhenLastExpanded = decl->getBody(false);
}
bool AbstractFunctionBodyScope::isCurrent() const {
return bodyWhenLastExpanded == decl->getBody();
return bodyWhenLastExpanded == decl->getBody(false);
;
}

Expand Down
92 changes: 64 additions & 28 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ParseRequests.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
Expand Down Expand Up @@ -6307,37 +6308,22 @@ bool AbstractFunctionDecl::argumentNameIsAPIByDefault() const {
}

BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const {
switch (getBodyKind()) {
case BodyKind::Deserialized:
case BodyKind::MemberwiseInitializer:
case BodyKind::None:
case BodyKind::Skipped:
if ((getBodyKind() == BodyKind::Synthesize ||
getBodyKind() == BodyKind::Unparsed) &&
!canSynthesize)
return nullptr;

case BodyKind::Parsed:
case BodyKind::TypeChecked:
return Body;
ASTContext &ctx = getASTContext();

case BodyKind::Unparsed:
// FIXME: Go parse now!
// Don't allow getBody() to trigger parsing of an unparsed body containing the
// code completion location.
if (getBodyKind() == BodyKind::Unparsed &&
ctx.SourceMgr.rangeContainsCodeCompletionLoc(getBodySourceRange())) {
return nullptr;

case BodyKind::Synthesize: {
if (!canSynthesize)
return nullptr;

const_cast<AbstractFunctionDecl *>(this)->setBodyKind(BodyKind::None);
BraceStmt *body;
bool isTypeChecked;

auto mutableThis = const_cast<AbstractFunctionDecl *>(this);
std::tie(body, isTypeChecked) = (Synthesizer.Fn)(
mutableThis, Synthesizer.Context);
mutableThis->setBody(
body, isTypeChecked ? BodyKind::TypeChecked : BodyKind::Parsed);
return body;
}
}

auto mutableThis = const_cast<AbstractFunctionDecl *>(this);
return evaluateOrDefault(ctx.evaluator, ParseAbstractFunctionBodyRequest{mutableThis}, nullptr);
}

SourceRange AbstractFunctionDecl::getBodySourceRange() const {
Expand Down Expand Up @@ -6804,11 +6790,15 @@ bool AbstractFunctionDecl::hasInlinableBodyText() const {
switch (getBodyKind()) {
case BodyKind::Deserialized:
return true;

case BodyKind::Unparsed:
case BodyKind::Parsed:
case BodyKind::TypeChecked:
return getBody() && !getBody()->isImplicit();
if (auto body = getBody())
return !body->isImplicit();
return false;

case BodyKind::None:
case BodyKind::Unparsed:
case BodyKind::Synthesize:
case BodyKind::Skipped:
case BodyKind::MemberwiseInitializer:
Expand Down Expand Up @@ -7676,3 +7666,49 @@ SourceLoc swift::extractNearestSourceLoc(const Decl *decl) {

return extractNearestSourceLoc(decl->getDeclContext());
}

Optional<BraceStmt *>
ParseAbstractFunctionBodyRequest::getCachedResult() const {
using BodyKind = AbstractFunctionDecl::BodyKind;
auto afd = std::get<0>(getStorage());
switch (afd->getBodyKind()) {
case BodyKind::Deserialized:
case BodyKind::MemberwiseInitializer:
case BodyKind::None:
case BodyKind::Skipped:
return nullptr;

case BodyKind::TypeChecked:
case BodyKind::Parsed:
return afd->Body;

case BodyKind::Synthesize:
case BodyKind::Unparsed:
return None;
}
}

void ParseAbstractFunctionBodyRequest::cacheResult(BraceStmt *value) const {
using BodyKind = AbstractFunctionDecl::BodyKind;
auto afd = std::get<0>(getStorage());
switch (afd->getBodyKind()) {
case BodyKind::Deserialized:
case BodyKind::MemberwiseInitializer:
case BodyKind::None:
case BodyKind::Skipped:
// The body is always empty, so don't cache anything.
assert(value == nullptr);
return;

case BodyKind::Parsed:
case BodyKind::TypeChecked:
afd->Body = value;
return;

case BodyKind::Synthesize:
case BodyKind::Unparsed:
llvm_unreachable("evaluate() did not set the body kind");
return;
}

}
6 changes: 4 additions & 2 deletions lib/AST/UnqualifiedLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl(
const bool isCascadingUse =
AFD->isCascadingContextForLookup(false) &&
(isCascadingUseArg.getValueOr(
Loc.isInvalid() || !AFD->getBody() ||
Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() ||
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc)));

if (AFD->getDeclContext()->isTypeContext())
Expand Down Expand Up @@ -814,7 +814,9 @@ void UnqualifiedLookupFactory::lookForLocalVariablesIn(
// FIXME: when we can parse and typecheck the function body partially
// for code completion, AFD->getBody() check can be removed.

if (Loc.isInvalid() || !AFD->getBody()) {
if (Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() ||
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) ||
!AFD->getBody()) {
return;
}

Expand Down
25 changes: 6 additions & 19 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5390,8 +5390,6 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD,
if (DelayedParseCB &&
DelayedParseCB->shouldDelayFunctionBodyParsing(*this, AFD, Attrs,
BodyRange)) {
State->delayFunctionBodyParsing(AFD, BodyRange,
BeginParserPosition.PreviousLoc);
AFD->setBodyDelayed(BodyRange);
} else {
AFD->setBodySkipped(BodyRange);
Expand Down Expand Up @@ -5672,15 +5670,12 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) {
}
}

bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
assert(!AFD->getBody() && "function should not have a parsed body");
BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
assert(AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::Unparsed &&
"function body should be delayed");

auto FunctionParserState = State->takeFunctionBodyState(AFD);
assert(FunctionParserState.get() && "should have a valid state");

auto BeginParserPosition = getParserPosition(FunctionParserState->BodyPos);
auto bodyRange = AFD->getBodySourceRange();
auto BeginParserPosition = getParserPosition({bodyRange.Start,bodyRange.End});
auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc());

// ParserPositionRAII needs a primed parser to restore to.
Expand All @@ -5700,20 +5695,12 @@ bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
restoreParserPosition(BeginParserPosition);

// Re-enter the lexical scope.
Scope S(this, FunctionParserState->takeScope());
Scope TopLevelScope(this, ScopeKind::TopLevel);
Scope S(this, ScopeKind::FunctionBody);
ParseFunctionBody CC(*this, AFD);
setLocalDiscriminatorToParamList(AFD->getParameters());

ParserResult<BraceStmt> Body =
parseBraceItemList(diag::func_decl_without_brace);
if (Body.isNull()) {
// FIXME: Should do some sort of error recovery here?
return true;
} else {
AFD->setBodyParsed(Body.get());
}

return false;
return parseBraceItemList(diag::func_decl_without_brace).getPtrOrNull();
}

/// Parse a 'enum' declaration, returning true (and doing no token
Expand Down
44 changes: 44 additions & 0 deletions lib/Parse/ParseRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,50 @@ ParseMembersRequest::evaluate(Evaluator &evaluator,
llvm::makeArrayRef(parser.parseDeclListDelayed(idc)));
}

BraceStmt *ParseAbstractFunctionBodyRequest::evaluate(
Evaluator &evaluator, AbstractFunctionDecl *afd) const {
using BodyKind = AbstractFunctionDecl::BodyKind;

switch (afd->getBodyKind()) {
case BodyKind::Deserialized:
case BodyKind::MemberwiseInitializer:
case BodyKind::None:
case BodyKind::Skipped:
return nullptr;

case BodyKind::TypeChecked:
case BodyKind::Parsed:
return afd->Body;

case BodyKind::Synthesize: {
BraceStmt *body;
bool isTypeChecked;

std::tie(body, isTypeChecked) = (afd->Synthesizer.Fn)(
afd, afd->Synthesizer.Context);
afd->setBodyKind(isTypeChecked ? BodyKind::TypeChecked : BodyKind::Parsed);
return body;
}

case BodyKind::Unparsed: {
// FIXME: It should be fine to delay body parsing of local functions, so
// the DelayBodyParsing should go away entirely
// FIXME: How do we configure code completion?
SourceFile &sf = *afd->getDeclContext()->getParentSourceFile();
SourceManager &sourceMgr = sf.getASTContext().SourceMgr;
unsigned bufferID = sourceMgr.findBufferContainingLoc(afd->getLoc());
Parser parser(bufferID, sf, nullptr, nullptr, nullptr,
/*DelayBodyParsing=*/false);
parser.SyntaxContext->setDiscard();
auto body = parser.parseAbstractFunctionBodyDelayed(afd);
afd->setBodyKind(BodyKind::Parsed);
return body;
}
}

}


// Define request evaluation functions for each of the type checker requests.
static AbstractRequestFunction *parseRequestFunctions[] = {
#define SWIFT_REQUEST(Zone, Name) \
Expand Down
7 changes: 5 additions & 2 deletions lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ class ParseDelayedFunctionBodies : public ASTWalker {

private:
void parseFunctionBody(AbstractFunctionDecl *AFD) {
// FIXME: This duplicates the evaluation of
// ParseAbstractFunctionBodyRequest, but installs a code completion
// factory.
assert(AFD->getBodyKind() == FuncDecl::BodyKind::Unparsed);

SourceFile &SF = *AFD->getDeclContext()->getParentSourceFile();
Expand All @@ -152,8 +155,8 @@ class ParseDelayedFunctionBodies : public ASTWalker {
CodeCompletionFactory->createCodeCompletionCallbacks(TheParser));
TheParser.setCodeCompletionCallbacks(CodeCompletion.get());
}
if (ParserState.hasFunctionBodyState(AFD))
TheParser.parseAbstractFunctionBodyDelayed(AFD);
auto body = TheParser.parseAbstractFunctionBodyDelayed(AFD);
AFD->setBodyParsed(body);

if (CodeCompletion)
CodeCompletion->doneParsing();
Expand Down
Loading