Skip to content

Commit 51de043

Browse files
committed
[Sema] Remove RecontextualizeClosures
Merge with ContextualizeClosuresAndMacros, and rename to ContextualizationWalker given that it re-contextualizes a whole bunch of AST nodes now. This ensures we correctly handle cases such as decls in if/switch expressions within autoclosures.
1 parent 55189ba commit 51de043

File tree

5 files changed

+117
-131
lines changed

5 files changed

+117
-131
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 89 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -61,61 +61,57 @@ using namespace swift;
6161
#define DEBUG_TYPE "TypeCheckStmt"
6262

6363
namespace {
64-
/// After forming autoclosures, we must re-parent any closure expressions
65-
/// nested inside the autoclosure, because the autoclosure introduces a new
66-
/// DeclContext.
67-
class ContextualizeClosuresAndMacros : public ASTWalker {
64+
/// After forming autoclosures and lazy initializer getters, we must update
65+
/// the DeclContexts for any AST nodes that store the DeclContext they're
66+
/// within. This includes e.g closures and decls, as well as some other
67+
/// expressions, statements, and patterns.
68+
class ContextualizationWalker : public ASTWalker {
6869
DeclContext *ParentDC;
70+
bool ForLazyInit;
71+
72+
ContextualizationWalker(DeclContext *parent, bool lazyInit)
73+
: ParentDC(parent), ForLazyInit(lazyInit) {}
74+
6975
public:
70-
ContextualizeClosuresAndMacros(DeclContext *parent) : ParentDC(parent) {}
76+
static void contextualize(ASTNode node, DeclContext *DC) {
77+
node.walk(ContextualizationWalker(DC, /*forLazyInit*/ false));
78+
}
79+
80+
static void contextualizeLazyInit(Expr *E, DeclContext *DC) {
81+
E->walk(ContextualizationWalker(DC, /*forLazyInit*/ true));
82+
}
7183

7284
MacroWalking getMacroWalkingBehavior() const override {
7385
return MacroWalking::ArgumentsAndExpansion;
7486
}
7587

88+
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
89+
// Don't walk lazy initializers, we walk the getter body specially when
90+
// synthesizing.
91+
return LazyInitializerWalking::None;
92+
}
93+
7694
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
77-
if (auto CE = dyn_cast<AutoClosureExpr>(E)) {
95+
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
7896
CE->setParent(ParentDC);
79-
80-
// Recurse into the autoclosure body with the new ParentDC.
81-
auto oldParentDC = ParentDC;
82-
ParentDC = CE;
83-
CE->getBody()->walk(*this);
84-
ParentDC = oldParentDC;
85-
86-
TypeChecker::computeCaptures(CE);
87-
return Action::SkipNode(E);
88-
}
89-
90-
if (auto CapE = dyn_cast<CaptureListExpr>(E)) {
91-
// Capture lists need to be reparented to enclosing autoclosures
92-
// and/or initializers of property wrapper backing properties
93-
// (because they subsume initializers associated with wrapped
94-
// properties).
95-
if (isa<AutoClosureExpr>(ParentDC) ||
96-
isPropertyWrapperBackingPropertyInitContext(ParentDC)) {
97-
for (auto &Cap : CapE->getCaptureList()) {
98-
Cap.PBD->setDeclContext(ParentDC);
99-
Cap.getVar()->setDeclContext(ParentDC);
100-
}
97+
// If we're contextualizing a lazy init, we don't need to walk into any
98+
// child DeclContexts, they'll already be correctly parented. We don't
99+
// need to compute captures either, that'll have been done already.
100+
if (!ForLazyInit) {
101+
contextualize(CE->getBody(), CE);
102+
TypeChecker::computeCaptures(CE);
101103
}
102-
}
103-
104-
if (auto CE = dyn_cast<ClosureExpr>(E)) {
105-
CE->setParent(ParentDC);
106-
CE->getBody()->walk(ContextualizeClosuresAndMacros(CE));
107-
108-
TypeChecker::computeCaptures(CE);
109104
return Action::SkipNode(E);
110105
}
111106

112107
// Caller-side default arguments need their @autoclosures checked.
113-
if (auto *DAE = dyn_cast<DefaultArgumentExpr>(E))
108+
if (auto *DAE = dyn_cast<DefaultArgumentExpr>(E)) {
114109
if (DAE->isCallerSide() &&
115110
(DAE->getParamDecl()->isAutoClosure() ||
116111
(DAE->getParamDecl()->getDefaultArgumentKind() ==
117112
DefaultArgumentKind::ExpressionMacro)))
118113
DAE->getCallerSideDefaultExpr()->walk(*this);
114+
}
119115

120116
// Macro expansion expressions require a DeclContext as well.
121117
if (auto macroExpansion = dyn_cast<MacroExpansionExpr>(E)) {
@@ -125,26 +121,59 @@ namespace {
125121
return Action::Continue(E);
126122
}
127123

128-
/// We don't want to recurse into most local declarations.
129-
PreWalkAction walkToDeclPre(Decl *D) override {
130-
// But we do want to walk into the initializers of local
131-
// variables.
132-
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
124+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
125+
// A couple of patterns store DeclContexts.
126+
if (auto *EP = dyn_cast<ExprPattern>(P))
127+
EP->setDeclContext(ParentDC);
128+
if (auto *EP = dyn_cast<EnumElementPattern>(P))
129+
EP->setDeclContext(ParentDC);
130+
131+
return Action::Continue(P);
133132
}
134133

135-
private:
136-
static bool isPropertyWrapperBackingPropertyInitContext(DeclContext *DC) {
137-
auto *init = dyn_cast<PatternBindingInitializer>(DC);
138-
if (!init)
139-
return false;
134+
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
135+
// The ASTWalker doesn't walk the case body variables, contextualize them
136+
// ourselves.
137+
if (auto *CS = dyn_cast<CaseStmt>(S)) {
138+
for (auto *CaseVar : CS->getCaseBodyVariablesOrEmptyArray())
139+
CaseVar->setDeclContext(ParentDC);
140+
}
141+
// A few statements store DeclContexts, update them.
142+
if (auto *BS = dyn_cast<BreakStmt>(S))
143+
BS->setDeclContext(ParentDC);
144+
if (auto *CS = dyn_cast<ContinueStmt>(S))
145+
CS->setDeclContext(ParentDC);
146+
if (auto *FS = dyn_cast<FallthroughStmt>(S))
147+
FS->setDeclContext(ParentDC);
148+
149+
return Action::Continue(S);
150+
}
140151

141-
if (auto *PB = init->getBinding()) {
142-
auto *var = PB->getSingleVar();
143-
return var && var->getOriginalWrappedProperty(
144-
PropertyWrapperSynthesizedPropertyKind::Backing);
152+
PreWalkAction walkToDeclPre(Decl *D) override {
153+
// We may encounter some decls parented outside of a local context, e.g
154+
// VarDecls in TopLevelCodeDecls are parented to the file. In such cases,
155+
// assume the DeclContext they already have is correct, autoclosures
156+
// and lazy var inits cannot be defined in such contexts anyway.
157+
if (!D->getDeclContext()->isLocalContext())
158+
return Action::SkipNode();
159+
160+
D->setDeclContext(ParentDC);
161+
162+
// Auxiliary decls need to have their contexts adjusted too.
163+
if (auto *VD = dyn_cast<VarDecl>(D)) {
164+
VD->visitAuxiliaryDecls([&](VarDecl *D) {
165+
D->setDeclContext(ParentDC);
166+
});
145167
}
146168

147-
return false;
169+
// We don't currently support peer macro declarations in local contexts,
170+
// however we don't reject them either; so just to be safe, adjust their
171+
// context too.
172+
D->visitAuxiliaryDecls([&](Decl *D) {
173+
D->setDeclContext(ParentDC);
174+
});
175+
176+
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
148177
}
149178
};
150179

@@ -197,20 +226,21 @@ namespace {
197226
} // end anonymous namespace
198227

199228
void TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) {
200-
ContextualizeClosuresAndMacros CC(DC);
201-
E->walk(CC);
229+
ContextualizationWalker::contextualize(E, DC);
202230
}
203231

204232
void TypeChecker::contextualizeCallSideDefaultArgument(DeclContext *DC,
205233
Expr *E) {
206-
ContextualizeClosuresAndMacros CC(DC);
207-
E->walk(CC);
234+
ContextualizationWalker::contextualize(E, DC);
208235
}
209236

210237
void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) {
211-
ContextualizeClosuresAndMacros CC(TLCD);
212238
if (auto *body = TLCD->getBody())
213-
body->walk(CC);
239+
ContextualizationWalker::contextualize(body, TLCD);
240+
}
241+
242+
void TypeChecker::contextualizeLazyInit(Expr *E, DeclContext *DC) {
243+
ContextualizationWalker::contextualizeLazyInit(E, DC);
214244
}
215245

216246
namespace {
@@ -1033,7 +1063,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
10331063
/// Type-check an entire function body.
10341064
bool typeCheckBody(BraceStmt *&S) {
10351065
bool HadError = typeCheckStmt(S);
1036-
S->walk(ContextualizeClosuresAndMacros(DC));
1066+
ContextualizationWalker::contextualize(S, DC);
10371067
return HadError;
10381068
}
10391069

@@ -2915,7 +2945,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
29152945
body = *optBody;
29162946
alreadyTypeChecked = true;
29172947

2918-
body->walk(ContextualizeClosuresAndMacros(AFD));
2948+
ContextualizationWalker::contextualize(body, AFD);
29192949
}
29202950
}
29212951
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 4 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,75 +1660,6 @@ synthesizeRead2CoroutineGetterBody(AccessorDecl *getter, ASTContext &ctx) {
16601660
return synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx);
16611661
}
16621662

