diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index eb34af0307f83..056eec33e5779 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 } + } +}