Skip to content

[5.9] [SILGen] Emit block after unreachable when emitting if/switch expressions #66571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2174,11 +2174,26 @@ RValue RValueEmitter::visitEnumIsCaseExpr(EnumIsCaseExpr *E,

RValue RValueEmitter::visitSingleValueStmtExpr(SingleValueStmtExpr *E,
SGFContext C) {
auto emitStmt = [&]() {
SGF.emitStmt(E->getStmt());

// A switch of an uninhabited value gets emitted as an unreachable. In that
// case we need to emit a block to emit the rest of the expression code
// into. This block will be unreachable, so will be eliminated by the
// SILOptimizer. This is easier than handling unreachability throughout
// expression emission, as eventually SingleValueStmtExprs ought to be able
// to appear in arbitrary expression position. The rest of the emission
// will reference the uninitialized temporary variable, but that's fine
// because it'll be eliminated.
if (!SGF.B.hasValidInsertionPoint())
SGF.B.emitBlock(SGF.createBasicBlock());
};

// A void SingleValueStmtExpr either only has Void expression branches, or
// we've decided that it should have purely statement semantics. In either
// case, we can just emit the statement as-is, and produce the void rvalue.
if (E->getType()->isVoid()) {
SGF.emitStmt(E->getStmt());
emitStmt();
return SGF.emitEmptyTupleRValue(E, C);
}
auto &lowering = SGF.getTypeLowering(E->getType());
Expand All @@ -2201,7 +2216,7 @@ RValue RValueEmitter::visitSingleValueStmtExpr(SingleValueStmtExpr *E,
// Push the initialization for branches of the statement to initialize into.
SGF.SingleValueStmtInitStack.push_back(std::move(initInfo));
SWIFT_DEFER { SGF.SingleValueStmtInitStack.pop_back(); };
SGF.emitStmt(E->getStmt());
emitStmt();
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(resultAddr));
}

Expand Down
8 changes: 8 additions & 0 deletions test/SILGen/if_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,11 @@ struct TestLValues {
opt![keyPath: kp] = if .random() { 1 } else { throw Err() }
}
}

func testNever1() -> Never {
if case let x = fatalError() { x } else { fatalError() }
}

func testNever2() -> Never {
if .random() { fatalError() } else { fatalError() }
}
87 changes: 87 additions & 0 deletions test/SILGen/switch_expr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -616,3 +616,90 @@ func exprPatternInClosure() {
}
}
}

func testNeverSwitch1() {
let x = switch fatalError() {}
return x
}

func testNeverSwitch2() -> Never {
let x = switch fatalError() {
case let x: x
}
return x
}

func testNeverSwitch3() -> Int {
let x = switch fatalError() {
case fatalError(): 0
case _ where .random(): 1
default: 2
}
return x
}

func testNeverSwitch4() {
let x: Void
x = switch fatalError() {}
return x
}

func testNeverSwitch5() -> Never {
let x: Never
x = switch fatalError() {
case let x: x
}
return x
}

func testNeverSwitch6() -> Int {
let x: Int
x = switch fatalError() {
case fatalError(): 0
case _ where .random(): 1
default: 2
}
return x
}

func testNeverSwitch7() {
let _ = switch fatalError() {}
let _ = switch fatalError() { case let x: x }
let _ = switch fatalError() { default: "" }
}

func testNeverSwitch8() {
let _ = switch fatalError() { default: C() }
}

func testNeverSwitch9() {
let i = switch Bool.random() {
case true:
switch fatalError() {}
case false:
switch fatalError() {}
}
return i
}

func testNeverSwitch10() -> Never {
switch fatalError() {}
}

func testNeverSwitch11() {
return switch fatalError() {}
}

func testNeverSwitch12() -> Never {
return switch fatalError() { case let x: x }
}

func testNeverSwitch13() {
return switch fatalError() { case let x: x }
}

extension Never {
init(value: Self) {
self = switch value { case let v: v }
}
}