1663-
namespace {
1664-
/// This ASTWalker explores an expression tree looking for expressions (which
1665-
/// are DeclContext's) and changes their parent DeclContext to NewDC.
1666-
/// TODO: We ought to consider merging this with
1667-
/// ContextualizeClosuresAndMacros, or better yet removing it in favor of
1668-
/// avoiding the recontextualization for lazy vars.
1669-
class RecontextualizeClosures : public ASTWalker {
1670-
DeclContext *NewDC;
1671-
public:
1672-
RecontextualizeClosures(DeclContext *NewDC) : NewDC(NewDC) {}
1673-
1674-
MacroWalking getMacroWalkingBehavior() const override {
1675-
return MacroWalking::ArgumentsAndExpansion;
1676-
}
1677-
1678-
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
1679-
// If we find a closure, update its declcontext and do *not* walk into it.
1680-
if (auto CE = dyn_cast<AbstractClosureExpr>(E)) {
1681-
CE->setParent(NewDC);
1682-
return Action::SkipNode(E);
1683-
}
1684-
1685-
return Action::Continue(E);
1686-
}
1687-
1688-
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
1689-
if (auto *EP = dyn_cast<ExprPattern>(P))
1690-
EP->setDeclContext(NewDC);
1691-
if (auto *EP = dyn_cast<EnumElementPattern>(P))
1692-
EP->setDeclContext(NewDC);
1693-
1694-
return Action::Continue(P);
1695-
}
1696-
1697-
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
1698-
// The ASTWalker doesn't walk the case body variables, contextualize them
1699-
// ourselves.
1700-
if (auto *CS = dyn_cast<CaseStmt>(S)) {
1701-
for (auto *CaseVar : CS->getCaseBodyVariablesOrEmptyArray())
1702-
CaseVar->setDeclContext(NewDC);
1703-
}
1704-
// A few statements store DeclContexts, update them.
1705-
if (auto *BS = dyn_cast<BreakStmt>(S))
1706-
BS->setDeclContext(NewDC);
1707-
if (auto *CS = dyn_cast<ContinueStmt>(S))
1708-
CS->setDeclContext(NewDC);
1709-
if (auto *FS = dyn_cast<FallthroughStmt>(S))
1710-
FS->setDeclContext(NewDC);
1711-
1712-
return Action::Continue(S);
1713-
}
1714-
1715-
PreWalkAction walkToDeclPre(Decl *D) override {
1716-
D->setDeclContext(NewDC);
1717-
1718-
// Auxiliary decls need to have their contexts adjusted too.
1719-
if (auto *VD = dyn_cast<VarDecl>(D)) {
1720-
VD->visitAuxiliaryDecls([&](VarDecl *D) {
1721-
D->setDeclContext(NewDC);
1722-
});
1723-
}
1724-
1725-
// Skip walking the children of any Decls that are also DeclContexts,
1726-
// they will already have the right parent.
1727-
return Action::SkipNodeIf(isa<DeclContext>(D));
1728-
}
1729-
};
1730-
} // end anonymous namespace
1731-
17321663
/// Synthesize the getter for a lazy property with the specified storage
17331664
/// vardecl.
17341665
static std::pair<BraceStmt *, bool>
@@ -1800,12 +1731,13 @@ synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage,
18001731
InitValue = new (Ctx) ErrorExpr(SourceRange(), Tmp2VD->getTypeInContext());
18011732
}
18021733

