From cca5a6e524ade74508929da0bd015bf7581f9932 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 12 Jun 2023 11:29:53 +0100 Subject: [PATCH] [SILGen] Emit block after unreachable when emitting if/switch expressions When emitting the underlying `switch` statement for a `switch` expression, we emit an `unreachable` if the subject is uninhabited. Statement emission code can handle this, but expression emission expects an RValue to handed back. To remedy this, emit an unreachable block that we can emit the rest of the expression emission code into. The SILOptimizer will then drop this unreachable block. --- lib/SILGen/SILGenExpr.cpp | 19 +++++++- test/SILGen/if_expr.swift | 8 ++++ test/SILGen/switch_expr.swift | 87 +++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index b55b6667eac89..6fb3c14dcded1 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -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()); @@ -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)); } diff --git a/test/SILGen/if_expr.swift b/test/SILGen/if_expr.swift index b85152bccce87..30a87fd3f72da 100644 --- a/test/SILGen/if_expr.swift +++ b/test/SILGen/if_expr.swift @@ -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() } +} diff --git a/test/SILGen/switch_expr.swift b/test/SILGen/switch_expr.swift index 310dc90329ab3..c590be65d2597 100644 --- a/test/SILGen/switch_expr.swift +++ b/test/SILGen/switch_expr.swift @@ -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 } + } +}