Skip to content

Commit 2838838

Browse files
committed
AST: UnqualifiedLookup only finds forward references to outer local bindings when IncludeOuterResults is set
The old behavior was that ASTScope would introduce all VarDecls defined in a BraceStmt at the beginning of the BraceStmt. I recently enabled the use of PatternEntryDeclScopes, which introduce the binding at its actual source location instead of at the beginning of the parent statement. This patch now makes use of the new information by having UnqualifiedLookupFlags::IncludeOuterResults toggle between the two behaviors. When searching for outer results, we also consider all VarDecls in a BraceStmt, not just those in scope. This is implemented by giving AbstractASTScopeDeclConsumer a new entry point, consumePossiblyNotInScope(). When looking up into a BraceStmt, all VarDecls are passed in to this entry point. The default implementation does nothing, which means that ASTScope::lookupSingleLocalDecl() now respects source locations when searching for bindings, just like parse-time lookup. However, Sema's preCheckExpression() pass, which sets Flags::IgnoreOuterResults, will continue to find forward-referenced VarDecls, just as it did with the old context-based DeclContext lookup.
1 parent 6042784 commit 2838838

File tree

5 files changed

+82
-20
lines changed

5 files changed

+82
-20
lines changed

include/swift/AST/ASTScope.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,10 +1658,22 @@ class CaseStmtBodyScope final : public ASTScopeImpl {
16581658
};
16591659

