Skip to content

Commit 0f9a706

Browse files
committed
Parse and record top-level "items" rather than always forcing declarations.
In the Swift grammar, the top-level of a source file is a mix of three different kinds of "items": declarations, statements, and expressions. However, the existing parser forces all of these into declarations at parse time, wrapping statements and expressions in TopLevelCodeDecls, so the primary API for getting the top-level entities in source files is based on getting declarations. Start generalizing the representation by storing ASTNode instances at the top level, rather than declaration pointers, updating many (but not all!) uses of this API. The walk over declarations is a (cached) filter to pick out all of the declarations. Existing parsed files are unaffected (the parser still creates top-level code declarations), but the new "macro expansion" source file kind skips creating top-level code declarations so we get the pure parse tree. Additionally, some generalized clients (like ASTScope lookup) will now look at the list of items, so they'll be able to walk into statements and expressions without the intervening TopLevelCodeDecl. Over time, I'd like to phase out `getTopLevelDecls()` entirely, relying on the new `getTopLevelItems()` for parsed content. We can introduce TopLevelCodeDecls more lazily for semantic walks.
1 parent 0cb2746 commit 0f9a706

File tree

16 files changed

+159
-70
lines changed

16 files changed

+159
-70
lines changed

docs/Generics/generics.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ \subsection*{Module System}
13511351
\apiref{SourceFile}{class}
13521352
Represents a parsed source file from disk. Inherits from \texttt{FileUnit}.
13531353
\begin{itemize}
1354-
\item \texttt{getTopLevelDecls()} returns an array of all top-level declarations in this source file.
1354+
\item \texttt{getTopLevelItems()} returns an array of all top-level items in this source file.
13551355
\item \texttt{isPrimary()} returns \texttt{true} if this is a primary file, \texttt{false} if this is a secondary file.
13561356
\item \texttt{isScriptMode()} answers if this is the main file of a module.
13571357
\item \texttt{getScope()} returns the root of the scope tree for unqualified lookup.

include/swift/AST/ParseRequests.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
namespace swift {
2727

28+
struct ASTNode;
29+
2830
/// Report that a request of the given kind is being evaluated, so it
2931
/// can be recorded by the stats reporter.
3032
template<typename Request>
@@ -85,13 +87,13 @@ class ParseAbstractFunctionBodyRequest
8587
};
8688

8789
struct SourceFileParsingResult {
88-
ArrayRef<Decl *> TopLevelDecls;
90+
ArrayRef<ASTNode> TopLevelItems;
8991
Optional<ArrayRef<Token>> CollectedTokens;
9092
Optional<StableHasher> InterfaceHasher;
9193
Optional<syntax::SourceFileSyntax> SyntaxRoot;
9294
};
9395

94-
/// Parse the top-level decls of a SourceFile.
96+
/// Parse the top-level items of a SourceFile.
9597
class ParseSourceFileRequest
9698
: public SimpleRequest<
9799
ParseSourceFileRequest, SourceFileParsingResult(SourceFile *),
@@ -116,6 +118,25 @@ class ParseSourceFileRequest
116118
readDependencySource(const evaluator::DependencyRecorder &) const;
117119
};
118120

121+
/// Parse the top-level items of a SourceFile.
122+
class ParseTopLevelDeclsRequest
123+
: public SimpleRequest<
124+
ParseTopLevelDeclsRequest, ArrayRef<Decl *>(SourceFile *),
125+
RequestFlags::Cached> {
126+
public:
127+
using SimpleRequest::SimpleRequest;
128+
129+
private:
130+
friend SimpleRequest;
131+
132+
// Evaluation.
133+
ArrayRef<Decl *> evaluate(Evaluator &evaluator, SourceFile *SF) const;
134+
135+
public:
136+
// Caching.
137+
bool isCached() const { return true; }
138+
};
139+
119140
void simple_display(llvm::raw_ostream &out,
120141
const CodeCompletionCallbacksFactory *factory);
121142

