Skip to content

Commit 157e488

Browse files
committed
[Sema] Allow implicit nodes to wrap if/switch expressions
Previously we would only look through a handful of AST node types when determining if an if/switch expression is in a valid position. However this doesn't handle cases where we synthesize code around an if/switch expression, such as `init(erasing:)` calls. As such, relax the logic such that it can look through any implicit expression. rdar://113435870
1 parent abe28dc commit 157e488

File tree

3 files changed

+63
-20
lines changed

3 files changed

+63
-20
lines changed

lib/AST/Expr.cpp

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,30 +2519,47 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
25192519

25202520
SingleValueStmtExpr *
25212521
SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(Expr *E) {
2522-
while (true) {
2523-
// Look through implicit conversions.
2524-
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E)) {
2525-
E = ICE->getSubExpr();
2526-
continue;
2522+
class SVEFinder final : public ASTWalker {
2523+
public:
2524+
SingleValueStmtExpr *FoundSVE = nullptr;
2525+
2526+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
2527+
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E)) {
2528+
FoundSVE = SVE;
2529+
return Action::Stop();
2530+
}
2531+
2532+
// Look through implicit exprs.
2533+
if (E->isImplicit())
2534+
return Action::Continue(E);
2535+
2536+
// Look through coercions.
2537+
if (isa<CoerceExpr>(E))
2538+
return Action::Continue(E);
2539+
2540+
// Look through try/await (this is invalid, but we'll error on it in
2541+
// effect checking).
2542+
if (isa<AnyTryExpr>(E) || isa<AwaitExpr>(E))
2543+
return Action::Continue(E);
2544+
2545+
return Action::Stop();
25272546
}
2528-
// Look through coercions.
2529-
if (auto *CE = dyn_cast<CoerceExpr>(E)) {
2530-
E = CE->getSubExpr();
2531-
continue;
2547+
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
2548+
return Action::Stop();
25322549
}
2533-
// Look through try/await (this is invalid, but we'll error on it in
2534-
// effect checking).
2535-
if (auto *TE = dyn_cast<AnyTryExpr>(E)) {
2536-
E = TE->getSubExpr();
2537-
continue;
2550+
PreWalkAction walkToDeclPre(Decl *D) override {
2551+
return Action::Stop();
25382552
}
2539-
if (auto *AE = dyn_cast<AwaitExpr>(E)) {
2540-
E = AE->getSubExpr();
2541-
continue;
2553+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
2554+
return Action::Stop();
25422555
}
2543-
break;
2544-
}
2545-
return dyn_cast<SingleValueStmtExpr>(E);
2556+
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
2557+
return Action::Stop();
2558+
}
2559+
};
2560+
SVEFinder finder;
2561+
E->walk(finder);
2562+
return finder.FoundSVE;
25462563
}
25472564

25482565
SourceRange SingleValueStmtExpr::getSourceRange() const {

test/expr/unary/if_expr.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,3 +1016,16 @@ func tryAwaitIf2() async throws -> Int {
10161016
// expected-error@-1 {{'try' may not be used on 'if' expression}}
10171017
// expected-error@-2 {{'await' may not be used on 'if' expression}}
10181018
}
1019+
1020+
struct AnyEraserP: EraserP {
1021+
init<T: EraserP>(erasing: T) {}
1022+
}
1023+
1024+
@_typeEraser(AnyEraserP)
1025+
protocol EraserP {}
1026+
struct SomeEraserP: EraserP {}
1027+
1028+
// rdar://113435870 - Make sure we allow an implicit init(erasing:) call.
1029+
dynamic func testDynamicOpaqueErase() -> some EraserP {
1030+
if .random() { SomeEraserP() } else { SomeEraserP() }
1031+
}

test/expr/unary/switch_expr.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,3 +1288,16 @@ func tryAwaitSwitch2() async throws -> Int {
12881288
// expected-error@-1 {{'try' may not be used on 'switch' expression}}
12891289
// expected-error@-2 {{'await' may not be used on 'switch' expression}}
12901290
}
1291+
1292+
struct AnyEraserP: EraserP {
1293+
init<T: EraserP>(erasing: T) {}
1294+
}
1295+
1296+
@_typeEraser(AnyEraserP)
1297+
protocol EraserP {}
1298+
struct SomeEraserP: EraserP {}
1299+
1300+
// rdar://113435870 - Make sure we allow an implicit init(erasing:) call.
1301+
dynamic func testDynamicOpaqueErase() -> some EraserP {
1302+
switch Bool.random() { default: SomeEraserP() }
1303+
}

0 commit comments

Comments
 (0)