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 ab5befa4ba40d470a2ef9dff5b37e44e7e920e7e..21e517003049002b4944485c71d882c16c62f07b 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 f09f2015e0378c1fe11c450c8cb12062635ecafe..bad7b91488014dee1dc8398a104341ce62a0455f 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 0000000000000000000000000000000000000000..e62edcb358ee5c624f1b9362b0e35acab3b304b8 --- /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 + -- 2.54.0