Skip to content

Commit c7be2de

Browse files
author
David Ungar
authored
Merge pull request #27143 from davidungar/A-9-12-eager-off
[NFC NameLookup ASTScope] Fixes for large app, eager primary tree creation, memberCount fix.
2 parents a3063cc + 1c07cee commit c7be2de

18 files changed

+210
-62
lines changed

include/swift/AST/ASTScope.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ class ASTScopeImpl {
215215

216216
bool checkSourceRangeOfThisASTNode() const;
217217

218+
/// For debugging
219+
bool doesRangeMatch(unsigned start, unsigned end, StringRef file = "",
220+
StringRef className = "");
221+
218222
private:
219223
SourceRange computeSourceRangeOfScope(bool omitAssertions = false) const;
220224
SourceRange
@@ -264,7 +268,7 @@ class ASTScopeImpl {
264268
getSourceRangeOfEnclosedParamsOfASTNode(bool omitAssertions) const;
265269

266270
private:
267-
bool checkSourceRangeAfterExpansion() const;
271+
bool checkSourceRangeAfterExpansion(const ASTContext &) const;
268272

269273
#pragma mark common queries
270274
public:
@@ -318,9 +322,9 @@ class ASTScopeImpl {
318322
private:
319323
/// Compare the pre-expasion range with the post-expansion range and return
320324
/// false if lazyiness couild miss lookups.
321-
bool checkLazySourceRange() const;
325+
bool checkLazySourceRange(const ASTContext &) const;
322326

323-
protected:
327+
public:
324328
/// Some scopes can be expanded lazily.
325329
/// Such scopes must: not change their source ranges after expansion, and
326330
/// their expansion must return an insertion point outside themselves.
@@ -517,6 +521,7 @@ class ASTSourceFileScope final : public ASTScopeImpl {
517521
NullablePtr<DeclContext> getDeclContext() const override;
518522

519523
void addNewDeclsToScopeTree();
524+
void buildScopeTreeEagerly();
520525

521526
const SourceFile *getSourceFile() const override;
522527
NullablePtr<const void> addressForPrinting() const override { return SF; }
@@ -1002,6 +1007,10 @@ class AbstractFunctionBodyScope : public ASTScopeImpl {
10021007
DeclConsumer) const override;
10031008
Optional<bool>
10041009
resolveIsCascadingUseForThisScope(Optional<bool>) const override;
1010+
1011+
public:
1012+
NullablePtr<ASTScopeImpl> insertionPointForDeferredExpansion() override;
1013+
SourceRange sourceRangeForDeferredExpansion() const override;
10051014
};
10061015

10071016
/// Body of methods, functions in types.

include/swift/AST/ExperimentalDependencies.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ class BiIndexedTwoStageMap {
222222
bool insert(const Key1 &k1, const Key2 &k2, Value &v) {
223223
const bool r1 = map1.insert(k1, k2, v);
224224
const bool r2 = map2.insert(k2, k1, v);
225+
(void)r2;
225226
assertConsistent(r1, r2);
226227
return r1;
227228
}

include/swift/AST/Module.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,8 @@ class SourceFile final : public FileUnit {
12411241

12421242
bool canBeParsedInFull() const;
12431243

1244+
bool isSuitableForASTScopes() const { return canBeParsedInFull(); }
1245+
12441246
syntax::SourceFileSyntax getSyntaxRoot() const;
12451247
void setSyntaxRoot(syntax::SourceFileSyntax &&Root);
12461248
bool hasSyntaxRoot() const;

include/swift/AST/NameLookup.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,10 @@ class ASTScope {
592592
public:
593593
ASTScope(SourceFile *);
594594

595+
/// Cannot be lazy during type-checking because it mutates the AST.
596+
/// So build eagerly before type-checking
597+
void buildScopeTreeEagerly();
598+
595599
/// \return the scopes traversed
596600
static llvm::SmallVector<const ast_scope::ASTScopeImpl *, 0>
597601
unqualifiedLookup(SourceFile *, DeclName, SourceLoc,

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ namespace swift {
254254
/// Should we compare to ASTScope-based resolution for debugging?
255255
bool CompareToASTScopeLookup = false;
256256

257+
/// Should we stress ASTScope-based resolution for debugging?
258+
bool StressASTScopeLookup = false;
259+
257260
/// Since some tests fail if the warning is output, use a flag to decide
258261
/// whether it is. The warning is useful for testing.
259262
bool WarnIfASTScopeLookup = false;

include/swift/Driver/ExperimentalDependencyDriverGraph.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ class ModuleDepGraph {
223223
ModuleDepGraphNode *nodeActuallyErased = nodeMap.findAndErase(
224224
nodeToErase->getSwiftDeps().getValueOr(std::string()),
225225
nodeToErase->getKey());
226+
(void)nodeActuallyErased;
226227
assert(
227228
nodeToErase == nodeActuallyErased ||
228229
mapCorruption("Node found from key must be same as node holding key."));

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ def disable_target_os_checking :
124124

125125
def compare_to_astscope_lookup : Flag<["-"], "compare-to-astscope-lookup">,
126126
HelpText<"Compare legacy to ASTScope-based unqualified name lookup (for debugging)">;
127+
128+
def stress_astscope_lookup : Flag<["-"], "stress-astscope-lookup">,
129+
HelpText<"Stress ASTScope-based unqualified name lookup (for testing)">;
127130

128131
def warn_if_astscope_lookup : Flag<["-"], "warn-if-astscope-lookup">,
129132
HelpText<"Print a warning if ASTScope lookup is used">;

lib/AST/ASTScopeCreation.cpp

Lines changed: 97 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,26 @@ static void dumpRangeable(SpecializeAttr *r, llvm::raw_ostream &f) {
9393
llvm::errs() << "SpecializeAttr\n";
9494
}
9595

96+
/// For Debugging
97+
template <typename T>
98+
bool doesRangeableRangeMatch(const T *x, const SourceManager &SM,
99+
unsigned start, unsigned end,
100+
StringRef file = "") {
101+
auto const r = getRangeableSourceRange(x);
102+
if (r.isInvalid())
103+
return false;
104+
if (start && SM.getLineNumber(r.Start) != start)
105+
return false;
106+
if (end && SM.getLineNumber(r.End) != end)
107+
return false;
108+
if (file.empty())
109+
return true;
110+
const auto buf = SM.findBufferContainingLoc(r.Start);
111+
return SM.getIdentifierForBuffer(buf).endswith(file);
112+
}
113+
114+
#pragma mark end of rangeable
115+
96116
static std::vector<ASTNode> asNodeVector(DeclRange dr) {
97117
std::vector<ASTNode> nodes;
98118
llvm::transform(dr, std::back_inserter(nodes),
@@ -165,9 +185,27 @@ class ScopeCreator final {
165185
/// For allocating scopes.
166186
ASTContext &ctx;
167187

188+
public:
189+
/// Because type checking can mutate the AST, eagerly build the tree, then
190+
/// freeze it
191+
enum class Temperature {
192+
Warm, // Can be lazy
193+
Freezing, // Should expand everything eagerly
194+
Frozen // No more changes, except when Decls are added to the source file
195+
};
196+
197+
private:
198+
/// Because type checking can mutate the AST, eagerly build the tree, then
199+
/// freeze it
200+
Temperature temperature = Temperature::Warm;
201+
168202
public:
169203
ASTSourceFileScope *const sourceFileScope;
170204
ASTContext &getASTContext() const { return ctx; }
205+
bool getIsFrozen() const { return temperature == Temperature::Frozen; }
206+
bool getIsFreezing() const { return temperature == Temperature::Freezing; }
207+
void beFreezing() { temperature = Temperature::Freezing; }
208+
void beFrozen() { temperature = Temperature::Frozen; }
171209

172210
/// The AST can have duplicate nodes, and we don't want to create scopes for
173211
/// those.
@@ -366,7 +404,7 @@ class ScopeCreator final {
366404
parent, vd);
367405
});
368406
}
369-
// HERE
407+
370408
public:
371409
/// Create the matryoshka nested generic param scopes (if any)
372410
/// that are subscopes of the receiver. Return
@@ -607,7 +645,9 @@ class ScopeCreator final {
607645
return !n.isDecl(DeclKind::Var);
608646
}
609647

610-
bool shouldBeLazy() const { return ctx.LangOpts.LazyASTScopes; }
648+
bool shouldBeLazy() const {
649+
return !getIsFreezing() && ctx.LangOpts.LazyASTScopes;
650+
}
611651

612652
public:
613653
/// For debugging. Return true if scope tree contains all the decl contexts in
@@ -617,9 +657,11 @@ class ScopeCreator final {
617657
auto allDeclContexts = findLocalizableDeclContextsInAST();
618658
llvm::DenseMap<const DeclContext *, const ASTScopeImpl *> bogusDCs;
619659
bool rebuilt = false;
620-
sourceFileScope->preOrderDo([&](ASTScopeImpl *scope) {
621-
rebuilt |= scope->reexpandIfObsolete(*this);
622-
});
660+
if (!getIsFrozen()) {
661+
sourceFileScope->preOrderDo([&](ASTScopeImpl *scope) {
662+
rebuilt |= scope->reexpandIfObsolete(*this);
663+
});
664+
}
623665
sourceFileScope->postOrderDo([&](ASTScopeImpl *scope) {
624666
if (auto *dc = scope->getDeclContext().getPtrOrNull()) {
625667
auto iter = allDeclContexts.find(dc);
@@ -699,12 +741,24 @@ class ScopeCreator final {
699741

700742
ASTScope::ASTScope(SourceFile *SF) : impl(createScopeTree(SF)) {}
701743

744+
void ASTScope::buildScopeTreeEagerly() {
745+
impl->buildScopeTreeEagerly();
746+
}
747+
702748
ASTSourceFileScope *ASTScope::createScopeTree(SourceFile *SF) {
703749
ScopeCreator *scopeCreator = new (SF->getASTContext()) ScopeCreator(SF);
704750
scopeCreator->sourceFileScope->addNewDeclsToScopeTree();
705751
return scopeCreator->sourceFileScope;
706752
}
707753

754+
void ASTSourceFileScope::buildScopeTreeEagerly() {
755+
scopeCreator->beFreezing();
756+
// Eagerly expand any decls already in the tree.
757+
preOrderDo([&](ASTScopeImpl *s) { s->reexpandIfObsolete(*scopeCreator); });
758+
addNewDeclsToScopeTree();
759+
scopeCreator->beFrozen();
760+
}
761+
708762
void ASTSourceFileScope::addNewDeclsToScopeTree() {
709763
assert(SF && scopeCreator);
710764
ArrayRef<Decl *> decls = SF->Decls;
@@ -805,6 +859,7 @@ class NodeAdder
805859
VISIT_AND_CREATE_WHOLE_PORTION(ExtensionDecl, ExtensionScope)
806860
VISIT_AND_CREATE_WHOLE_PORTION(StructDecl, NominalTypeScope)
807861
VISIT_AND_CREATE_WHOLE_PORTION(ClassDecl, NominalTypeScope)
862+
VISIT_AND_CREATE_WHOLE_PORTION(ProtocolDecl, NominalTypeScope)
808863
VISIT_AND_CREATE_WHOLE_PORTION(EnumDecl, NominalTypeScope)
809864
VISIT_AND_CREATE_WHOLE_PORTION(TypeAliasDecl, TypeAliasScope)
810865
VISIT_AND_CREATE_WHOLE_PORTION(OpaqueTypeDecl, OpaqueTypeScope)
@@ -817,12 +872,6 @@ class NodeAdder
817872
return visitAbstractFunctionDecl(ad, p, scopeCreator);
818873
}
819874

820-
NullablePtr<ASTScopeImpl> visitProtocolDecl(ProtocolDecl *e, ASTScopeImpl *p,
821-
ScopeCreator &scopeCreator) {
822-
return scopeCreator.ifUniqueConstructWithPortionExpandAndInsert<
823-
NominalTypeScope, GenericTypeOrExtensionWholePortion>(p, e);
824-
}
825-
826875
#pragma mark simple creation with deferred nodes
827876

828877
// Each of the following creates a new scope, so that nodes which were parsed
@@ -1011,6 +1060,9 @@ void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) {
10111060
assert(!child->getParent() && "child should not already have parent");
10121061
child->parent = this;
10131062
clearCachedSourceRangesOfMeAndAncestors();
1063+
// It's possible that some callees do lookups back into the tree.
1064+
// So make sure childrenCountWhenLastExpanded is up to date.
1065+
setChildrenCountWhenLastExpanded();
10141066
}
10151067

10161068
void ASTScopeImpl::removeChildren() {
@@ -1036,8 +1088,7 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) {
10361088
insertionPointForDeferredExpansion().get() == insertionPoint);
10371089
}
10381090
beCurrent();
1039-
setChildrenCountWhenLastExpanded();
1040-
assert(checkSourceRangeAfterExpansion());
1091+
assert(checkSourceRangeAfterExpansion(scopeCreator.getASTContext()));
10411092
return insertionPoint;
10421093
}
10431094

@@ -1449,25 +1500,30 @@ void AttachedPropertyWrapperScope::
14491500

14501501
ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope(
14511502
GenericTypeOrExtensionScope *scope, ScopeCreator &scopeCreator) const {
1503+
// Get now in case recursion emancipates scope
1504+
auto *const ip = scope->getParent().get();
1505+
14521506
// Prevent circular request bugs caused by illegal input and
14531507
// doing lookups that getExtendedNominal in the midst of getExtendedNominal.
14541508
// rdar://53972776
14551509
if (scope->shouldHaveABody() && !scope->doesDeclHaveABody())
1456-
return scope->getParent().get();
1510+
return ip;
14571511

14581512
auto *deepestScope = scopeCreator.addNestedGenericParamScopesToTree(
14591513
scope->getDecl(), scope->getGenericContext()->getGenericParams(), scope);
14601514
if (scope->getGenericContext()->getTrailingWhereClause())
14611515
scope->createTrailingWhereClauseScope(deepestScope, scopeCreator);
14621516
scope->createBodyScope(deepestScope, scopeCreator);
1463-
return scope->getParent().get();
1517+
return ip;
14641518
}
14651519

14661520
ASTScopeImpl *
14671521
IterableTypeBodyPortion::expandScope(GenericTypeOrExtensionScope *scope,
14681522
ScopeCreator &scopeCreator) const {
1523+
// Get it now in case of recursion and this one gets emancipated
1524+
auto *const ip = scope->getParent().get();
14691525
scope->expandBody(scopeCreator);
1470-
return scope->getParent().get();
1526+
return ip;
14711527
}
14721528

14731529
ASTScopeImpl *GenericTypeOrExtensionWherePortion::expandScope(
@@ -1671,7 +1727,9 @@ void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) {
16711727
#pragma mark - reexpandIfObsolete
16721728

16731729
bool ASTScopeImpl::reexpandIfObsolete(ScopeCreator &scopeCreator) {
1674-
if (isCurrent())
1730+
if (scopeCreator.getIsFrozen() ||
1731+
(isCurrent() &&
1732+
!scopeCreator.getASTContext().LangOpts.StressASTScopeLookup))
16751733
return false;
16761734
reexpand(scopeCreator);
16771735
return true;
@@ -1732,6 +1790,11 @@ NullablePtr<ASTScopeImpl> ASTScopeImpl::insertionPointForDeferredExpansion() {
17321790
return nullptr;
17331791
}
17341792

1793+
NullablePtr<ASTScopeImpl>
1794+
AbstractFunctionBodyScope::insertionPointForDeferredExpansion() {
1795+
return getParent().get();
1796+
}
1797+
17351798
NullablePtr<ASTScopeImpl>
17361799
IterableTypeScope::insertionPointForDeferredExpansion() {
17371800
return portion->insertionPointForDeferredExpansion(this);
@@ -1783,20 +1846,29 @@ bool TopLevelCodeScope::isCurrent() const {
17831846
return bodyWhenLastExpanded == decl->getBody();
17841847
}
17851848

1849+
// Try to avoid the work of counting
1850+
static const bool assumeVarsDoNotGetAdded = true;
1851+
1852+
static unsigned countVars(const PatternBindingEntry &entry) {
1853+
unsigned varCount = 0;
1854+
entry.getPattern()->forEachVariable([&](VarDecl *) { ++varCount; });
1855+
return varCount;
1856+
}
1857+
17861858
void PatternEntryDeclScope::beCurrent() {
17871859
initWhenLastExpanded = getPatternEntry().getOriginalInit();
1788-
unsigned varCount = 0;
1789-
getPatternEntry().getPattern()->forEachVariable(
1790-
[&](VarDecl *) { ++varCount; });
1791-
varCountWhenLastExpanded = varCount;
1860+
if (assumeVarsDoNotGetAdded && varCountWhenLastExpanded)
1861+
return;
1862+
varCountWhenLastExpanded = countVars(getPatternEntry());
17921863
}
17931864
bool PatternEntryDeclScope::isCurrent() const {
17941865
if (initWhenLastExpanded != getPatternEntry().getOriginalInit())
17951866
return false;
1796-
unsigned varCount = 0;
1797-
getPatternEntry().getPattern()->forEachVariable(
1798-
[&](VarDecl *) { ++varCount; });
1799-
return varCount == varCountWhenLastExpanded;
1867+
if (assumeVarsDoNotGetAdded && varCountWhenLastExpanded) {
1868+
assert(varCountWhenLastExpanded == countVars(getPatternEntry()));
1869+
return true;
1870+
}
1871+
return countVars(getPatternEntry()) == varCountWhenLastExpanded;
18001872
}
18011873

18021874
void WholeClosureScope::beCurrent() {

lib/AST/ASTScopeLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup(
5858

5959
auto *const fileScope = sourceFile->getScope().impl;
6060
// Parser may have added decls to source file, since previous lookup
61-
sourceFile->getScope().impl->addNewDeclsToScopeTree();
61+
fileScope->addNewDeclsToScopeTree();
6262
if (name.isOperator())
6363
return fileScope; // operators always at file scope
6464

0 commit comments

Comments
 (0)