From 2323410146038e3a876ad9fa59509fff13d20d2a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 23 Nov 2025 17:15:29 +0200 Subject: [PATCH] improve handling of noreturn statements --- src/Translator.zig | 32 ++++------- src/ast.zig | 19 ++++--- test/cases/translate/bool_not.c | 12 ++-- test/cases/translate/continue_in_switch.c | 67 +++++++++++++++++++++++ test/cases/translate/do_loop.c | 12 ++++ test/cases/translate/early_return.c | 10 ++++ test/cases/translate/switch_on_int.c | 4 +- 7 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 test/cases/translate/continue_in_switch.c create mode 100644 test/cases/translate/early_return.c diff --git a/src/Translator.zig b/src/Translator.zig index 01248b9..80ddd5c 100644 --- a/src/Translator.zig +++ b/src/Translator.zig @@ -1618,7 +1618,10 @@ fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: * const result = try t.transStmt(&block.base, stmt); switch (result.tag()) { .declaration, .empty_block => {}, - else => try block.statements.append(t.gpa, result), + else => { + try block.statements.append(t.gpa, result); + if (result.isNoreturn()) return; + }, } } } @@ -1754,7 +1757,7 @@ fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) Tr }; var body_node = try t.transStmt(&loop_scope, do_stmt.body); - if (body_node.isNoreturn(true)) { + if (body_node.isNoreturn()) { // The body node ends in a noreturn statement. Simply put it in a while (true) // in case it contains breaks or continues. } else if (do_stmt.body.get(t.tree) == .compound_stmt) { @@ -1974,8 +1977,6 @@ fn transSwitchProngStmt( body: []const Node.Index, ) TransError!ZigNode { switch (stmt.get(t.tree)) { - .break_stmt => return ZigTag.@"break".init(), - .return_stmt => return t.transStmt(scope, stmt), .case_stmt, .default_stmt => unreachable, else => { var block_scope = try Scope.Block.init(t, scope, false); @@ -1996,15 +1997,6 @@ fn transSwitchProngStmtInline( ) TransError!void { for (body) |stmt| { switch (stmt.get(t.tree)) { - .return_stmt => { - const result = try t.transStmt(&block.base, stmt); - try block.statements.append(t.gpa, result); - return; - }, - .break_stmt => { - try block.statements.append(t.gpa, ZigTag.@"break".init()); - return; - }, .case_stmt => |case_stmt| { var sub = case_stmt.body; while (true) switch (sub.get(t.tree)) { @@ -2015,7 +2007,7 @@ fn transSwitchProngStmtInline( const result = try t.transStmt(&block.base, sub); assert(result.tag() != .declaration); try block.statements.append(t.gpa, result); - if (result.isNoreturn(true)) return; + if (result.isNoreturn()) return; }, .default_stmt => |default_stmt| { var sub = default_stmt.body; @@ -2027,18 +2019,16 @@ fn transSwitchProngStmtInline( const result = try t.transStmt(&block.base, sub); assert(result.tag() != .declaration); try block.statements.append(t.gpa, result); - if (result.isNoreturn(true)) return; - }, - .compound_stmt => |compound_stmt| { - const result = try t.transCompoundStmt(&block.base, compound_stmt); - try block.statements.append(t.gpa, result); - if (result.isNoreturn(true)) return; + if (result.isNoreturn()) return; }, else => { const result = try t.transStmt(&block.base, stmt); switch (result.tag()) { .declaration, .empty_block => {}, - else => try block.statements.append(t.gpa, result), + else => { + try block.statements.append(t.gpa, result); + if (result.isNoreturn()) return; + }, } }, } diff --git a/src/ast.zig b/src/ast.zig index b7a8fd4..47ad1d6 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -455,14 +455,14 @@ pub const Node = extern union { return .{ .ptr_otherwise = payload }; } - pub fn isNoreturn(node: Node, break_counts: bool) bool { - switch (node.tag()) { + pub fn isNoreturn(node: Node) bool { + return switch (node.tag()) { .block => { const block_node = node.castTag(.block).?; if (block_node.data.stmts.len == 0) return false; const last = block_node.data.stmts[block_node.data.stmts.len - 1]; - return last.isNoreturn(break_counts); + return last.isNoreturn(); }, .@"switch" => { const switch_node = node.castTag(.@"switch").?; @@ -475,15 +475,16 @@ pub const Node = extern union { else unreachable; - if (!body.isNoreturn(break_counts)) return false; + if (!body.isNoreturn()) return false; } return true; }, - .@"return", .return_void => return true, - .@"break" => if (break_counts) return true, - else => {}, - } - return false; + .@"return", .return_void => true, + .@"break" => true, + .@"continue" => true, + .@"unreachable" => true, + else => false, + }; } pub fn isBoolRes(res: Node) bool { diff --git a/test/cases/translate/bool_not.c b/test/cases/translate/bool_not.c index b2bd564..bf0b2f4 100644 --- a/test/cases/translate/bool_not.c +++ b/test/cases/translate/bool_not.c @@ -2,9 +2,9 @@ int foo() { int a; float b; void *c; - return !(a == 0); - return !a; - return !b; + if (1) return !(a == 0); + if (1) return !a; + if (1) return !b; return !c; } @@ -17,8 +17,8 @@ int foo() { // _ = &b; // var c: ?*anyopaque = undefined; // _ = &c; -// return @intFromBool(!(a == @as(c_int, 0))); -// return @intFromBool(!(a != 0)); -// return @intFromBool(!(b != 0)); +// if (true) return @intFromBool(!(a == @as(c_int, 0))); +// if (true) return @intFromBool(!(a != 0)); +// if (true) return @intFromBool(!(b != 0)); // return @intFromBool(!(c != null)); // } diff --git a/test/cases/translate/continue_in_switch.c b/test/cases/translate/continue_in_switch.c new file mode 100644 index 0000000..766b5d1 --- /dev/null +++ b/test/cases/translate/continue_in_switch.c @@ -0,0 +1,67 @@ +int entry(int i) { + int res = i; + while (1) switch (res) { + case 0: + res += 1; + case 1: + continue; + case 2: { + continue; + } + case 3: + if (res == 42) return - 1; + res = 42; + continue; + case 4: + return 1; + case 5: + res = 10; + default: + break; + }; + return res; +} + +// translate +// +// pub export fn entry(arg_i: c_int) c_int { +// var i = arg_i; +// _ = &i; +// var res: c_int = i; +// _ = &res; +// while (true) { +// while (true) { +// switch (res) { +// @as(c_int, 0) => { +// res += 1; +// continue; +// }, +// @as(c_int, 1) => { +// continue; +// }, +// @as(c_int, 2) => { +// { +// continue; +// } +// }, +// @as(c_int, 3) => { +// if (res == @as(c_int, 42)) return -@as(c_int, 1); +// res = 42; +// continue; +// }, +// @as(c_int, 4) => { +// return 1; +// }, +// @as(c_int, 5) => { +// res = 10; +// break; +// }, +// else => { +// break; +// }, +// } +// break; +// } +// } +// return res; +// } diff --git a/test/cases/translate/do_loop.c b/test/cases/translate/do_loop.c index e8cddac..80a132f 100644 --- a/test/cases/translate/do_loop.c +++ b/test/cases/translate/do_loop.c @@ -8,6 +8,12 @@ void foo(void) { do b = b -1; while (b); + do { + __builtin_unreachable(); + } while (0); + do { + continue; + } while (1); } // translate @@ -25,4 +31,10 @@ void foo(void) { // b = b - @as(c_int, 1); // if (!(b != 0)) break; // } +// while (true) { +// unreachable; +// } +// while (true) { +// continue; +// } // } diff --git a/test/cases/translate/early_return.c b/test/cases/translate/early_return.c new file mode 100644 index 0000000..00d30fe --- /dev/null +++ b/test/cases/translate/early_return.c @@ -0,0 +1,10 @@ +static unsigned long early_return(void) { + return 8 * 1024 * 1024; + return 2 * 1024 * 1024; +} + +// translate +// +// pub fn early_return() callconv(.c) c_ulong { +// return @bitCast(@as(c_long, (@as(c_int, 8) * @as(c_int, 1024)) * @as(c_int, 1024))); +// } diff --git a/test/cases/translate/switch_on_int.c b/test/cases/translate/switch_on_int.c index ac61d39..58f5a3a 100644 --- a/test/cases/translate/switch_on_int.c +++ b/test/cases/translate/switch_on_int.c @@ -69,7 +69,9 @@ void switch_fn(int i) { // @as(c_int, 6) => { // while (true) { // switch (res) { -// @as(c_int, 9) => break, +// @as(c_int, 9) => { +// break; +// }, // else => {}, // } // break;