Skip to content

Commit e4cd209

Browse files
committed
ASTScope: Local pattern bindings are now introduced by a new scope
Repurpose PatternEntryDeclScope for local bindings. The initializer expression is now outside of this scope, which begins immediately after the initializer (or the pattern, if there is no initializer) ends. The scope becomes a new insertion point for the parent BraceStmt, since local pattern bindings become visible until the end of the enclosing lexical scope.
1 parent 5c877c5 commit e4cd209

File tree

5 files changed

+111
-100
lines changed

5 files changed

+111
-100
lines changed

include/swift/AST/ASTScope.h

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,6 @@ class ASTScopeImpl {
344344
virtual NullablePtr<ASTScopeImpl> insertionPointForDeferredExpansion();
345345
virtual SourceRange sourceRangeForDeferredExpansion() const;
346346

347-
public:
348-
bool isATypeDeclScope() const;
349-
350347
private:
351348
virtual ScopeCreator &getScopeCreator();
352349

@@ -1018,31 +1015,25 @@ class AttachedPropertyWrapperScope final : public ASTScopeImpl {
10181015
/// PatternBindingDecl's (PBDs) are tricky (See the comment for \c
10191016
/// PatternBindingDecl):
10201017
///
1021-
/// A PBD contains a list of "patterns", e.g.
1022-
/// var (a, b) = foo(), (c,d) = bar() which has two patterns.
1018+
/// A PBD contains a list of "pattern binding entries", eg
1019+
/// var (a, b) = foo(), (c,d) = bar() has two pattern bindings.
10231020
///
1024-
/// For each pattern, there will be potentially three scopes:
1025-
/// always one for the declarations, maybe one for the initializers, and maybe
1026-
/// one for users of that pattern.
1021+
/// A pattern binding entry consists of a "pattern" which binds zero or more
1022+
/// names, together with an optional initializer expression.
10271023
///
1028-
/// If a PBD occurs in code, its initializer can access all prior declarations.
1029-
/// Thus, a new scope must be created, nested in the scope of the PBD.
1030-
/// In contrast, if a PBD occurs in a type declaration body, its initializer
1031-
/// cannot access prior declarations in that body.
1024+
/// The initializer expression gets its own scope. This scope can introduce
1025+
/// a 'self' binding. Currently this is only used by initializer expressions
1026+
/// of 'lazy' properties, which can access the containing 'self' binding.
10321027
///
1033-
/// As a further complication, we get VarDecls and their accessors in deferred
1034-
/// which really must go into one of the PBD scopes. So we discard them in
1035-
/// createIfNeeded, and special-case their creation in
1036-
/// addVarDeclScopesAndTheirAccessors.
1037-
1028+
/// Pattern binding entries in local context also define a scope to
1029+
/// introduce the bindings. This scope begins immediately after the
1030+
/// initializer expression (or parent) ends.
10381031
class AbstractPatternEntryScope : public ASTScopeImpl {
10391032
public:
10401033
PatternBindingDecl *const decl;
10411034
const unsigned patternEntryIndex;
1042-
const DeclVisibilityKind vis;
10431035

1044-
AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex,
1045-
DeclVisibilityKind);
1036+
AbstractPatternEntryScope(PatternBindingDecl *, unsigned entryIndex);
10461037
virtual ~AbstractPatternEntryScope() {}
10471038

10481039
const PatternBindingEntry &getPatternEntry() const;
@@ -1057,37 +1048,43 @@ class AbstractPatternEntryScope : public ASTScopeImpl {
10571048
};
10581049

10591050
class PatternEntryDeclScope final : public AbstractPatternEntryScope {
1051+
/// The first source location where this pattern binding entry's
1052+
/// bindings become visible.
1053+
SourceLoc loc;
1054+
10601055
public:
10611056
PatternEntryDeclScope(PatternBindingDecl *pbDecl, unsigned entryIndex,
1062-
DeclVisibilityKind vis)
1063-
: AbstractPatternEntryScope(pbDecl, entryIndex, vis) {}
1057+
SourceLoc loc)
1058+
: AbstractPatternEntryScope(pbDecl, entryIndex), loc(loc) {}
10641059
virtual ~PatternEntryDeclScope() {}
10651060

10661061
protected:
10671062
ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override;
10681063

1069-
private:
1070-
AnnotatedInsertionPoint
1071-
expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &);
1072-
10731064
public:
10741065
std::string getClassName() const override;
10751066
SourceRange
10761067
getSourceRangeOfThisASTNode(bool omitAssertions = false) const override;
10771068

10781069
NullablePtr<const void> getReferrent() const override;
10791070

1071+
static SourceLoc
1072+
getStartLocForBinding(SourceManager &SM,
1073+
ASTScopeImpl *parent,
1074+
PatternBindingDecl *decl,
1075+
unsigned entryIndex);
1076+
10801077
protected:
10811078
bool lookupLocalsOrMembers(DeclConsumer) const override;
1079+
bool isLabeledStmtLookupTerminator() const override;
10821080
};
10831081