1803-
// Recontextualize any closure declcontexts nested in the initializer to
1804-
// realize that they are in the getter function.
1734+
// We may have stolen the self decl from a PatternBindingInitializer,
1735+
// re-parent it here.
18051736
if (Get->hasImplicitSelfDecl())
18061737
Get->getImplicitSelfDecl()->setDeclContext(Get);
18071738

1808-
InitValue->walk(RecontextualizeClosures(Get));
1739+
// Also Recontextualize any nodes nested in the initializer.
1740+
TypeChecker::contextualizeLazyInit(InitValue, Get);
18091741

18101742
// Wrap the initializer in a LazyInitializerExpr to avoid walking it twice.
18111743
auto initType = InitValue->getType();

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,9 @@ void contextualizeInitializer(Initializer *DC, Expr *init);
779779
void contextualizeCallSideDefaultArgument(DeclContext *DC, Expr *init);
780780
void contextualizeTopLevelCode(TopLevelCodeDecl *TLCD);
781781

782+
/// Contextualize a lazy initializer expression within its implicit getter.
783+
void contextualizeLazyInit(Expr *E, DeclContext *DC);
784+
782785
/// Retrieve the default type for the given protocol.
783786
///
784787
/// Some protocols, particularly those that correspond to literals, have

test/SILGen/if_expr.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,21 @@ struct LazyProp {
659659
}
660660
}
661661

