Skip to content

Commit b6382cd

Browse files
committed
Special-case Pattern Binding Decls Created by LLDB
When LLDB wraps a user-defined expression in the REPL, it takes something like this ``` <expr> ``` and turns it into (very very abstractly) ``` var result do { result = <expr> } print(result) ``` In the process, it creates an implicit pattern binding and an implicit do block. Of these, only the implicit do is considered by ASTScope lookup to be relevant. This presents a problem when <expr> is or contains a closure, as the parameters of that closure are defined within a scope that will never be expanded. Thus, ``` > [42].map { x in x } // <- cannot find 'x' in scope ``` This patch provides the Swift half of the fix wherein we privilege pattern bindings created by the debugger and look through them to the underlying user expression when performing ASTScope expansion. rdar://78256873
1 parent a58e8c1 commit b6382cd

File tree

5 files changed

+75
-6
lines changed

5 files changed

+75
-6
lines changed

include/swift/AST/ASTScope.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,9 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope {
957957
public:
958958
PatternEntryInitializerScope(PatternBindingDecl *pbDecl, unsigned entryIndex)
959959
: AbstractPatternEntryScope(pbDecl, entryIndex),
960-
initAsWrittenWhenCreated(pbDecl->getOriginalInit(entryIndex)) {}
960+
initAsWrittenWhenCreated(pbDecl->isDebuggerBinding() ?
961+
pbDecl->getInit(entryIndex) :
962+
pbDecl->getOriginalInit(entryIndex)) {}
961963
virtual ~PatternEntryInitializerScope() {}
962964

963965
protected:

include/swift/AST/Decl.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,13 @@ class alignas(1 << DeclAlignInBits) Decl {
318318
Hoisted : 1
319319
);
320320

321-
SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+2+16,
321+
SWIFT_INLINE_BITFIELD_FULL(PatternBindingDecl, Decl, 1+1+2+16,
322322
/// Whether this pattern binding declares static variables.
323323
IsStatic : 1,
324324

325+
/// Whether this pattern binding is synthesized by the debugger.
326+
IsDebugger : 1,
327+
325328
/// Whether 'static' or 'class' was used.
326329
StaticSpelling : 2,
327330

@@ -1474,9 +1477,10 @@ class PatternBindingEntry {
14741477
enum class PatternFlags {
14751478
IsText = 1 << 0,
14761479
IsFullyValidated = 1 << 1,
1480+
IsFromDebugger = 1 << 2,
14771481
};
14781482
/// The initializer context used for this pattern binding entry.
1479-
llvm::PointerIntPair<DeclContext *, 2, OptionSet<PatternFlags>>
1483+
llvm::PointerIntPair<DeclContext *, 3, OptionSet<PatternFlags>>
14801484
InitContextAndFlags;
14811485

14821486
/// Values captured by this initializer.
@@ -1504,6 +1508,14 @@ class PatternBindingEntry {
15041508
PatternFlags::IsFullyValidated);
15051509
}
15061510

1511+
/// Set if this pattern binding came from the debugger.
1512+
///
1513+
/// Stay away unless you are \c PatternBindingDecl::createForDebugger
1514+
void setFromDebugger() {
1515+
InitContextAndFlags.setInt(InitContextAndFlags.getInt() |
1516+
PatternFlags::IsFromDebugger);
1517+
}
1518+
15071519
public:
15081520
/// \p E is the initializer as parsed.
15091521
PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E,
@@ -1586,6 +1598,11 @@ class PatternBindingEntry {
15861598
PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Subsumed);
15871599
}
15881600

1601+
/// Returns \c true if the debugger created this pattern binding entry.
1602+
bool isFromDebugger() const {
1603+
return InitContextAndFlags.getInt().contains(PatternFlags::IsFromDebugger);
1604+
}
1605+
15891606
// Return the first variable initialized by this pattern.
15901607
VarDecl *getAnchoringVarDecl() const;
15911608

@@ -1671,6 +1688,13 @@ class PatternBindingDecl final : public Decl,
16711688
unsigned NumPatternEntries,
16721689
DeclContext *Parent);
16731690

1691+
// A dedicated entrypoint that allows LLDB to create pattern bindings
1692+
// that look implicit to the compiler but contain user code.
1693+
static PatternBindingDecl *createForDebugger(ASTContext &Ctx,
1694+
StaticSpellingKind Spelling,
1695+
Pattern *Pat, Expr *E,
1696+
DeclContext *Parent);
1697+
16741698
SourceLoc getStartLoc() const {
16751699
return StaticLoc.isValid() ? StaticLoc : VarLoc;
16761700
}
@@ -1872,6 +1896,9 @@ class PatternBindingDecl final : public Decl,
18721896
return getPatternList()[i].getInitStringRepresentation(scratch);
18731897
}
18741898