10841082
class PatternEntryInitializerScope final : public AbstractPatternEntryScope {
10851083
Expr *initAsWrittenWhenCreated;
10861084

10871085
public:
1088-
PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex,
1089-
DeclVisibilityKind vis)
1090-
: AbstractPatternEntryScope(pbDecl, entryIndex, vis),
1086+
PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex)
1087+
: AbstractPatternEntryScope(pbDecl, entryIndex),
10911088
initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {}
10921089
virtual ~PatternEntryInitializerScope() {}
10931090

lib/AST/ASTScopeCreation.cpp

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@
3838
using namespace swift;
3939
using namespace ast_scope;
4040

41-
/// If true, nest scopes so a variable is out of scope before its declaration
42-
/// Does not handle capture rules for local functions properly.
43-
/// If false don't push uses down into subscopes after decls.
44-
static const bool handleUseBeforeDef = false;
45-
4641
#pragma mark source range utilities
4742
static bool rangeableIsIgnored(const Decl *d) { return d->isImplicit(); }
4843
static bool rangeableIsIgnored(const Expr *d) {
@@ -215,6 +210,10 @@ class ScopeCreator final {
215210
ScopeCreator(const ScopeCreator &) = delete; // ensure no copies
216211
ScopeCreator(const ScopeCreator &&) = delete; // ensure no moves
217212

213+
SourceManager &getSourceManager() const {
214+
return ctx.SourceMgr;
215+
}
216+
218217
/// Given an array of ASTNodes or Decl pointers, add them
219218
/// Return the resultant insertionPoint
220219
ASTScopeImpl *
@@ -283,7 +282,7 @@ class ScopeCreator final {
283282
/// \endcode
284283
/// I'm seeing a dumped AST include:
285284
/// (pattern_binding_decl range=[test.swift:13:8 - line:12:29]
286-
const auto &SM = d->getASTContext().SourceMgr;
285+
const auto &SM = getSourceManager();
287286

288287
// Once we allow invalid PatternBindingDecls (see
289288
// isWorthTryingToCreateScopeFor), then
@@ -746,22 +745,62 @@ class NodeAdder
746745
if (auto *var = patternBinding->getSingleVar())
747746
scopeCreator.addChildrenForKnownAttributes(var, parentScope);
748747

749-
const bool isInTypeDecl = parentScope->isATypeDeclScope();
748+
const bool isLocalBinding = patternBinding->getDeclContext()->isLocalContext();
750749

751-
const DeclVisibilityKind vis =
752-
isInTypeDecl ? DeclVisibilityKind::MemberOfCurrentNominal
753-
: DeclVisibilityKind::LocalVariable;
754750
auto *insertionPoint = parentScope;
755751
for (auto i : range(patternBinding->getNumPatternEntries())) {
756-
insertionPoint =
752+
// Create a child for the initializer, if present.
753+
// Cannot trust the source range given in the ASTScopeImpl for the end of the
754+
// initializer (because of InterpolatedLiteralStrings and EditorPlaceHolders),
755+
// so compute it ourselves.
756+
// Even if this predicate fails, there may be an initContext but
757+
// we cannot make a scope for it, since no source range.
758+
if (auto *expr = patternBinding->getOriginalInit(i)) {
759+
if (isLocalizable(expr)) {
760+
ASTScopeAssert(
761+
!scopeCreator.getSourceManager().isBeforeInBuffer(
762+
expr->getStartLoc(), patternBinding->getStartLoc()),
763+
"Original inits are always after the '='");
757764
scopeCreator
758-
.ifUniqueConstructExpandAndInsert<PatternEntryDeclScope>(
759-
insertionPoint, patternBinding, i, vis)
760-
.getPtrOr(insertionPoint);
765+
.constructExpandAndInsertUncheckable<PatternEntryInitializerScope>(
766+
insertionPoint, patternBinding, i);
767+
}
768+
}
769+
770+
// Add accessors for the variables in this pattern.
771+
patternBinding->getPattern(i)->forEachVariable([&](VarDecl *var) {
772+
scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(
773+
var, insertionPoint);
774+
});
775+
776+
// For local bindings, introduce the scope where the names become
777+
// visible.
778+
if(isLocalBinding) {
779+
// Don't create the scope if the start is past the end of the buffer.
780+
//
781+
// This can happen with invalid code missing an end delimiter, eg
782+
//
783+
// func foo() { var x = 123<EOF>
784+
//
785+
// The PatternEntryDeclScope for 'x' should begin immediately after
786+
// the 123, but that is not a valid location, so we drop the scope
787+
// since we're not going to be able to see it anyway.
788+
auto loc = PatternEntryDeclScope::getStartLocForBinding(
789+
scopeCreator.getSourceManager(),
790+
insertionPoint, patternBinding, i);
791+
if (loc.isValid()) {
792+
insertionPoint =
793+
scopeCreator
794+
.ifUniqueConstructExpandAndInsert<PatternEntryDeclScope>(
795+
insertionPoint, patternBinding, i, loc)
796+
.getPtrOr(insertionPoint);
797+
}
798+
}
761799
}
800+
762801
// If in a type decl, the type search will find these,
763802
// but if in a brace stmt, must continue under the last binding.
764-
return isInTypeDecl ? parentScope : insertionPoint;
803+
return isLocalBinding ? insertionPoint : parentScope;
765804
}
766805

767806
NullablePtr<ASTScopeImpl> visitEnumElementDecl(EnumElementDecl *eed,
@@ -947,7 +986,6 @@ ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) {
947986
CREATES_NEW_INSERTION_POINT(ASTSourceFileScope)
948987
CREATES_NEW_INSERTION_POINT(ConditionalClauseScope)
949988
CREATES_NEW_INSERTION_POINT(GuardStmtScope)
950-
CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope)
951989
CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope)
952990
CREATES_NEW_INSERTION_POINT(BraceStmtScope)
953991
CREATES_NEW_INSERTION_POINT(TopLevelCodeScope)
@@ -976,6 +1014,7 @@ NO_NEW_INSERTION_POINT(SwitchStmtScope)
9761014
NO_NEW_INSERTION_POINT(WhileStmtScope)
9771015

9781016
NO_EXPANSION(GenericParamScope)
1017+
NO_EXPANSION(PatternEntryDeclScope)
9791018
NO_EXPANSION(SpecializeAttributeScope)
9801019
NO_EXPANSION(DifferentiableAttributeScope)
9811020
NO_EXPANSION(ConditionalClausePatternUseScope)
@@ -1013,41 +1052,6 @@ ParameterListScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
10131052
}
10141053
}
10151054

