Skip to content

Commit 1993863

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 5d63199 commit 1993863

File tree

4 files changed

+87
-127
lines changed

4 files changed

+87
-127
lines changed

lib/Sema/TypeCheckStmt.cpp

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,18 @@ 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;
6970

70-
ContextualizeClosuresAndMacros(DeclContext *parent) : ParentDC(parent) {}
71+
ContextualizationWalker(DeclContext *parent) : ParentDC(parent) {}
7172

7273
public:
7374
static void contextualize(ASTNode node, DeclContext *DC) {
74-
node.walk(ContextualizeClosuresAndMacros(DC));
75+
node.walk(ContextualizationWalker(DC));
7576
}
7677

7778
MacroWalking getMacroWalkingBehavior() const override {
@@ -85,34 +86,7 @@ namespace {
8586
}
8687

8788
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
88-
if (auto CE = dyn_cast<AutoClosureExpr>(E)) {
89-
CE->setParent(ParentDC);
90-
91-
// Recurse into the autoclosure body with the new ParentDC.
92-
auto oldParentDC = ParentDC;
93-
ParentDC = CE;
94-
CE->getBody()->walk(*this);
95-
ParentDC = oldParentDC;
96-
97-
TypeChecker::computeCaptures(CE);
98-
return Action::SkipNode(E);
99-
}
100-
101-
if (auto CapE = dyn_cast<CaptureListExpr>(E)) {
102-
// Capture lists need to be reparented to enclosing autoclosures
103-
// and/or initializers of property wrapper backing properties
104-
// (because they subsume initializers associated with wrapped
105-
// properties).
106-
if (isa<AutoClosureExpr>(ParentDC) ||
107-
isPropertyWrapperBackingPropertyInitContext(ParentDC)) {
108-
for (auto &Cap : CapE->getCaptureList()) {
109-
Cap.PBD->setDeclContext(ParentDC);
110-
Cap.getVar()->setDeclContext(ParentDC);
111-
}
112-
}
113-
}
114-
115-
if (auto CE = dyn_cast<ClosureExpr>(E)) {
89+
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
11690
CE->setParent(ParentDC);
11791
contextualize(CE->getBody(), CE);
11892

@@ -121,12 +95,13 @@ namespace {
12195
}
12296

12397
// Caller-side default arguments need their @autoclosures checked.
124-
if (auto *DAE = dyn_cast<DefaultArgumentExpr>(E))
98+
if (auto *DAE = dyn_cast<DefaultArgumentExpr>(E)) {
12599
if (DAE->isCallerSide() &&
126100
(DAE->getParamDecl()->isAutoClosure() ||
127101
(DAE->getParamDecl()->getDefaultArgumentKind() ==
128102
DefaultArgumentKind::ExpressionMacro)))
129103
DAE->getCallerSideDefaultExpr()->walk(*this);
104+
}
130105

131106
// Macro expansion expressions require a DeclContext as well.
132107
if (auto macroExpansion = dyn_cast<MacroExpansionExpr>(E)) {
@@ -136,26 +111,59 @@ namespace {
136111
return Action::Continue(E);
137112
}
138113

139-
/// We don't want to recurse into most local declarations.
140-
PreWalkAction walkToDeclPre(Decl *D) override {
141-
// But we do want to walk into the initializers of local
142-
// variables.
143-
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
114+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
115+
// A couple of patterns store DeclContexts.
116+
if (auto *EP = dyn_cast<ExprPattern>(P))
117+
EP->setDeclContext(ParentDC);
118+
if (auto *EP = dyn_cast<EnumElementPattern>(P))
119+
EP->setDeclContext(ParentDC);
120+
121+
return Action::Continue(P);
144122
}
145123

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

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

158-
return false;
159+
// We don't currently support peer macro declarations in local contexts,
160+
// however we don't reject them either; so just to be safe, adjust their
161+
// context too.
162+
D->visitAuxiliaryDecls([&](Decl *D) {
163+
D->setDeclContext(ParentDC);
164+
});
165+
166+
return Action::VisitNodeIf(isa<PatternBindingDecl>(D));
159167
}
160168
};
161169

@@ -208,12 +216,12 @@ namespace {
208216
} // end anonymous namespace
209217

210218
void TypeChecker::contextualizeExpr(Expr *E, DeclContext *DC) {
211-
ContextualizeClosuresAndMacros::contextualize(E, DC);
219+
ContextualizationWalker::contextualize(E, DC);
212220
}
213221

214222
void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) {
215223
if (auto *body = TLCD->getBody())
216-
ContextualizeClosuresAndMacros::contextualize(body, TLCD);
224+
ContextualizationWalker::contextualize(body, TLCD);
217225
}
218226

