Skip to content

Commit 88e4123

Browse files
committed
Sema: Skip non-single-expression closure bodies in MiscDiagnostics
This is a defensive move to avoid duplicated work and guard against crashes when a multi-expression closure body or TapExpr has not been type checked yet. Fixes <rdar://problem/48852402>.
1 parent 93b205e commit 88e4123

File tree

5 files changed

+42
-15
lines changed

5 files changed

+42
-15
lines changed

include/swift/AST/ASTWalker.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ class ASTWalker {
211211
/// However, ASTWalker does not walk into LazyInitializerExprs on its own.
212212
virtual bool shouldWalkIntoLazyInitializers() { return true; }
213213

214+
/// This method configures whether the walker should visit the body of a
215+
/// non-single expression closure.
216+
///
217+
/// For work that is performed for every top-level expression, this should
218+
/// be overridden to return false, to avoid duplicating work or visiting
219+
/// bodies of closures that have not yet been type checked.
220+
virtual bool shouldWalkIntoNonSingleExpressionClosure() { return true; }
221+
214222
/// walkToParameterListPre - This method is called when first visiting a
215223
/// ParameterList, before walking into its parameters. If it returns false,
216224
/// the subtree is skipped.

lib/AST/ASTWalker.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,9 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
755755
return nullptr;
756756
}
757757

758+
if (!Walker.shouldWalkIntoNonSingleExpressionClosure())
759+
return expr;
760+
758761
// Handle other closures.
759762
if (BraceStmt *body = cast_or_null<BraceStmt>(doIt(expr->getBody()))) {
760763
expr->setBody(body, false);
@@ -1065,12 +1068,14 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10651068
if (auto oldSubExpr = E->getSubExpr()) {
10661069
if (auto subExpr = doIt(oldSubExpr)) {
10671070
E->setSubExpr(subExpr);
1068-
}
1069-
else {
1071+
} else {
10701072
return nullptr;
10711073
}
10721074
}
10731075

1076+
if (!Walker.shouldWalkIntoNonSingleExpressionClosure())
1077+
return E;
1078+
10741079
if (auto oldBody = E->getBody()) {
10751080
if (auto body = doIt(oldBody)) {
10761081
E->setBody(dyn_cast<BraceStmt>(body));

lib/Sema/MiscDiagnostics.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
111111
bool walkToDeclPre(Decl *D) override { return false; }
112112
bool walkToTypeReprPre(TypeRepr *T) override { return true; }
113113

114+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
115+
114116
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
115117
// See through implicit conversions of the expression. We want to be able
116118
// to associate the parent of this expression with the ultimate callee.
@@ -1399,6 +1401,8 @@ static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E,
13991401
cast<VarDecl>(DRE->getDecl())->isSelfParameter();
14001402
}
14011403

1404+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
1405+
14021406
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
14031407
Expr *subExpr;
14041408
bool isStore = false;
@@ -1547,11 +1551,10 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E,
15471551
return false;
15481552
}
15491553

1554+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
1555+
15501556
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
15511557
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
1552-
if (!CE->hasSingleExpressionBody())
1553-
return { false, E };
1554-
15551558
// If this is a potentially-escaping closure expression, start looking
15561559
// for references to self if we aren't already.
15571560
if (isClosureRequiringSelfQualification(CE))
@@ -2348,7 +2351,7 @@ class VarDeclUsageChecker : public ASTWalker {
23482351
// other things going on in the initializer expressions.
23492352
return true;
23502353
}
2351-
2354+
23522355
/// The heavy lifting happens when visiting expressions.
23532356
std::pair<bool, Expr *> walkToExprPre(Expr *E) override;
23542357

@@ -2733,8 +2736,6 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27332736
E->walk(*this);
27342737
}
27352738

2736-
2737-
27382739
/// The heavy lifting happens when visiting expressions.
27392740
std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
27402741
// Sema leaves some subexpressions null, which seems really unfortunate. It
@@ -2975,6 +2976,8 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) {
29752976
public:
29762977
DiagnoseWalker(TypeChecker &tc) : TC(tc) { }
29772978

2979+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
2980+
29782981
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
29792982
// Dig into implicit expression.
29802983
if (E->isImplicit()) return { true, E };
@@ -3106,6 +3109,8 @@ class ObjCSelectorWalker : public ASTWalker {
31063109
ObjCSelectorWalker(TypeChecker &tc, const DeclContext *dc, Type selectorTy)
31073110
: TC(tc), DC(dc), SelectorTy(selectorTy) { }
31083111

3112+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3113+
31093114
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
31103115
auto *stringLiteral = dyn_cast<StringLiteralExpr>(expr);
31113116
bool fromStringLiteral = false;
@@ -3777,14 +3782,12 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E,
37773782
}
37783783
}
37793784

3785+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3786+
37803787
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
37813788
if (!E || isa<ErrorExpr>(E) || !E->getType())
37823789
return { false, E };
37833790

3784-
if (auto *CE = dyn_cast<AbstractClosureExpr>(E))
3785-
if (!CE->hasSingleExpressionBody())
3786-
return { false, E };
3787-
37883791
if (IgnoredExprs.count(E))
37893792
return { true, E };
37903793

@@ -3851,6 +3854,8 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E,
38513854
}
38523855
}
38533856

3857+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3858+
38543859
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
38553860
if (!E || isa<ErrorExpr>(E) || !E->getType())
38563861
return {false, E};

test/attr/attr_noescape.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,19 +305,16 @@ var escape : CompletionHandlerNE
305305
var escapeOther : CompletionHandler
306306
func doThing1(_ completion: (_ success: Bool) -> ()) {
307307
// expected-note@-1{{parameter 'completion' is implicitly non-escaping}}
308-
// expected-error @+2 {{non-escaping value 'escape' may only be called}}
309308
// expected-error @+1 {{non-escaping parameter 'completion' may only be called}}
310309
escape = completion // expected-error {{declaration closing over non-escaping parameter 'escape' may allow it to escape}}
311310
}
312311
func doThing2(_ completion: CompletionHandlerNE) {
313312
// expected-note@-1{{parameter 'completion' is implicitly non-escaping}}
314-
// expected-error @+2 {{non-escaping value 'escape' may only be called}}
315313
// expected-error @+1 {{non-escaping parameter 'completion' may only be called}}
316314
escape = completion // expected-error {{declaration closing over non-escaping parameter 'escape' may allow it to escape}}
317315
}
318316
func doThing3(_ completion: CompletionHandler) {
319317
// expected-note@-1{{parameter 'completion' is implicitly non-escaping}}
320-
// expected-error @+2 {{non-escaping value 'escape' may only be called}}
321318
// expected-error @+1 {{non-escaping parameter 'completion' may only be called}}
322319
escape = completion // expected-error {{declaration closing over non-escaping parameter 'escape' may allow it to escape}}
323320
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: not %target-typecheck-verify-swift
2+
3+
struct Foo {
4+
public func subscribe(_: @escaping () -> Void) {}
5+
public static func foo() {}
6+
7+
func bind() {
8+
subscribe {
9+
_ = "\(foo)"
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)