1899+
/// Returns \c true if this pattern binding was created by the debugger.
1900+
bool isDebuggerBinding() const { return Bits.PatternBindingDecl.IsDebugger; }
1901+
18751902
static bool classof(const Decl *D) {
18761903
return D->getKind() == DeclKind::PatternBinding;
18771904
}

lib/AST/ASTScopeCreation.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,15 @@ ScopeCreator::addToScopeTreeAndReturnInsertionPoint(ASTNode n,
480480
if (!n)
481481
return parent;
482482

483+
// HACK: LLDB creates implicit pattern bindings that... contain user
484+
// expressions. We need to actually honor lookups through those bindings
485+
// in case they contain closures that bind additional variables in further
486+
// scopes.
483487
if (auto *d = n.dyn_cast<Decl *>())
484488
if (d->isImplicit())
485-
return parent;
489+
if (!isa<PatternBindingDecl>(d)
490+
|| !cast<PatternBindingDecl>(d)->isDebuggerBinding())
491+
return parent;
486492

487493
NodeAdder adder(endLoc);
488494
if (auto *p = n.dyn_cast<Decl *>())
@@ -733,6 +739,23 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
733739
this, decl, patternEntryIndex);
734740
}
735741

742+
// If this pattern binding entry was created by the debugger, it will always
743+
// have a synthesized init that is created from user code. We special-case
744+
// lookups into these scopes to look through the debugger's chicanery to the
745+
// underlying user-defined scopes, if any.
746+
if (patternEntry.isFromDebugger() && patternEntry.getInit()) {
747+
ASTScopeAssert(
748+
patternEntry.getInit()->getSourceRange().isValid(),
749+
"pattern initializer has invalid source range");
750+
ASTScopeAssert(
751+
!getSourceManager().isBeforeInBuffer(
752+
patternEntry.getInit()->getStartLoc(), decl->getStartLoc()),
753+
"inits are always after the '='");
754+
scopeCreator
755+
.constructExpandAndInsert<PatternEntryInitializerScope>(
756+
this, decl, patternEntryIndex);
757+
}
758+
736759
// Add accessors for the variables in this pattern.
737760
patternEntry.getPattern()->forEachVariable([&](VarDecl *var) {
738761
scopeCreator.addChildrenForParsedAccessors(var, this);
@@ -751,8 +774,7 @@ void
751774
PatternEntryInitializerScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
752775
ScopeCreator &scopeCreator) {
753776
// Create a child for the initializer expression.
754-
scopeCreator.addToScopeTree(ASTNode(getPatternEntry().getOriginalInit()),
755-
this);
777+
scopeCreator.addToScopeTree(ASTNode(initAsWrittenWhenCreated), this);
756778
}
757779

758780

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ static SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *);
4040

4141
void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child,
4242
const ASTContext &ctx) const {
43+
// Ignore debugger bindings - they're a special mix of user code and implicit
44+
// wrapper code that is too difficult to check for consistency.
45+
if (auto d = getDeclIfAny().getPtrOrNull())
46+
if (auto *PBD = dyn_cast<PatternBindingDecl>(d))
47+
if (PBD->isDebuggerBinding())
48+
return;
49+
4350
auto &sourceMgr = ctx.SourceMgr;
4451

4552
auto range = getCharSourceRangeOfScope(sourceMgr);

lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,17 @@ PatternBindingDecl *PatternBindingDecl::createImplicit(
13411341
return Result;
13421342
}
13431343

1344+
PatternBindingDecl *PatternBindingDecl::createForDebugger(
1345+
ASTContext &Ctx, StaticSpellingKind StaticSpelling, Pattern *Pat, Expr *E,
1346+
DeclContext *Parent) {
1347+
auto *Result = createImplicit(Ctx, StaticSpelling, Pat, E, Parent);
1348+
Result->Bits.PatternBindingDecl.IsDebugger = true;
1349+
for (auto &entry : Result->getMutablePatternList()) {
1350+
entry.setFromDebugger();
1351+
}
1352+
return Result;
1353+
}
1354+
13441355
PatternBindingDecl *
13451356
PatternBindingDecl::create(ASTContext &Ctx, SourceLoc StaticLoc,
13461357
StaticSpellingKind StaticSpelling,

0 commit comments

Comments
 (0)