zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 36faf76fe1eeb3aafcc4de94fc83fd8ca3b18b25 (tree)
parent d4cac43d308ffa930cd7174567be7c1fe18e29fb
Author: Pavel Verigo <paul.verigo@gmail.com>
Date:   Tue, 10 Mar 2026 08:38:12 +0100

AstGen: add missing addRestoreErrRetIndex calls when handling for/while expr

Fixes #30912

Diffstat:
Mlib/std/zig/AstGen.zig | 16++++++++++++++++
Mtest/error_traces.zig | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 142 insertions(+), 0 deletions(-)

diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig @@ -6656,9 +6656,16 @@ fn whileExpr( .operand = undefined, } }, }); + if (!continue_scope.is_comptime) { + _ = try continue_scope.addRestoreErrRetIndex(.{ .block = continue_block }, .always, then_node); + } _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); } try continue_scope.setBlockBody(continue_block); + if (!then_scope.is_comptime) { + const cont_node = while_full.ast.cont_expr.unwrap() orelse then_node; + _ = try then_scope.addRestoreErrRetIndex(.{ .block = cond_block }, .always, cont_node); + } _ = try then_scope.addBreak(break_tag, cond_block, .void_value); var else_scope = parent_gz.makeSubBlock(&cond_scope.base); @@ -6703,6 +6710,9 @@ fn whileExpr( try checkUsed(parent_gz, &else_scope.base, sub_scope); if (!else_scope.endsWithNoReturn()) { + if (!else_scope.is_comptime) { + _ = try else_scope.addRestoreErrRetIndex(.{ .block = loop_block }, .always, else_node); + } _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); } } else { @@ -6973,6 +6983,9 @@ fn forExpr( }); const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; + if (!then_scope.is_comptime) { + _ = try then_scope.addRestoreErrRetIndex(.{ .block = cond_block }, .always, then_node); + } _ = try then_scope.addBreak(break_tag, cond_block, .void_value); var else_scope = parent_gz.makeSubBlock(&cond_scope.base); @@ -6990,6 +7003,9 @@ fn forExpr( _ = try addEnsureResult(&else_scope, else_result, else_node); } if (!else_scope.endsWithNoReturn()) { + if (!else_scope.is_comptime) { + _ = try else_scope.addRestoreErrRetIndex(.{ .block = loop_block }, .always, else_node); + } _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); } } else { diff --git a/test/error_traces.zig b/test/error_traces.zig @@ -93,6 +93,132 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .name = "for loop pops error return trace", + .source = + \\fn foo() !void { return error.FooError; } + \\ + \\pub fn main() !void { + \\ for (0..2) |_| { + \\ const f = foo(); + \\ f catch {}; + \\ } else { + \\ const f = foo(); + \\ f catch {}; + \\ } + \\ return error.Stop; + \\} + , + .expect_error = "Stop", + .expect_trace = + \\source.zig:11:5: [address] in main + \\ return error.Stop; + \\ ^ + , + .disable_trace_optimized = &.{ + .{ .x86_64, .windows }, + .{ .x86, .windows }, + .{ .x86_64, .macos }, + .{ .aarch64, .macos }, + }, + }); + + cases.addCase(.{ + .name = "implicit continue in for loop pops stale error return trace", + .source = + \\fn foo() !void { return error.FooError; } + \\ + \\pub fn main() !void { + \\ for (0..2) |i| { + \\ const f = foo(); + \\ f catch {}; + \\ + \\ if (i == 1) return error.Stop; + \\ } + \\} + , + .expect_error = "Stop", + .expect_trace = + \\source.zig:1:18: [address] in foo + \\fn foo() !void { return error.FooError; } + \\ ^ + \\source.zig:8:21: [address] in main + \\ if (i == 1) return error.Stop; + \\ ^ + , + .disable_trace_optimized = &.{ + .{ .x86_64, .windows }, + .{ .x86, .windows }, + .{ .x86_64, .macos }, + .{ .aarch64, .macos }, + }, + }); + + cases.addCase(.{ + .name = "while loop pops error return trace", + .source = + \\fn foo() !void { return error.FooError; } + \\ + \\pub fn main() !void { + \\ var i: usize = 0; + \\ while (i < 2) { + \\ const f = foo(); + \\ f catch {}; + \\ i += 1; + \\ } else { + \\ const f = foo(); + \\ f catch {}; + \\ } + \\ return error.Stop; + \\} + , + .expect_error = "Stop", + .expect_trace = + \\source.zig:13:5: [address] in main + \\ return error.Stop; + \\ ^ + , + .disable_trace_optimized = &.{ + .{ .x86_64, .windows }, + .{ .x86, .windows }, + .{ .x86_64, .macos }, + .{ .aarch64, .macos }, + }, + }); + + cases.addCase(.{ + .name = "implicit continue in while loop pops stale error return trace", + .source = + \\fn foo() !void { return error.FooError; } + \\ + \\pub fn main() !void { + \\ var i: usize = 0; + \\ while (i < 2) { + \\ const f = foo(); + \\ f catch {}; + \\ + \\ if (i == 1) return error.Stop; + \\ i += 1; + \\ } + \\} + , + .expect_error = "Stop", + .expect_trace = + \\source.zig:1:18: [address] in foo + \\fn foo() !void { return error.FooError; } + \\ ^ + \\source.zig:9:21: [address] in main + \\ if (i == 1) return error.Stop; + \\ ^ + , + .disable_trace_optimized = &.{ + .{ .x86_64, .windows }, + .{ .x86, .windows }, + .{ .x86_64, .macos }, + .{ .aarch64, .macos }, + }, + }); + + cases.addCase(.{ .name = "try return + handled catch/if-else", .source = \\fn foo() !void {