662+
// https://github.com/swiftlang/swift/issues/75294
663+
func testAsyncLet(_ x: Int?) async {
664+
async let _ = if let i = x { i } else { 0 }
665+
// CHECK-LABEL: sil private [ossa] @$s7if_expr12testAsyncLetyySiSgYaFSiyYaYbcfu_ : $@convention(thin) @Sendable @async @substituted <τ_0_0> (Optional<Int>) -> (@out τ_0_0, @error any Error) for <Int>
666+
667+
async let _ = if let i = x {
668+
// CHECK-LABEL: sil private [ossa] @$s7if_expr12testAsyncLetyySiSgYaFSiyYaYbcfu0_ : $@convention(thin) @Sendable @async @substituted <τ_0_0> (Optional<Int>) -> (@out τ_0_0, @error any Error) for <Int>
669+
lazy var y = i
670+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr12testAsyncLetyySiSgYaFSiyYaYbcfu0_1yL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Int) -> Int
671+
then y
672+
} else {
673+
0
674+
}
675+
}
676+
662677
func testNestedFallthrough1() throws -> Int {
663678
let x = if .random() {
664679
switch Bool.random() {

test/SILGen/switch_expr.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ struct TestPropertyInit {
302302
}
303303
}
304304

305+
// https://github.com/swiftlang/swift/issues/75294
306+
func testAsyncLet(_ x: Int?) async {
307+
async let _ = switch x { case let i?: i default: 0 }
308+
// CHECK-LABEL: sil private [ossa] @$s11switch_expr12testAsyncLetyySiSgYaFSiyYaYbcfu_ : $@convention(thin) @Sendable @async @substituted <τ_0_0> (Optional<Int>) -> (@out τ_0_0, @error any Error) for <Int>
309+
}
310+
305311
func testNested(_ e: E) throws -> Int {
306312
switch e {
307313
case .a:

0 commit comments

Comments
 (0)