Skip to content

Commit 836bc57

Browse files
committed
[AST Walker] Stop visiting the bodies of closures as expressions.
Single-expression closures have always been traversed differently from multi-statement closures. The former were traversed as if the expression was their only child, skipping the BraceStmt and implicit return, while the later was traversed as a normal BraceStmt. Unify on the latter treatment, so that traversal There are a few places where we unintentionally relied on this expression-as-child behavior. Clean those up to work with arbitrary closures, which is an overall simplification in the logic.
1 parent ad42d47 commit 836bc57

File tree

13 files changed

+102
-63
lines changed

13 files changed

+102
-63
lines changed

lib/AST/ASTWalker.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -815,21 +815,15 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
815815
return nullptr;
816816
}
817817

818-
// Handle single-expression closures.
819-
if (expr->hasSingleExpressionBody()) {
820-
if (Expr *body = doIt(expr->getSingleExpressionBody())) {
821-
expr->setSingleExpressionBody(body);
822-
return expr;
823-
}
824-
return nullptr;
825-
}
826-
827-
if (!Walker.shouldWalkIntoNonSingleExpressionClosure(expr))
818+
// Always walk into closures with a single-expression body; for those
819+
// that don't have a single-expression body, ask the walker first.
820+
if (!expr->hasSingleExpressionBody() &&
821+
!Walker.shouldWalkIntoNonSingleExpressionClosure(expr))
828822
return expr;
829823

830824
// Handle other closures.
831825
if (BraceStmt *body = cast_or_null<BraceStmt>(doIt(expr->getBody()))) {
832-
expr->setBody(body, false);
826+
expr->setBody(body, expr->hasSingleExpressionBody());
833827
return expr;
834828
}
835829
return nullptr;

lib/IDE/Refactoring.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3659,16 +3659,24 @@ static CallExpr *findTrailingClosureTarget(SourceManager &SM,
36593659
return N.isStmt(StmtKind::Brace) || N.isExpr(ExprKind::Call);
36603660
});
36613661
Finder.resolve();
3662-
if (Finder.getContexts().empty()
3663-
|| !Finder.getContexts().back().is<Expr*>())
3662+
auto contexts = Finder.getContexts();
3663+
if (contexts.empty())
36643664
return nullptr;
3665-
CallExpr *CE = cast<CallExpr>(Finder.getContexts().back().get<Expr*>());
3665+
3666+
// If the innermost context is a brace statement, drop it.
3667+
if (contexts.back().is<Stmt *>()) {
3668+
contexts = contexts.drop_back();
3669+
}
3670+
3671+
if (contexts.empty() || !contexts.back().is<Expr*>())
3672+
return nullptr;
3673+
CallExpr *CE = cast<CallExpr>(contexts.back().get<Expr*>());
36663674

36673675
if (CE->hasTrailingClosure())
36683676
// Call expression already has a trailing closure.
36693677
return nullptr;
36703678