16601660
class BraceStmtScope final : public AbstractStmtScope {
1661+
BraceStmt *const stmt;
1662+
1663+
/// Declarations which are in scope from the beginning of the statement.
1664+
SmallVector<ValueDecl *, 2> localFuncsAndTypes;
1665+
1666+
/// Declarations that are normally in scope only after their
1667+
/// definition.
1668+
SmallVector<VarDecl *, 2> localVars;
16611669

16621670
public:
1663-
BraceStmt *const stmt;
1664-
BraceStmtScope(BraceStmt *e) : stmt(e) {}
1671+
BraceStmtScope(BraceStmt *e,
1672+
SmallVector<ValueDecl *, 2> localFuncsAndTypes,
1673+
SmallVector<VarDecl *, 2> localVars)
1674+
: stmt(e),
1675+
localFuncsAndTypes(localFuncsAndTypes),
1676+
localVars(localVars) {}
16651677
virtual ~BraceStmtScope() {}
16661678

16671679
protected:

include/swift/AST/NameLookup.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,14 @@ class AbstractASTScopeDeclConsumer {
613613
lookInMembers(DeclContext *const scopeDC,
614614
NominalTypeDecl *const nominal) = 0;
615615

616+
/// Called for local VarDecls that might not yet be in scope.
617+
///
618+
/// Note that the set of VarDecls visited here are going to be a
619+
/// superset of those visited in consume().
620+
virtual bool consumePossiblyNotInScope(ArrayRef<VarDecl *> values) {
621+
return false;
622+
}
623+
616624
/// Called right before looking at the parent scope of a BraceStmt.
617625
///
618626
/// \return true if the lookup should be stopped at this point.

lib/AST/ASTScopeCreation.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,10 +667,30 @@ class NodeAdder
667667

668668
NullablePtr<ASTScopeImpl> visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p,
669669
ScopeCreator &scopeCreator) {
670+
SmallVector<ValueDecl *, 2> localFuncsAndTypes;
671+
SmallVector<VarDecl *, 2> localVars;
672+
673+
// All types and functions are visible anywhere within a brace statement
674+
// scope. When ordering matters (i.e. var decl) we will have split the brace
675+
// statement into nested scopes.
676+
for (auto braceElement : bs->getElements()) {
677+
if (auto localBinding = braceElement.dyn_cast<Decl *>()) {
678+
if (auto *vd = dyn_cast<ValueDecl>(localBinding)) {
679+
if (isa<FuncDecl>(vd) || isa<TypeDecl>(vd)) {
680+
localFuncsAndTypes.push_back(vd);
681+
} else if (auto *var = dyn_cast<VarDecl>(localBinding)) {
682+
localVars.push_back(var);
683+
}
684+
}
685+
}
686+
}
687+
670688
auto maybeBraceScope =
671-
scopeCreator.ifUniqueConstructExpandAndInsert<BraceStmtScope>(p, bs);
689+
scopeCreator.ifUniqueConstructExpandAndInsert<BraceStmtScope>(
690+
p, bs, std::move(localFuncsAndTypes), std::move(localVars));
672691
if (auto *s = scopeCreator.getASTContext().Stats)
673692
++s->getFrontendCounters().NumBraceStmtASTScopes;
693+
674694
return maybeBraceScope.getPtrOr(p);
675695
}
676696

lib/AST/ASTScopeLookup.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -371,20 +371,10 @@ bool DifferentiableAttributeScope::lookupLocalsOrMembers(
371371
}
372372

373373
bool BraceStmtScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
374-
// All types and functions are visible anywhere within a brace statement
375-
// scope. When ordering matters (i.e. var decl) we will have split the brace
376-
// statement into nested scopes.
377-
//
378-
// Don't stop at the first one, there may be local funcs with same base name
379-
// and want them all.
380-
SmallVector<ValueDecl *, 32> localBindings;
381-
for (auto braceElement : stmt->getElements()) {
382-
if (auto localBinding = braceElement.dyn_cast<Decl *>()) {
383-
if (auto *vd = dyn_cast<ValueDecl>(localBinding))
384-
localBindings.push_back(vd);
385-
}
386-
}
387-
if (consumer.consume(localBindings, DeclVisibilityKind::LocalVariable))
374+
if (consumer.consume(localFuncsAndTypes, DeclVisibilityKind::LocalVariable))
375+
return true;
376+
377+
if (consumer.consumePossiblyNotInScope(localVars))
388378
return true;
389379

390380
if (consumer.finishLookupInBraceStmt(stmt))

lib/AST/UnqualifiedLookup.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,12 @@ namespace {
133133
/// Can lookup stop searching for results, assuming hasn't looked for outer
134134
/// results yet?
135135
bool isFirstResultEnough() const;
136-
136+
137+
/// Do we want precise scoping of VarDecls? If IncludeOuterResults is on,
138+
/// this is true, which allows us to resolve forward references to
139+
/// local VarDecls from inside local function and closure bodies.
140+
bool hasPreciseScopingOfVarDecls() const;
141+
137142
/// Every time lookup finishes searching a scope, call me
138143
/// to record the dividing line between results from first fruitful scope and
139144
/// the result.
@@ -209,6 +214,8 @@ class ASTScopeDeclConsumerForUnqualifiedLookup
209214
bool consume(ArrayRef<ValueDecl *> values, DeclVisibilityKind vis,
210215
NullablePtr<DeclContext> baseDC = nullptr) override;
211216

217+
bool consumePossiblyNotInScope(ArrayRef<VarDecl *> vars) override;
218+
212219
/// returns true if finished
213220
bool lookInMembers(DeclContext *const scopeDC,
214221
NominalTypeDecl *const nominal) override;
@@ -462,6 +469,10 @@ bool UnqualifiedLookupFactory::isFirstResultEnough() const {
462469
return !Results.empty() && !options.contains(Flags::IncludeOuterResults);
463470
}
464471

472+
bool UnqualifiedLookupFactory::hasPreciseScopingOfVarDecls() const {
473+
return !options.contains(Flags::IncludeOuterResults);
474+
}
475+
465476
void UnqualifiedLookupFactory::recordCompletionOfAScope() {
466477
// OK to call (NOOP) if there are more inner results and Results is empty
467478
if (IndexOfFirstOuterResult == 0)
@@ -556,12 +567,18 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume(
556567
if (factory.isOriginallyTypeLookup && !isa<TypeDecl>(value))
557568
continue;
558569

559-
// Try to resolve the base for unqualified instance member
560-
// references. This is used by lookInMembers().
561570
if (auto *var = dyn_cast<VarDecl>(value)) {
571+
// Try to resolve the base for unqualified instance member
572+
// references. This is used by lookInMembers().
562573
if (var->getName() == factory.Ctx.Id_self) {
563574
maybeUpdateSelfDC(var);
564575
}
576+
577+
// Local VarDecls with a pattern binding are visited as part of their
578+
// BraceStmt when hasPreciseScopingOfVarDecls() is off.
579+
if (var->getParentPatternBinding() &&
580+
!factory.hasPreciseScopingOfVarDecls())
581+
continue;
565582
}
566583

567584
if (!value->getName().matchesRef(factory.Name.getFullName()))
@@ -587,6 +604,21 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume(
587604
return factory.isFirstResultEnough();
588605
}
589606

607+
bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope(
608+
ArrayRef<VarDecl *> vars) {
609+
if (factory.hasPreciseScopingOfVarDecls())
610+
return false;
611+
612+
for (auto *var : vars) {
613+
if (!factory.Name.getFullName().isSimpleName(var->getName()))
614+
continue;
615+
616+
factory.Results.push_back(LookupResultEntry(var));
617+
}
618+
619+
return false;
620+
}
621+
590622
bool ASTScopeDeclGatherer::consume(ArrayRef<ValueDecl *> valuesArg,
591623
DeclVisibilityKind,
592624
NullablePtr<DeclContext>) {

0 commit comments

Comments
 (0)