Skip to content

Commit d915202

Browse files
committed
[AST] Allow configurable lazy initializer walking
Allow ASTWalker subclasses to specify whether they want to visit lazy variable initializers as part of the pattern binding, getter body, or not at all.
1 parent df66f27 commit d915202

File tree

5 files changed

+57
-24
lines changed

5 files changed

+57
-24
lines changed

include/swift/AST/ASTWalker.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ struct ReferenceMetaData {
5050
: Kind(Kind), AccKind(AccKind), isImplicit(isImplicit) {}
5151
};
5252

53+
/// Specifies how the initialization expression of a \c lazy variable should be
54+
/// walked by the ASTWalker.
55+
enum class LazyInitializerWalking {
56+
/// No lazy initialization expressions will be walked.
57+
None,
58+
59+
/// The lazy initialization expression will only be walked as a part of
60+
/// the variable's pattern binding decl. This is the default behavior, and is
61+
/// consistent with the initializer being syntactically part of the pattern
62+
/// binding.
63+
InPatternBinding,
64+
65+
/// The lazy initialization expression will only be walked as part of the
66+
/// body of the synthesized accessor for the lazy variable. In such an
67+
/// accessor, the expression is denoted by LazyInitializerExpr. This is mainly
68+
/// useful for code emission.
69+
InAccessor
70+
};
71+
5372
/// An abstract class used to traverse an AST.
5473
class ASTWalker {
5574
public:
@@ -193,15 +212,13 @@ class ASTWalker {
193212
/// params in AbstractFunctionDecl and NominalTypeDecl.
194213
virtual bool shouldWalkIntoGenericParams() { return false; }
195214

196-
/// This method configures whether the walker should walk into the
197-
/// initializers of lazy variables. These initializers are semantically
198-
/// different from other initializers in their context and so sometimes
199-
/// should not be visited.
200-
///
201-
/// Note that visiting the body of the lazy getter will find a
202-
/// LazyInitializerExpr with the initializer as its sub-expression.
203-
/// However, ASTWalker does not walk into LazyInitializerExprs on its own.
204-
virtual bool shouldWalkIntoLazyInitializers() { return true; }
215+
/// This method configures how the walker should walk the initializers of
216+
/// lazy variables. These initializers are semantically different from other
217+
/// initializers in their context and so sometimes should be visited as part
218+
/// of the synthesized getter, or should not be visited at all.
219+
virtual LazyInitializerWalking getLazyInitializerWalkingBehavior() {
220+
return LazyInitializerWalking::InPatternBinding;
221+
}
205222

206223
/// This method configures whether the walker should visit the body of a
207224
/// closure that was checked separately from its enclosing expression.

include/swift/AST/Expr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5314,6 +5314,7 @@ class LazyInitializerExpr : public Expr {
53145314
SourceLoc getLoc() const { return SubExpr->getLoc(); }
53155315

53165316
Expr *getSubExpr() const { return SubExpr; }
5317+
void setSubExpr(Expr *subExpr) { SubExpr = subExpr; }
53175318

53185319
static bool classof(const Expr *E) {
53195320
return E->getKind() == ExprKind::LazyInitializer;

lib/AST/ASTVerifier.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,11 +2338,11 @@ class Verifier : public ASTWalker {
23382338
verifyCheckedBase(VD);
23392339
}
23402340

2341-
bool shouldWalkIntoLazyInitializers() override {
2341+
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
23422342
// We don't want to walk into lazy initializers because they should
23432343
// have been reparented to their synthesized getter, which will
23442344
// invalidate various invariants.
2345-
return false;
2345+
return LazyInitializerWalking::None;
23462346
}
23472347

23482348
void verifyChecked(PatternBindingDecl *binding) {

lib/AST/ASTWalker.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,23 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
195195
PBD->setPattern(idx, Pat, PBD->getInitContext(idx));
196196
else
197197
return true;
198-
if (PBD->getInit(idx) &&
199-
!isPropertyWrapperBackingProperty &&
200-
(!PBD->isInitializerSubsumed(idx) ||
201-
Walker.shouldWalkIntoLazyInitializers())) {
198+
199+
if (!PBD->getInit(idx) || isPropertyWrapperBackingProperty)
200+
continue;
201+
202+
if (PBD->isInitializerSubsumed(idx) &&
203+
Walker.getLazyInitializerWalkingBehavior() !=
204+
LazyInitializerWalking::InPatternBinding) {
205+
break;
206+
}
207+
202208
#ifndef NDEBUG
203-
PrettyStackTraceDecl debugStack("walking into initializer for", PBD);
209+
PrettyStackTraceDecl debugStack("walking into initializer for", PBD);
204210
#endif
205-
if (Expr *E2 = doIt(PBD->getInit(idx)))
206-
PBD->setInit(idx, E2);
207-
else
208-
return true;
209-
}
211+
if (Expr *E2 = doIt(PBD->getInit(idx)))
212+
PBD->setInit(idx, E2);
213+
else
214+
return true;
210215
}
211216
return false;
212217
}
@@ -1073,7 +1078,17 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10731078
}
10741079

10751080
Expr *visitLazyInitializerExpr(LazyInitializerExpr *E) {
1076-
// Initializer is totally opaque for most visitation purposes.
1081+
// The initializer is opaque unless we specifically want to visit it as part
1082+
// of the accessor body.
1083+
if (Walker.getLazyInitializerWalkingBehavior() !=
1084+
LazyInitializerWalking::InAccessor) {
1085+
return E;
1086+
}
1087+
auto *sub = doIt(E->getSubExpr());
1088+
if (!sub)
1089+
return nullptr;
1090+
1091+
E->setSubExpr(sub);
10771092
return E;
10781093
}
10791094

lib/Sema/TypeCheckCaptures.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,11 @@ class FindCapturedVars : public ASTWalker {
210210
checkType(VD->getInterfaceType(), VD->getLoc());
211211
}
212212

213-
bool shouldWalkIntoLazyInitializers() override {
213+
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
214214
// We don't want to walk into lazy initializers because they're not
215215
// really present at this level. We'll catch them when processing
216216
// the getter.
217-
return false;
217+
return LazyInitializerWalking::None;
218218
}
219219

220220
std::pair<bool, Expr *> walkToDeclRefExpr(DeclRefExpr *DRE) {

0 commit comments

Comments
 (0)