diff --git a/src/AstGen.zig b/src/AstGen.zig index ad7cb813ef..b613a6b677 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6919,6 +6919,8 @@ fn switchExprErrUnion( }; assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma); + const do_err_trace = astgen.fn_block != null; + const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); const case_nodes = tree.extra_data[extra.start..extra.end]; @@ -7304,10 +7306,14 @@ fn switchExprErrUnion( case_scope.instructions_top = parent_gz.instructions.items.len; defer case_scope.unstack(); + if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) + _ = try case_scope.addSaveErrRetIndex(.always); + try case_scope.addDbgBlockBegin(); if (dbg_var_name != .empty) { try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst); } + const target_expr_node = case.ast.target_expr; const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node); // check capture_scope, not err_scope to avoid false positive unused error capture @@ -7318,7 +7324,17 @@ fn switchExprErrUnion( any_uses_err_capture = true; } try case_scope.addDbgBlockEnd(); + if (!parent_gz.refIsNoReturn(case_result)) { + if (do_err_trace) + try restoreErrRetIndex( + &case_scope, + .{ .block = switch_block }, + block_scope.break_result_info, + target_expr_node, + case_result, + ); + _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); } diff --git a/src/Sema.zig b/src/Sema.zig index 89a5ba0920..dfb71e08b1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13117,6 +13117,7 @@ fn validateErrSetSwitch( .defer_err_code, .err_union_code, .ret_err_value_code, + .save_err_ret_index, .restore_err_ret_index, .is_non_err, .ret_is_non_err, diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 467c53bcb9..5126e3a538 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -807,4 +807,59 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, }); + cases.addCase(.{ + .name = "error union switch with call operand", + .source = + \\pub fn main() !void { + \\ try foo(); + \\ return error.TheSkyIsFalling; + \\} + \\ + \\noinline fn failure() error{ Fatal, NonFatal }!void { + \\ return error.NonFatal; + \\} + \\ + \\fn foo() error{Fatal}!void { + \\ return failure() catch |err| switch (err) { + \\ error.Fatal => return error.Fatal, + \\ error.NonFatal => return, + \\ }; + \\} + , + .Debug = .{ + .expect = + \\error: TheSkyIsFalling + \\source.zig:3:5: [address] in main (test) + \\ return error.TheSkyIsFalling; + \\ ^ + \\ + , + }, + .ReleaseSafe = .{ + .exclude_os = &.{ + .windows, // TODO + .linux, // defeated by aggressive inlining + }, + .expect = + \\error: TheSkyIsFalling + \\source.zig:3:5: [address] in [function] + \\ return error.TheSkyIsFalling; + \\ ^ + \\ + , + .error_tracing = true, + }, + .ReleaseFast = .{ + .expect = + \\error: TheSkyIsFalling + \\ + , + }, + .ReleaseSmall = .{ + .expect = + \\error: TheSkyIsFalling + \\ + , + }, + }); }