From 135b91aecd9be1f6f5806b667e07e383dd481198 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 6 Jun 2022 04:13:52 -0600 Subject: [PATCH] Treat blocks with "return" as "noreturn" Block statements that end with "break" should not be considered "noreturn" for the enclosing scope, but other "noreturn" instructions (return, panic, compile error, etc.) should be. This differentiation necessitates handling "break" differently from the other "noreturn" instructions when inside a block statement. --- src/AstGen.zig | 10 ++++++++++ src/Zir.zig | 13 +++++++++++++ .../code_after_return_in_block_is_unreachable.zig | 14 ++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index ab5befa4ba..21e5170030 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1967,6 +1967,9 @@ fn blockExpr( } try blockExprStmts(gz, scope, statements); + if (gz.endsWithNoReturn() and !gz.endsWithBreak()) { + return Zir.Inst.Ref.unreachable_value; + } return rvalue(gz, rl, .void_value, block_node); } @@ -9930,6 +9933,13 @@ const GenZir = struct { return tags[last_inst].isNoReturn(); } + fn endsWithBreak(gz: GenZir) bool { + if (gz.isEmpty()) return false; + const tags = gz.astgen.instructions.items(.tag); + const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; + return tags[last_inst].isBreak(); + } + /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { if (inst_ref == .unreachable_value) return true; diff --git a/src/Zir.zig b/src/Zir.zig index f09f2015e0..bad7b91488 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1250,6 +1250,19 @@ pub const Inst = struct { }; } + /// Returns whether the instruction is a "break". This differs from + /// isNoReturn because a "break" in a block statement is not a + /// "noreturn" for the outer scope, whereas the other "noreturn" + /// instructions are. + pub fn isBreak(tag: Tag) bool { + return switch (tag) { + .@"break", + .break_inline, + => true, + else => false, + }; + } + /// AstGen uses this to find out if `Ref.void_value` should be used in place /// of the result of a given instruction. This allows Sema to forego adding /// the instruction to the map after analysis. diff --git a/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig b/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig new file mode 100644 index 0000000000..e62edcb358 --- /dev/null +++ b/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig @@ -0,0 +1,14 @@ +export fn entry() void { + { + return; + } + + return; +} + +// error +// target=native +// +// :6:5: error: unreachable code +// :2:5: note: control flow is diverted here +