include/swift/AST/ParseTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ SWIFT_REQUEST(Parse, ParseAbstractFunctionBodyRequest,
2525
SWIFT_REQUEST(Parse, ParseSourceFileRequest,
2626
SourceFileParsingResult(SourceFile *), SeparatelyCached,
2727
NoLocationInfo)
28+
SWIFT_REQUEST(Parse, ParseTopLevelDeclsRequest,
29+
ArrayRef<Decl *>(SourceFile *), Cached,
30+
NoLocationInfo)

include/swift/AST/SourceFile.h

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/Hashing.h"
2222
#include "llvm/ADT/SetVector.h"
2323
#include "llvm/ADT/SmallPtrSet.h"
24+
#include "llvm/ADT/STLExtras.h"
2425

2526
namespace swift {
2627

@@ -152,11 +153,11 @@ class SourceFile final : public FileUnit {
152153
/// been validated.
153154
llvm::SetVector<ValueDecl *> UnvalidatedDeclsWithOpaqueReturnTypes;
154155

155-
/// The list of top-level declarations in the source file. This is \c None if
156+
/// The list of top-level items in the source file. This is \c None if
156157
/// they have not yet been parsed.
157158
/// FIXME: Once addTopLevelDecl/prependTopLevelDecl
158159
/// have been removed, this can become an optional ArrayRef.
159-
Optional<std::vector<Decl *>> Decls;
160+
Optional<std::vector<ASTNode>> Items;
160161

161162
/// The list of hoisted declarations. See Decl::isHoisted().
162163
/// This is only used by lldb.
@@ -207,40 +208,37 @@ class SourceFile final : public FileUnit {
207208

208209
/// Appends the given declaration to the end of the top-level decls list. Do
209210
/// not add any additional uses of this function.
210-
void addTopLevelDecl(Decl *d) {
211-
// Force decl parsing if we haven't already.
212-
(void)getTopLevelDecls();
213-
Decls->push_back(d);
214-
}
211+
void addTopLevelDecl(Decl *d);
215212

216213
/// Prepends a declaration to the top-level decls list.
217214
///
218215
/// FIXME: This entrypoint exists to support LLDB. Calls to this function are
219216
/// always a mistake, and additional uses should not be added.
220217
///
221218
/// See rdar://58355191
222-
void prependTopLevelDecl(Decl *d) {
223-
// Force decl parsing if we haven't already.
224-
(void)getTopLevelDecls();
225-
Decls->insert(Decls->begin(), d);
226-
}
219+
void prependTopLevelDecl(Decl *d);
227220

228221
/// Add a hoisted declaration. See Decl::isHoisted().
229222
void addHoistedDecl(Decl *d);
230223

224+
/// Retrieves an immutable view of the list of top-level items in this file.
225+
ArrayRef<ASTNode> getTopLevelItems() const;
226+
231227
/// Retrieves an immutable view of the list of top-level decls in this file.
228+
///
229+
/// NOTE: Please use getTopLevelItems() instead.
232230
ArrayRef<Decl *> getTopLevelDecls() const;
233231

234232
/// Retrieves an immutable view of the list of hoisted decls in this file.
235233
/// See Decl::isHoisted().
236234
ArrayRef<Decl *> getHoistedDecls() const;
237235

238-
/// Retrieves an immutable view of the top-level decls if they have already
236+
/// Retrieves an immutable view of the top-level items if they have already
239237
/// been parsed, or \c None if they haven't. Should only be used for dumping.
240-
Optional<ArrayRef<Decl *>> getCachedTopLevelDecls() const {
241-
if (!Decls)
238+
Optional<ArrayRef<ASTNode>> getCachedTopLevelItems() const {
239+
if (!Items)
242240
return None;
243-
return llvm::makeArrayRef(*Decls);
241+
return llvm::makeArrayRef(*Items);
244242
}
245243

246244
/// Retrieve the parsing options for the file.
@@ -522,7 +520,7 @@ class SourceFile final : public FileUnit {
522520
// FIXME: Ideally the parser state should be an output of
523521
// ParseSourceFileRequest, but the evaluator doesn't currently support
524522
// move-only outputs for cached requests.
525-
(void)getTopLevelDecls();
523+
(void)getTopLevelItems();
526524

527525
auto *state = DelayedParserState.get();
528526
assert(state && "Didn't set any delayed parser state!");

include/swift/Parse/Parser.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ namespace swift {
7979
InactiveConditionalBlock,
8080
/// The body of the active clause of an #if/#else/#endif block
8181
ActiveConditionalBlock,
82+
/// The top-level of a macro expansion "file".
83+
MacroExpansion,
8284
};
8385

8486
/// The receiver will be fed with consumed tokens while parsing. The main purpose
@@ -969,8 +971,10 @@ class Parser {
969971
/// Returns true if the parser is at the start of a SIL decl.
970972
bool isStartOfSILDecl();
971973

972-
/// Parse the top-level Swift decls into the provided vector.
973-
void parseTopLevel(SmallVectorImpl<Decl *> &decls);
974+
/// Parse the top-level Swift items into the provided vector.
975+
///
976+
/// Each item will be a declaration, statement, or expression.
977+
void parseTopLevelItems(SmallVectorImpl<ASTNode> &items);
974978

975979
/// Parse the top-level SIL decls into the SIL module.
976980
/// \returns \c true if there was a parsing error.

lib/AST/ASTDumper.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -804,13 +804,21 @@ namespace {
804804
PrintWithColorRAII(OS, ASTNodeColor) << "source_file ";
805805
PrintWithColorRAII(OS, LocationColor) << '\"' << SF.getFilename() << '\"';
806806

807-
if (auto decls = SF.getCachedTopLevelDecls()) {
808-
for (Decl *D : *decls) {
809-
if (D->isImplicit())
807+
if (auto items = SF.getCachedTopLevelItems()) {
808+
for (auto item : *items) {
809+
if (item.isImplicit())
810810
continue;
811811

812812
OS << '\n';
813-
printRec(D);
813+
814+
if (auto decl = item.dyn_cast<Decl *>()) {
815+
printRec(decl);
816+
} else if (auto stmt = item.dyn_cast<Stmt *>()) {
817+
stmt->dump(OS, &SF.getASTContext(), Indent + 2);
818+
} else {
819+
auto expr = item.get<Expr *>();
820+
expr->dump(OS, Indent + 2);
821+
}
814822
}
815823
}
816824
PrintWithColorRAII(OS, ParenthesisColor) << ')';
@@ -1470,7 +1478,7 @@ void SourceFile::dump(llvm::raw_ostream &OS, bool parseIfNeeded) const {
14701478
// parsing request as by default the dumping logic tries not to kick any
14711479
// requests.
14721480
if (parseIfNeeded)
1473-
(void)getTopLevelDecls();
1481+
(void)getTopLevelItems();
14741482

14751483
PrintDecl(OS).visitSourceFile(*this);
14761484
llvm::errs() << '\n';

lib/AST/ASTScopeCreation.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,9 @@ ASTSourceFileScope::expandAScopeThatCreatesANewInsertionPoint(
692692
SourceLoc endLoc = getSourceRangeOfThisASTNode().End;
693693

694694
ASTScopeImpl *insertionPoint = this;
695-
for (auto *d : SF->getTopLevelDecls()) {
695+
for (auto node : SF->getTopLevelItems()) {
696696
insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint(
697-
ASTNode(d), insertionPoint, endLoc);
697+
node, insertionPoint, endLoc);
698698
}
699699

700700
return {insertionPoint, "Next time decls are added they go here."};

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode(
181181
return SourceRange(charRange.getStart(), charRange.getEnd());
182182
}
183183

184-
if (SF->getTopLevelDecls().empty())
184+
if (SF->getTopLevelItems().empty())
185185
return SourceRange();
186186

187187
// Use the source ranges of the declarations in the file.
188-
return SourceRange(SF->getTopLevelDecls().front()->getStartLoc(),
189-
SF->getTopLevelDecls().back()->getEndLoc());
188+
return SourceRange(SF->getTopLevelItems().front().getStartLoc(),
189+
SF->getTopLevelItems().back().getEndLoc());
190190
}
191191

192192
SourceRange GenericTypeOrExtensionScope::getSourceRangeOfThisASTNode(

lib/AST/Module.cpp

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3274,10 +3274,39 @@ void SourceFile::addHoistedDecl(Decl *d) {
32743274
}
32753275

32763276
ArrayRef<Decl *> SourceFile::getTopLevelDecls() const {
3277+
auto &ctx = getASTContext();
3278+
auto *mutableThis = const_cast<SourceFile *>(this);
3279+
return evaluateOrDefault(
3280+
ctx.evaluator, ParseTopLevelDeclsRequest{mutableThis}, {});
3281+
}
3282+
3283+
void SourceFile::addTopLevelDecl(Decl *d) {
3284+
// Force decl parsing if we haven't already.
3285+
(void)getTopLevelItems();
3286+
Items->push_back(d);
3287+
3288+
// FIXME: This violates core properties of the evaluator.
3289+
auto &ctx = getASTContext();
3290+
auto *mutableThis = const_cast<SourceFile *>(this);
3291+
ctx.evaluator.clearCachedOutput(ParseTopLevelDeclsRequest{mutableThis});
3292+
}
3293+
3294+
void SourceFile::prependTopLevelDecl(Decl *d) {
3295+
// Force decl parsing if we haven't already.
3296+
(void)getTopLevelItems();
3297+
Items->insert(Items->begin(), d);
3298+
3299+
// FIXME: This violates core properties of the evaluator.
3300+
auto &ctx = getASTContext();
3301+
auto *mutableThis = const_cast<SourceFile *>(this);
3302+
ctx.evaluator.clearCachedOutput(ParseTopLevelDeclsRequest{mutableThis});
3303+
}
3304+
3305+
ArrayRef<ASTNode> SourceFile::getTopLevelItems() const {
32773306
auto &ctx = getASTContext();
32783307
auto *mutableThis = const_cast<SourceFile *>(this);
32793308
return evaluateOrDefault(ctx.evaluator, ParseSourceFileRequest{mutableThis},
3280-
{}).TopLevelDecls;
3309+
{}).TopLevelItems;
32813310
}
32823311

32833312
ArrayRef<Decl *> SourceFile::getHoistedDecls() const {
@@ -3335,20 +3364,21 @@ bool FileUnit::walk(ASTWalker &walker) {
33353364
bool SourceFile::walk(ASTWalker &walker) {
33363365
llvm::SaveAndRestore<ASTWalker::ParentTy> SAR(walker.Parent,
33373366
getParentModule());
3338-
for (Decl *D : getTopLevelDecls()) {
3339-
#ifndef NDEBUG
3340-
PrettyStackTraceDecl debugStack("walking into decl", D);
3341-
#endif
3342-
3343-
if (D->walk(walker))
3344-
return true;
3367+
for (auto Item : getTopLevelItems()) {
3368+
if (auto D = Item.dyn_cast<Decl *>()) {
3369+
if (D->walk(walker))
3370+
return true;
3371+
} else {
3372+
Item.walk(walker);
3373+
}
33453374

33463375
if (walker.shouldWalkAccessorsTheOldWay()) {
33473376
// Pretend that accessors share a parent with the storage.
33483377
//
33493378
// FIXME: Update existing ASTWalkers to deal with accessors appearing as
33503379
// children of the storage instead.
3351-
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
3380+
if (auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(
3381+
Item.dyn_cast<Decl *>())) {
33523382
for (auto AD : ASD->getAllAccessors()) {
33533383
if (AD->walk(walker))
33543384
return true;

lib/Parse/ParseDecl.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ extern "C" void swift_ASTGen_buildTopLevelASTNodes(void *sourceFile,
197197
/// decl-sil [[only in SIL mode]
198198
/// decl-sil-stage [[only in SIL mode]
199199
/// \endverbatim
200-
void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
200+
void Parser::parseTopLevelItems(SmallVectorImpl<ASTNode> &items) {
201201
#if SWIFT_SWIFT_PARSER
202202
if ((Context.LangOpts.hasFeature(Feature::Macros) ||
203203
Context.LangOpts.hasFeature(Feature::BuiltinMacros) ||
@@ -220,7 +220,7 @@ void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
220220
// If we want to do ASTGen, do so now.
221221
if (Context.LangOpts.hasFeature(Feature::ParserASTGen)) {
222222
swift_ASTGen_buildTopLevelASTNodes(
223-
exportedSourceFile, CurDeclContext, &Context, &decls, appendToVector);
223+
exportedSourceFile, CurDeclContext, &Context, &items, appendToVector);
224224

225225
// Spin the C++ parser to the end; we won't be using it.
226226
while (!Tok.is(tok::eof)) {
@@ -237,7 +237,6 @@ void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
237237
consumeTokenWithoutFeedingReceiver();
238238

239239
// Parse the body of the file.
240-
SmallVector<ASTNode, 128> items;
241240
while (!Tok.is(tok::eof)) {
242241
// If we run into a SIL decl, skip over until the next Swift decl. We need
243242
// to delay parsing these, as SIL parsing currently requires type checking
@@ -248,9 +247,25 @@ void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
248247
continue;
249248
}
250249

251-
parseBraceItems(items, allowTopLevelCode()
252-
? BraceItemListKind::TopLevelCode
253-
: BraceItemListKind::TopLevelLibrary);
250+
// Figure out how to parse the items in this source file.
251+
BraceItemListKind braceItemListKind;
252+
switch (SF.Kind) {
253+
case SourceFileKind::Main:
254+
braceItemListKind = BraceItemListKind::TopLevelCode;
255+
break;
256+
257+
case SourceFileKind::Library:
258+
case SourceFileKind::Interface:
259+
case SourceFileKind::SIL:
260+
braceItemListKind = BraceItemListKind::TopLevelLibrary;
261+
break;
262+
263+
case SourceFileKind::MacroExpansion:
264+
braceItemListKind = BraceItemListKind::MacroExpansion;
265+
break;
266+
}
267+
268+
parseBraceItems(items, braceItemListKind);
254269

255270
// In the case of a catastrophic parse error, consume any trailing
256271
// #else, #elseif, or #endif and move on to the next statement or
@@ -267,13 +282,6 @@ void Parser::parseTopLevel(SmallVectorImpl<Decl *> &decls) {
267282
}
268283
}
269284

270-
// Then append the top-level decls we parsed.
271-
for (auto item : items) {
272-
auto *decl = item.get<Decl *>();
273-
assert(!isa<AccessorDecl>(decl) && "accessors should not be added here");
274-
decls.push_back(decl);
275-
}
276-
277285
// Finalize the syntax context.
278286
SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia);
279287
}

0 commit comments

Comments
 (0)