3671-
// The last arugment is a closure?
3679+
// The last argument is a closure?
36723680
Expr *Args = CE->getArg();
36733681
if (!Args)
36743682
return nullptr;

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3859,7 +3859,8 @@ namespace {
38593859
if (expr->isLiteralInit()) {
38603860
auto *literalInit = expr->getSubExpr();
38613861
if (auto *call = dyn_cast<CallExpr>(literalInit)) {
3862-
call->getFn()->forEachChildExpr([&](Expr *subExpr) -> Expr * {
3862+
forEachExprInConstraintSystem(call->getFn(),
3863+
[&](Expr *subExpr) -> Expr * {
38633864
auto *TE = dyn_cast<TypeExpr>(subExpr);
38643865
if (!TE)
38653866
return subExpr;
@@ -5788,7 +5789,7 @@ static bool applyTypeToClosureExpr(ConstraintSystem &cs,
57885789
cs.setType(CE, toType);
57895790

57905791
// If this closure isn't type-checked in its enclosing expression, write
5791-
// theg type into the ClosureExpr directly here, since the visitor won't.
5792+
// the type into the ClosureExpr directly here, since the visitor won't.
57925793
if (!shouldTypeCheckInEnclosingExpression(CE))
57935794
CE->setType(toType);
57945795

lib/Sema/CSClosure.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class ClosureConstraintGenerator
8484
if (!expr)
8585
return;
8686

87+
// FIXME: Use SolutionApplicationTarget?
88+
cs.setContextualType(
89+
expr, TypeLoc::withoutLoc(closureResultType), CTP_ClosureResult);
8790
expr = cs.generateConstraints(expr, closure, /*isInputExpression=*/false);
8891
if (!expr) {
8992
hadError = true;

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ bool MissingConformanceFailure::diagnoseAsError() {
442442
}
443443

444444
bool hasFix = false;
445-
caseExpr->forEachChildExpr([&](Expr *expr) -> Expr * {
445+
forEachExprInConstraintSystem(caseExpr, [&](Expr *expr) -> Expr * {
446446
hasFix |= anchors.count(expr);
447447
return hasFix ? nullptr : expr;
448448
});
@@ -3277,7 +3277,7 @@ bool MissingMemberFailure::diagnoseAsError() {
32773277

32783278
bool hasUnresolvedPattern = false;
32793279
if (auto *E = getAsExpr(anchor)) {
3280-
const_cast<Expr *>(E)->forEachChildExpr([&](Expr *expr) {
3280+
forEachExprInConstraintSystem(const_cast<Expr *>(E), [&](Expr *expr) {
32813281
hasUnresolvedPattern |= isa<UnresolvedPatternExpr>(expr);
32823282
return hasUnresolvedPattern ? nullptr : expr;
32833283
});

lib/Sema/CSGen.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,6 +3882,13 @@ namespace {
38823882
}
38833883
}
38843884

3885+
// If this is a closure, only walk into its children if they
3886+
// are type-checked in the context of the enclosing expression.
3887+
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
3888+
if (!shouldTypeCheckInEnclosingExpression(closure))
3889+
return { false, expr };
3890+
}
3891+
38853892
// Now, we're ready to walk into sub expressions.
38863893
return {true, expr};
38873894
}
@@ -4021,12 +4028,6 @@ namespace {
40214028

40224029
/// Ignore declarations.
40234030
bool walkToDeclPre(Decl *decl) override { return false; }
4024-
4025-
// Don't walk into statements. This handles the BraceStmt in
4026-
// non-single-expr closures, so we don't walk into their body.
4027-
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
4028-
return { false, S };
4029-
}
40304031
};
40314032

40324033
class ConstraintWalker : public ASTWalker {

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,7 +1548,7 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor,
15481548

15491549
// Something like `foo { x in }` or `foo { $0 }`
15501550
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
1551-
closure->forEachChildExpr([&](Expr *expr) -> Expr * {
1551+
forEachExprInConstraintSystem(closure, [&](Expr *expr) -> Expr * {
15521552
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
15531553
if (!isParam(UDE->getBase()))
15541554
return expr;
@@ -9334,7 +9334,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) {
93349334

93359335
bool found = false;
93369336
if (auto *expr = getAsExpr(anchor)) {
9337-
expr->forEachChildExpr([&](Expr *subExpr) -> Expr * {
9337+
forEachExprInConstraintSystem(expr, [&](Expr *subExpr) -> Expr * {
93389338
found |= anchors.count(subExpr);
93399339
return subExpr;
93409340
});

lib/Sema/ConstraintSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5493,6 +5493,12 @@ BraceStmt *applyFunctionBuilderTransform(
54935493
/// of the parameter and result types without looking at the body.
54945494
bool shouldTypeCheckInEnclosingExpression(ClosureExpr *expr);
54955495

5496+
/// Visit each subexpression that will be part of the constraint system
5497+
/// of the given expression, including those in closure bodies that will be
5498+
/// part of the constraint system/
5499+
void forEachExprInConstraintSystem(
5500+
Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
5501+
54965502
} // end namespace swift
54975503

54985504
#endif // LLVM_SWIFT_SEMA_CONSTRAINT_SYSTEM_H

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,15 +1294,13 @@ namespace {
12941294
}
12951295

12961296
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
1297-
// Never walk into statements.
1298-
return { false, stmt };
1297+
return { true, stmt };
12991298
}
13001299
};
13011300
} // end anonymous namespace
13021301

13031302
/// Perform prechecking of a ClosureExpr before we dive into it. This returns
1304-
/// true for single-expression closures, where we want the body to be considered
1305-
/// part of this larger expression.
1303+
/// true when we want the body to be considered part of this larger expression.
13061304
bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) {
13071305
auto *PL = closure->getParameters();
13081306

@@ -4061,3 +4059,30 @@ HasDynamicCallableAttributeRequest::evaluate(Evaluator &evaluator,
40614059
bool swift::shouldTypeCheckInEnclosingExpression(ClosureExpr *expr) {
40624060
return expr->hasSingleExpressionBody();
40634061
}
4062+
4063+
void swift::forEachExprInConstraintSystem(
4064+
Expr *expr, llvm::function_ref<Expr *(Expr *)> callback) {
4065+
struct ChildWalker : ASTWalker {
4066+
llvm::function_ref<Expr *(Expr *)> callback;
4067+
4068+
ChildWalker(llvm::function_ref<Expr *(Expr *)> callback)
4069+
: callback(callback) {}
4070+
4071+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
4072+
if (auto closure = dyn_cast<ClosureExpr>(E)) {
4073+
if (!shouldTypeCheckInEnclosingExpression(closure))
4074+
return { false, callback(E) };
4075+
}
4076+
return { true, callback(E) };
4077+
}
4078+
4079+
std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override {
4080+
return { false, P };
4081+
}
4082+
bool walkToDeclPre(Decl *D) override { return false; }
4083+
bool walkToTypeReprPre(TypeRepr *T) override { return false; }
4084+
bool walkToTypeLocPre(TypeLoc &TL) override { return false; }
4085+
};
4086+
4087+
expr->walk(ChildWalker(callback));
4088+
}

test/Constraints/fixes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ struct S1116 {
179179

180180
let a1116: [S1116] = []
181181
var s1116 = Set(1...10).subtracting(a1116.map({ $0.s })) // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
182-
// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49=(}} {{53-53= ?? <#default value#>)}}
182+
// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{53-53= ?? <#default value#>}}
183183
// expected-note@-2{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{53-53=!}}
184184

185185
func makeArray<T>(_ x: T) -> [T] { [x] }

0 commit comments

Comments
 (0)