Skip to content

Commit d52b33d

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 b8900de commit d52b33d

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

@@ -1471,9 +1474,10 @@ class PatternBindingEntry {
14711474
enum class PatternFlags {
14721475
IsText = 1 << 0,
14731476
IsFullyValidated = 1 << 1,
1477+
IsFromDebugger = 1 << 2,
14741478
};
14751479
/// The initializer context used for this pattern binding entry.
1476-
llvm::PointerIntPair<DeclContext *, 2, OptionSet<PatternFlags>>
1480+
llvm::PointerIntPair<DeclContext *, 3, OptionSet<PatternFlags>>
14771481
InitContextAndFlags;
14781482

14791483
/// Values captured by this initializer.
@@ -1501,6 +1505,14 @@ class PatternBindingEntry {
15011505
PatternFlags::IsFullyValidated);
15021506
}
15031507

1508+
/// Set if this pattern binding came from the debugger.
1509+
///
1510+
/// Stay away unless you are \c PatternBindingDecl::createForDebugger
1511+
void setFromDebugger() {
1512+
InitContextAndFlags.setInt(InitContextAndFlags.getInt() |
1513+
PatternFlags::IsFromDebugger);
1514+
}
1515+
15041516
public:
15051517
/// \p E is the initializer as parsed.
15061518
PatternBindingEntry(Pattern *P, SourceLoc EqualLoc, Expr *E,
@@ -1583,6 +1595,11 @@ class PatternBindingEntry {
15831595
PatternAndFlags.setInt(PatternAndFlags.getInt() | Flags::Subsumed);
15841596
}
15851597

1598+
/// Returns \c true if the debugger created this pattern binding entry.
1599+
bool isFromDebugger() const {
1600+
return InitContextAndFlags.getInt().contains(PatternFlags::IsFromDebugger);
1601+
}
1602+
15861603
// Return the first variable initialized by this pattern.
15871604
VarDecl *getAnchoringVarDecl() const;
15881605

@@ -1668,6 +1685,13 @@ class PatternBindingDecl final : public Decl,
16681685
unsigned NumPatternEntries,
16691686
DeclContext *Parent);
16701687

1688+
// A dedicated entrypoint that allows LLDB to create pattern bindings
1689+
// that look implicit to the compiler but contain user code.
1690+
static PatternBindingDecl *createForDebugger(ASTContext &Ctx,
1691+
StaticSpellingKind Spelling,
1692+
Pattern *Pat, Expr *E,
1693+
DeclContext *Parent);
1694+
16711695
SourceLoc getStartLoc() const {
16721696
return StaticLoc.isValid() ? StaticLoc : VarLoc;
16731697
}
@@ -1869,6 +1893,9 @@ class PatternBindingDecl final : public Decl,
18691893
return getPatternList()[i].getInitStringRepresentation(scratch);
18701894
}
18711895

1896+
/// Returns \c true if this pattern binding was created by the debugger.
1897+
bool isDebuggerBinding() const { return Bits.PatternBindingDecl.IsDebugger; }
1898+
18721899
static bool classof(const Decl *D) {
18731900
return D->getKind() == DeclKind::PatternBinding;
18741901
}

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)