1016-
AnnotatedInsertionPoint
1017-
PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
1018-
ScopeCreator &scopeCreator) {
1019-
// Initializers come before VarDecls, e.g. PCMacro/didSet.swift 19
1020-
auto patternEntry = getPatternEntry();
1021-
1022-
// Create a child for the initializer, if present.
1023-
// Cannot trust the source range given in the ASTScopeImpl for the end of the
1024-
// initializer (because of InterpolatedLiteralStrings and EditorPlaceHolders),
1025-
// so compute it ourselves.
1026-
// Even if this predicate fails, there may be an initContext but
1027-
// we cannot make a scope for it, since no source range.
1028-
if (patternEntry.getOriginalInit() &&
1029-
isLocalizable(patternEntry.getOriginalInit())) {
1030-
ASTScopeAssert(
1031-
!getSourceManager().isBeforeInBuffer(
1032-
patternEntry.getOriginalInit()->getStartLoc(), decl->getStartLoc()),
1033-
"Original inits are always after the '='");
1034-
scopeCreator
1035-
.constructExpandAndInsertUncheckable<PatternEntryInitializerScope>(
1036-
this, decl, patternEntryIndex, vis);
1037-
}
1038-
1039-
// Add accessors for the variables in this pattern.
1040-
patternEntry.getPattern()->forEachVariable([&](VarDecl *var) {
1041-
scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(var, this);
1042-
});
1043-
1044-
ASTScopeAssert(!handleUseBeforeDef,
1045-
"next line is wrong otherwise; would need a use scope");
1046-
1047-
return {getParent().get(), "When not handling use-before-def, succeeding "
1048-
"code just goes in the same scope as this one"};
1049-
}
1050-
10511055
void
10521056
PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
10531057
ScopeCreator &scopeCreator) {
@@ -1417,18 +1421,12 @@ ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes(
14171421
}
14181422

14191423
AbstractPatternEntryScope::AbstractPatternEntryScope(
1420-
PatternBindingDecl *declBeingScoped, unsigned entryIndex,
1421-
DeclVisibilityKind vis)
1422-
: decl(declBeingScoped), patternEntryIndex(entryIndex), vis(vis) {
1424+
PatternBindingDecl *declBeingScoped, unsigned entryIndex)
1425+
: decl(declBeingScoped), patternEntryIndex(entryIndex) {
14231426
ASTScopeAssert(entryIndex < declBeingScoped->getPatternList().size(),
14241427
"out of bounds");
14251428
}
14261429

1427-
bool ASTScopeImpl::isATypeDeclScope() const {
1428-
Decl *const pd = getDeclIfAny().getPtrOrNull();
1429-
return pd && (isa<NominalTypeDecl>(pd) || isa<ExtensionDecl>(pd));
1430-
}
1431-
14321430
#pragma mark new operators
14331431
void *ASTScopeImpl::operator new(size_t bytes, const ASTContext &ctx,
14341432
unsigned alignment) {
@@ -1481,8 +1479,6 @@ ScopeCreator &ASTSourceFileScope::getScopeCreator() { return *scopeCreator; }
14811479
}
14821480

14831481
GET_REFERRENT(AbstractFunctionDeclScope, getDecl())
1484-
// If the PatternBindingDecl is a dup, detect it for the first
1485-
// PatternEntryDeclScope; the others are subscopes.
14861482
GET_REFERRENT(PatternEntryDeclScope, getPattern())
14871483
GET_REFERRENT(TopLevelCodeScope, getDecl())
14881484
GET_REFERRENT(SubscriptDeclScope, getDecl())

lib/AST/ASTScopeLookup.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,10 @@ bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
279279
}
280280