219227
namespace {
@@ -1036,7 +1044,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
10361044
/// Type-check an entire function body.
10371045
bool typeCheckBody(BraceStmt *&S) {
10381046
bool HadError = typeCheckStmt(S);
1039-
ContextualizeClosuresAndMacros::contextualize(S, DC);
1047+
ContextualizationWalker::contextualize(S, DC);
10401048
return HadError;
10411049
}
10421050

@@ -2918,7 +2926,7 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval,
29182926
body = *optBody;
29192927
alreadyTypeChecked = true;
29202928

2921-
ContextualizeClosuresAndMacros::contextualize(body, AFD);
2929+
ContextualizationWalker::contextualize(body, AFD);
29222930
}
29232931
}
29242932
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 4 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,76 +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-
TypeChecker::computeCaptures(CE);
1683-
return Action::SkipNode(E);
1684-
}
1685-
1686-
return Action::Continue(E);
1687-
}
1688-
1689-
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
1690-
if (auto *EP = dyn_cast<ExprPattern>(P))
1691-
EP->setDeclContext(NewDC);
1692-
if (auto *EP = dyn_cast<EnumElementPattern>(P))
1693-
EP->setDeclContext(NewDC);
1694-
1695-
return Action::Continue(P);
1696-
}
1697-
1698-
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
1699-
// The ASTWalker doesn't walk the case body variables, contextualize them
1700-
// ourselves.
1701-
if (auto *CS = dyn_cast<CaseStmt>(S)) {
1702-
for (auto *CaseVar : CS->getCaseBodyVariablesOrEmptyArray())
1703-
CaseVar->setDeclContext(NewDC);
1704-
}
1705-
// A few statements store DeclContexts, update them.
1706-
if (auto *BS = dyn_cast<BreakStmt>(S))
1707-
BS->setDeclContext(NewDC);
1708-
if (auto *CS = dyn_cast<ContinueStmt>(S))
1709-
CS->setDeclContext(NewDC);
1710-
if (auto *FS = dyn_cast<FallthroughStmt>(S))
1711-
FS->setDeclContext(NewDC);
1712-
1713-
return Action::Continue(S);
1714-
}
1715-
1716-
PreWalkAction walkToDeclPre(Decl *D) override {
1717-
D->setDeclContext(NewDC);
1718-
1719-
// Auxiliary decls need to have their contexts adjusted too.
1720-
if (auto *VD = dyn_cast<VarDecl>(D)) {
1721-
VD->visitAuxiliaryDecls([&](VarDecl *D) {
1722-
D->setDeclContext(NewDC);
1723-
});
1724-
}
1725-
1726-
// Skip walking the children of any Decls that are also DeclContexts,
1727-
// they will already have the right parent.
1728-
return Action::SkipNodeIf(isa<DeclContext>(D));
1729-
}
1730-
};
1731-
} // end anonymous namespace
1732-
17331663
/// Synthesize the getter for a lazy property with the specified storage
17341664
/// vardecl.
17351665
static std::pair<BraceStmt *, bool>
@@ -1801,12 +1731,13 @@ synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage,
18011731
InitValue = new (Ctx) ErrorExpr(SourceRange(), Tmp2VD->getTypeInContext());
18021732
}
18031733

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

1809-
InitValue->walk(RecontextualizeClosures(Get));
1739+
// Also re-contextualize any nodes nested in the initializer.
1740+
TypeChecker::contextualizeExpr(InitValue, Get);
18101741

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

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)