Skip to content

Commit

Permalink
[Sema] Allow implicit nodes to wrap if/switch expressions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
hamishknight committed Sep 20, 2023
1 parent abe28dc commit 157e488
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 20 deletions.
57 changes: 37 additions & 20 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2519,30 +2519,47 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(

SingleValueStmtExpr *
SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(Expr *E) {
while (true) {
// Look through implicit conversions.
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(E)) {
E = ICE->getSubExpr();
continue;
class SVEFinder final : public ASTWalker {
public:
SingleValueStmtExpr *FoundSVE = nullptr;

PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E)) {
FoundSVE = SVE;
return Action::Stop();
}

// Look through implicit exprs.
if (E->isImplicit())
return Action::Continue(E);

// Look through coercions.
if (isa<CoerceExpr>(E))
return Action::Continue(E);

// Look through try/await (this is invalid, but we'll error on it in
// effect checking).
if (isa<AnyTryExpr>(E) || isa<AwaitExpr>(E))
return Action::Continue(E);

return Action::Stop();
}
// Look through coercions.
if (auto *CE = dyn_cast<CoerceExpr>(E)) {
E = CE->getSubExpr();
continue;
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
return Action::Stop();
}
// Look through try/await (this is invalid, but we'll error on it in
// effect checking).
if (auto *TE = dyn_cast<AnyTryExpr>(E)) {
E = TE->getSubExpr();
continue;
PreWalkAction walkToDeclPre(Decl *D) override {
return Action::Stop();
}
if (auto *AE = dyn_cast<AwaitExpr>(E)) {
E = AE->getSubExpr();
continue;
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
return Action::Stop();
}
break;
}
return dyn_cast<SingleValueStmtExpr>(E);
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
return Action::Stop();
}
};
SVEFinder finder;
E->walk(finder);
return finder.FoundSVE;
}

SourceRange SingleValueStmtExpr::getSourceRange() const {
Expand Down
13 changes: 13 additions & 0 deletions test/expr/unary/if_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1016,3 +1016,16 @@ func tryAwaitIf2() async throws -> Int {
// expected-error@-1 {{'try' may not be used on 'if' expression}}
// expected-error@-2 {{'await' may not be used on 'if' expression}}
}

struct AnyEraserP: EraserP {
init<T: EraserP>(erasing: T) {}
}

@_typeEraser(AnyEraserP)
protocol EraserP {}
struct SomeEraserP: EraserP {}

// rdar://113435870 - Make sure we allow an implicit init(erasing:) call.
dynamic func testDynamicOpaqueErase() -> some EraserP {
if .random() { SomeEraserP() } else { SomeEraserP() }
}
13 changes: 13 additions & 0 deletions test/expr/unary/switch_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1288,3 +1288,16 @@ func tryAwaitSwitch2() async throws -> Int {
// expected-error@-1 {{'try' may not be used on 'switch' expression}}
// expected-error@-2 {{'await' may not be used on 'switch' expression}}
}

struct AnyEraserP: EraserP {
init<T: EraserP>(erasing: T) {}
}

@_typeEraser(AnyEraserP)
protocol EraserP {}
struct SomeEraserP: EraserP {}

// rdar://113435870 - Make sure we allow an implicit init(erasing:) call.
dynamic func testDynamicOpaqueErase() -> some EraserP {
switch Bool.random() { default: SomeEraserP() }
}

0 comments on commit 157e488

Please sign in to comment.