Skip to content

Commit c29f8f0

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 8621c03 commit c29f8f0

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
@@ -761,6 +761,9 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
761761
return nullptr;
762762
}
763763

764+
if (!Walker.shouldWalkIntoNonSingleExpressionClosure())
765+
return expr;
766+
764767
// Handle other closures.
765768
if (BraceStmt *body = cast_or_null<BraceStmt>(doIt(expr->getBody()))) {
766769
expr->setBody(body, false);
@@ -1071,12 +1074,14 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10711074
if (auto oldSubExpr = E->getSubExpr()) {
10721075
if (auto subExpr = doIt(oldSubExpr)) {
10731076
E->setSubExpr(subExpr);
1074-
}
1075-
else {
1077+
} else {
10761078
return nullptr;
10771079
}
10781080
}
10791081

1082+
if (!Walker.shouldWalkIntoNonSingleExpressionClosure())
1083+
return E;
1084+
10801085
if (auto oldBody = E->getBody()) {
10811086
if (auto body = doIt(oldBody)) {
10821087
E->setBody(dyn_cast<BraceStmt>(body));

lib/Sema/MiscDiagnostics.cpp

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

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

1413+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
1414+
14111415
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
14121416
Expr *subExpr;
14131417
bool isStore = false;
@@ -1556,11 +1560,10 @@ static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E,
15561560
return false;
15571561
}
15581562

1563+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
1564+
15591565
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
15601566
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
1561-
if (!CE->hasSingleExpressionBody())
1562-
return { false, E };
1563-
15641567
// If this is a potentially-escaping closure expression, start looking
15651568
// for references to self if we aren't already.
15661569
if (isClosureRequiringSelfQualification(CE))
@@ -2387,7 +2390,7 @@ class VarDeclUsageChecker : public ASTWalker {
23872390
// other things going on in the initializer expressions.
23882391
return true;
23892392
}
2390-
2393+
23912394
/// The heavy lifting happens when visiting expressions.
23922395
std::pair<bool, Expr *> walkToExprPre(Expr *E) override;
23932396

@@ -2772,8 +2775,6 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27722775
E->walk(*this);
27732776
}
27742777

2775-
2776-
27772778
/// The heavy lifting happens when visiting expressions.
27782779
std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
27792780
// Sema leaves some subexpressions null, which seems really unfortunate. It
@@ -3020,6 +3021,8 @@ static void checkStmtConditionTrailingClosure(TypeChecker &TC, const Expr *E) {
30203021
public:
30213022
DiagnoseWalker(TypeChecker &tc) : TC(tc) { }
30223023

3024+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3025+
30233026
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
30243027
// Dig into implicit expression.
30253028
if (E->isImplicit()) return { true, E };
@@ -3151,6 +3154,8 @@ class ObjCSelectorWalker : public ASTWalker {
31513154
ObjCSelectorWalker(TypeChecker &tc, const DeclContext *dc, Type selectorTy)
31523155
: TC(tc), DC(dc), SelectorTy(selectorTy) { }
31533156

3157+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3158+
31543159
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
31553160
auto *stringLiteral = dyn_cast<StringLiteralExpr>(expr);
31563161
bool fromStringLiteral = false;
@@ -3827,14 +3832,12 @@ static void diagnoseUnintendedOptionalBehavior(TypeChecker &TC, const Expr *E,
38273832
}
38283833
}
38293834

3835+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3836+
38303837
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
38313838
if (!E || isa<ErrorExpr>(E) || !E->getType())
38323839
return { false, E };
38333840

3834-
if (auto *CE = dyn_cast<AbstractClosureExpr>(E))
3835-
if (!CE->hasSingleExpressionBody())
3836-
return { false, E };
3837-
38383841
if (IgnoredExprs.count(E))
38393842
return { true, E };
38403843

@@ -3901,6 +3904,8 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E,
39013904
}
39023905
}
39033906

3907+
bool shouldWalkIntoNonSingleExpressionClosure() override { return false; }
3908+
39043909
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
39053910
if (!E || isa<ErrorExpr>(E) || !E->getType())
39063911
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)