281281
bool PatternEntryDeclScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
282-
if (vis != DeclVisibilityKind::LocalVariable)
283-
return false; // look in self type will find this later
284-
return lookupLocalBindingsInPattern(getPattern(), vis, consumer);
282+
ASTScopeAssert(decl->getDeclContext()->isLocalContext(),
283+
"Only local bindings need their own scope");
284+
return lookupLocalBindingsInPattern(getPattern(), DeclVisibilityKind::LocalVariable,
285+
consumer);
285286
}
286287

287288
bool ForEachPatternScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
@@ -510,6 +511,10 @@ bool CaseStmtBodyScope::isLabeledStmtLookupTerminator() const {
510511
return false;
511512
}
512513

514+
bool PatternEntryDeclScope::isLabeledStmtLookupTerminator() const {
515+
return false;
516+
}
517+
513518
llvm::SmallVector<LabeledStmt *, 4>
514519
ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) {
515520
// Find the innermost scope from which to start our search.

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,23 @@ SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode(
247247

248248
SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode(
249249
const bool omitAssertions) const {
250-
return getPatternEntry().getSourceRange();
250+
return loc;
251+
}
252+
253+
SourceLoc
254+
PatternEntryDeclScope::getStartLocForBinding(SourceManager &SM,
255+
ASTScopeImpl *parent,
256+
PatternBindingDecl *decl,
257+
unsigned entryIndex) {
258+
// FIXME: This feels more complicated than it should be.
259+
auto end = decl->getPatternList()[entryIndex].getSourceRange().End;
260+
auto next = Lexer::getLocForEndOfToken(SM, end);
261+
262+
auto endOfParent = parent->getSourceRangeOfThisASTNode(true).End;
263+
if (SM.isBeforeInBuffer(endOfParent, next))
264+
return SourceLoc();
265+
266+
return next;
251267
}
252268

253269
SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode(

test/NameLookup/scope_map_top_level.swift

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ var i: Int = b.my_identity()
3131
// CHECK-EXPANDED-NEXT: `-NominalTypeBodyScope {{.*}}, [4:11 - 4:13]
3232
// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [6:1 - 21:28]
3333
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [6:1 - 21:28]
34-
// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [6:5 - 6:15] entry 0 'a'
35-
// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a'
34+
// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [6:15 - 6:15] entry 0 'a'
3635
// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [8:1 - 21:28]
3736
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [8:1 - 21:28]
3837
// CHECK-EXPANDED-NEXT: `-GuardStmtScope {{.*}}, [8:1 - 21:28]
@@ -46,8 +45,7 @@ var i: Int = b.my_identity()
4645
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [11:18 - 11:19]
4746
// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [13:1 - 21:28]
4847
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [13:1 - 21:28]
49-
// CHECK-EXPANDED-NEXT: |-PatternEntryDeclScope {{.*}}, [13:5 - 13:9] entry 0 'c'
50-
// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c'
48+
// CHECK-EXPANDED-NEXT: |-PatternEntryInitializerScope {{.*}}, [13:9 - 13:9] entry 0 'c'
5149
// CHECK-EXPANDED-NEXT: |-TypeAliasDeclScope {{.*}}, [15:1 - 15:15]
5250
// CHECK-EXPANDED-NEXT: |-ExtensionDeclScope {{.*}}, [17:14 - 19:1]
5351
// CHECK-EXPANDED-NEXT: `-ExtensionBodyScope {{.*}}, [17:15 - 19:1]
@@ -56,8 +54,7 @@ var i: Int = b.my_identity()
5654
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [18:29 - 18:43]
5755
// CHECK-EXPANDED-NEXT: `-TopLevelCodeScope {{.*}}, [21:1 - 21:28]
5856
// CHECK-EXPANDED-NEXT: `-BraceStmtScope {{.*}}, [21:1 - 21:28]
59-
// CHECK-EXPANDED-NEXT: `-PatternEntryDeclScope {{.*}}, [21:5 - 21:28] entry 0 'i'
60-
// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [21:14 - 21:28] entry 0 'i'
57+
// CHECK-EXPANDED-NEXT: `-PatternEntryInitializerScope {{.*}}, [21:14 - 21:28] entry 0 'i'
6158

6259

6360
// REQUIRES: asserts

0 commit comments

Comments
 (0)