authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-09-12 23:05:50-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-21 10:43:42-07:00
logeda3eb1561ec9a68e692821d5de71d03e6f50d42
tree5a3acd315089bfda58cf4366445ae8ca1916e5fd
parent5316a00a188955d60cc38d56def51b8605181225

stage2: "Pop" error trace for break/return within catch

This implement trace "popping" for correctly handled errors within `catch { ... }` and `else { ... }` blocks. When breaking from these blocks with any non-error, we pop the error trace frames corresponding to the operand. When breaking with an error, we preserve the frames so that error traces "chain" together as usual. ```zig fn foo(cond1: bool, cond2: bool) !void { bar() catch { if (cond1) { // If baz() result is a non-error, pop the error trace frames from bar() // If baz() result is an error, leave the bar() frames on the error trace return baz(); } else if (cond2) { // If we break/return an error, then leave the error frames from bar() on the error trace return error.Foo; } }; // An error returned from here does not include bar()'s error frames in the trace return error.Bar; } ``` Notice that if foo() does not return an error it, it leaves no extra frames on the error trace. This is piece (1/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574

3 files changed, 301 insertions(+), 29 deletions(-)

src/AstGen.zig+108-25
...@@ -1834,6 +1834,13 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1834,6 +1834,13 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1834 const break_label = node_datas[node].lhs;1834 const break_label = node_datas[node].lhs;
1835 const rhs = node_datas[node].rhs;1835 const rhs = node_datas[node].rhs;
18361836
1837 // Breaking out of a `catch { ... }` or `else |err| { ... }` block with a non-error value
1838 // means that the corresponding error was correctly handled, and the error trace index
1839 // needs to be restored so that any entries from the caught error are effectively "popped"
1840 //
1841 // Note: We only restore for the outermost block, since that will "pop" any nested blocks.
1842 var err_trace_index_to_restore: Zir.Inst.Ref = .none;
1843
1837 // Look for the label in the scope.1844 // Look for the label in the scope.
1838 var scope = parent_scope;1845 var scope = parent_scope;
1839 while (true) {1846 while (true) {
...@@ -1842,6 +1849,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1842,6 +1849,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1842 const block_gz = scope.cast(GenZir).?;1849 const block_gz = scope.cast(GenZir).?;
18431850
1844 if (block_gz.cur_defer_node != 0) {1851 if (block_gz.cur_defer_node != 0) {
1852 // We are breaking out of a `defer` block.
1845 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{1853 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
1846 try astgen.errNoteNode(1854 try astgen.errNoteNode(
1847 block_gz.cur_defer_node,1855 block_gz.cur_defer_node,
...@@ -1851,6 +1859,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1851,6 +1859,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1851 });1859 });
1852 }1860 }
18531861
1862 if (block_gz.saved_err_trace_index != .none) {
1863 // We are breaking out of a `catch { ... }` or `else |err| { ... }`.
1864 err_trace_index_to_restore = block_gz.saved_err_trace_index;
1865 }
1866
1854 const block_inst = blk: {1867 const block_inst = blk: {
1855 if (break_label != 0) {1868 if (break_label != 0) {
1856 if (block_gz.label) |*label| {1869 if (block_gz.label) |*label| {
...@@ -1862,9 +1875,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1862,9 +1875,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1862 } else if (block_gz.break_block != 0) {1875 } else if (block_gz.break_block != 0) {
1863 break :blk block_gz.break_block;1876 break :blk block_gz.break_block;
1864 }1877 }
1878 // If not the target, start over with the parent
1865 scope = block_gz.parent;1879 scope = block_gz.parent;
1866 continue;1880 continue;
1867 };1881 };
1882 // If we made it here, this block is the target of the break expr
18681883
1869 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime)1884 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime)
1870 .break_inline1885 .break_inline
...@@ -1874,6 +1889,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1874,6 +1889,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1874 if (rhs == 0) {1889 if (rhs == 0) {
1875 try genDefers(parent_gz, scope, parent_scope, .normal_only);1890 try genDefers(parent_gz, scope, parent_scope, .normal_only);
18761891
1892 // As our last action before the break, "pop" the error trace if needed
1893 if (err_trace_index_to_restore != .none) {
1894 // TODO: error-liveness and is_non_err
1895
1896 _ = try parent_gz.add(.{
1897 .tag = .restore_err_ret_index,
1898 .data = .{ .un_node = .{
1899 .operand = err_trace_index_to_restore,
1900 .src_node = parent_gz.nodeIndexToRelative(node),
1901 } },
1902 });
1903 }
1904
1877 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);1905 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
1878 return Zir.Inst.Ref.unreachable_value;1906 return Zir.Inst.Ref.unreachable_value;
1879 }1907 }
...@@ -1884,6 +1912,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1884,6 +1912,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18841912
1885 try genDefers(parent_gz, scope, parent_scope, .normal_only);1913 try genDefers(parent_gz, scope, parent_scope, .normal_only);
18861914
1915 // As our last action before the break, "pop" the error trace if needed
1916 if (err_trace_index_to_restore != .none) {
1917 // TODO: error-liveness and is_non_err
1918
1919 _ = try parent_gz.add(.{
1920 .tag = .restore_err_ret_index,
1921 .data = .{ .un_node = .{
1922 .operand = err_trace_index_to_restore,
1923 .src_node = parent_gz.nodeIndexToRelative(node),
1924 } },
1925 });
1926 }
1927
1887 switch (block_gz.break_result_loc) {1928 switch (block_gz.break_result_loc) {
1888 .block_ptr => {1929 .block_ptr => {
1889 const br = try parent_gz.addBreak(break_tag, block_inst, operand);1930 const br = try parent_gz.addBreak(break_tag, block_inst, operand);
...@@ -5160,9 +5201,7 @@ fn orelseCatchExpr(...@@ -5160,9 +5201,7 @@ fn orelseCatchExpr(
5160 block_scope.setBreakResultLoc(rl);5201 block_scope.setBreakResultLoc(rl);
5161 defer block_scope.unstack();5202 defer block_scope.unstack();
51625203
5163 if (do_err_trace) {5204 const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none;
5164 block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node);
5165 }
51665205
5167 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {5206 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
5168 .ref => .ref,5207 .ref => .ref,
...@@ -5195,6 +5234,12 @@ fn orelseCatchExpr(...@@ -5195,6 +5234,12 @@ fn orelseCatchExpr(
5195 var else_scope = block_scope.makeSubBlock(scope);5234 var else_scope = block_scope.makeSubBlock(scope);
5196 defer else_scope.unstack();5235 defer else_scope.unstack();
51975236
5237 // Any break (of a non-error value) that navigates out of this scope means
5238 // that the error was handled successfully, so this index will be restored.
5239 else_scope.saved_err_trace_index = saved_err_trace_index;
5240 if (else_scope.outermost_err_trace_index == .none)
5241 else_scope.outermost_err_trace_index = saved_err_trace_index;
5242
5198 var err_val_scope: Scope.LocalVal = undefined;5243 var err_val_scope: Scope.LocalVal = undefined;
5199 const else_sub_scope = blk: {5244 const else_sub_scope = blk: {
5200 const payload = payload_token orelse break :blk &else_scope.base;5245 const payload = payload_token orelse break :blk &else_scope.base;
...@@ -5220,6 +5265,17 @@ fn orelseCatchExpr(...@@ -5220,6 +5265,17 @@ fn orelseCatchExpr(
5220 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);5265 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
5221 if (!else_scope.endsWithNoReturn()) {5266 if (!else_scope.endsWithNoReturn()) {
5222 block_scope.break_count += 1;5267 block_scope.break_count += 1;
5268
5269 // TODO: Add is_non_err and break check
5270 if (do_err_trace) {
5271 _ = try else_scope.add(.{
5272 .tag = .restore_err_ret_index,
5273 .data = .{ .un_node = .{
5274 .operand = saved_err_trace_index,
5275 .src_node = parent_gz.nodeIndexToRelative(node),
5276 } },
5277 });
5278 }
5223 }5279 }
5224 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);5280 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
52255281
...@@ -5243,15 +5299,6 @@ fn orelseCatchExpr(...@@ -5243,15 +5299,6 @@ fn orelseCatchExpr(
5243 block,5299 block,
5244 break_tag,5300 break_tag,
5245 );5301 );
5246 if (do_err_trace) {
5247 _ = try parent_gz.add(.{
5248 .tag = .restore_err_ret_index,
5249 .data = .{ .un_node = .{
5250 .operand = parent_gz.saved_err_trace_index,
5251 .src_node = parent_gz.nodeIndexToRelative(node),
5252 } },
5253 });
5254 }
5255 return result;5302 return result;
5256}5303}
52575304
...@@ -5454,9 +5501,7 @@ fn ifExpr(...@@ -5454,9 +5501,7 @@ fn ifExpr(
5454 block_scope.setBreakResultLoc(rl);5501 block_scope.setBreakResultLoc(rl);
5455 defer block_scope.unstack();5502 defer block_scope.unstack();
54565503
5457 if (do_err_trace) {5504 const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none;
5458 block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node);
5459 }
54605505
5461 const payload_is_ref = if (if_full.payload_token) |payload_token|5506 const payload_is_ref = if (if_full.payload_token) |payload_token|
5462 token_tags[payload_token] == .asterisk5507 token_tags[payload_token] == .asterisk
...@@ -5574,6 +5619,12 @@ fn ifExpr(...@@ -5574,6 +5619,12 @@ fn ifExpr(
5574 var else_scope = parent_gz.makeSubBlock(scope);5619 var else_scope = parent_gz.makeSubBlock(scope);
5575 defer else_scope.unstack();5620 defer else_scope.unstack();
55765621
5622 // Any break (of a non-error value) that navigates out of this scope means
5623 // that the error was handled successfully, so this index will be restored.
5624 else_scope.saved_err_trace_index = saved_err_trace_index;
5625 if (else_scope.outermost_err_trace_index == .none)
5626 else_scope.outermost_err_trace_index = saved_err_trace_index;
5627
5577 const else_node = if_full.ast.else_expr;5628 const else_node = if_full.ast.else_expr;
5578 const else_info: struct {5629 const else_info: struct {
5579 src: Ast.Node.Index,5630 src: Ast.Node.Index,
...@@ -5625,6 +5676,18 @@ fn ifExpr(...@@ -5625,6 +5676,18 @@ fn ifExpr(
5625 },5676 },
5626 };5677 };
56275678
5679 if (do_err_trace and !else_scope.endsWithNoReturn()) {
5680 // TODO: is_non_err and other checks
5681
5682 _ = try else_scope.add(.{
5683 .tag = .restore_err_ret_index,
5684 .data = .{ .un_node = .{
5685 .operand = saved_err_trace_index,
5686 .src_node = parent_gz.nodeIndexToRelative(node),
5687 } },
5688 });
5689 }
5690
5628 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";5691 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
5629 const result = try finishThenElseBlock(5692 const result = try finishThenElseBlock(
5630 parent_gz,5693 parent_gz,
...@@ -5641,15 +5704,6 @@ fn ifExpr(...@@ -5641,15 +5704,6 @@ fn ifExpr(
5641 block,5704 block,
5642 break_tag,5705 break_tag,
5643 );5706 );
5644 if (do_err_trace) {
5645 _ = try parent_gz.add(.{
5646 .tag = .restore_err_ret_index,
5647 .data = .{ .un_node = .{
5648 .operand = parent_gz.saved_err_trace_index,
5649 .src_node = parent_gz.nodeIndexToRelative(node),
5650 } },
5651 });
5652 }
5653 return result;5707 return result;
5654}5708}
56555709
...@@ -6780,11 +6834,24 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6780,11 +6834,24 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6780 const operand = try reachableExpr(gz, scope, rl, operand_node, node);6834 const operand = try reachableExpr(gz, scope, rl, operand_node, node);
6781 gz.anon_name_strategy = prev_anon_name_strategy;6835 gz.anon_name_strategy = prev_anon_name_strategy;
67826836
6837 // TODO: This should be almost identical for every break/ret
6783 switch (nodeMayEvalToError(tree, operand_node)) {6838 switch (nodeMayEvalToError(tree, operand_node)) {
6784 .never => {6839 .never => {
6785 // Returning a value that cannot be an error; skip error defers.6840 // Returning a value that cannot be an error; skip error defers.
6786 try genDefers(gz, defer_outer, scope, .normal_only);6841 try genDefers(gz, defer_outer, scope, .normal_only);
6787 try emitDbgStmt(gz, ret_line, ret_column);6842 try emitDbgStmt(gz, ret_line, ret_column);
6843
6844 // As our last action before the return, "pop" the error trace if needed
6845 if (gz.outermost_err_trace_index != .none) {
6846 _ = try gz.add(.{
6847 .tag = .restore_err_ret_index,
6848 .data = .{ .un_node = .{
6849 .operand = gz.outermost_err_trace_index,
6850 .src_node = gz.nodeIndexToRelative(node),
6851 } },
6852 });
6853 }
6854
6788 try gz.addRet(rl, operand, node);6855 try gz.addRet(rl, operand, node);
6789 return Zir.Inst.Ref.unreachable_value;6856 return Zir.Inst.Ref.unreachable_value;
6790 },6857 },
...@@ -6826,6 +6893,17 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6826,6 +6893,17 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6826 };6893 };
6827 try genDefers(&else_scope, defer_outer, scope, which_ones);6894 try genDefers(&else_scope, defer_outer, scope, which_ones);
6828 try emitDbgStmt(&else_scope, ret_line, ret_column);6895 try emitDbgStmt(&else_scope, ret_line, ret_column);
6896
6897 // As our last action before the return, "pop" the error trace if needed
6898 if (else_scope.outermost_err_trace_index != .none) {
6899 _ = try else_scope.add(.{
6900 .tag = .restore_err_ret_index,
6901 .data = .{ .un_node = .{
6902 .operand = else_scope.outermost_err_trace_index,
6903 .src_node = else_scope.nodeIndexToRelative(node),
6904 } },
6905 });
6906 }
6829 try else_scope.addRet(rl, operand, node);6907 try else_scope.addRet(rl, operand, node);
68306908
6831 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);6909 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
...@@ -10334,7 +10412,12 @@ const GenZir = struct {...@@ -10334,7 +10412,12 @@ const GenZir = struct {
10334 /// Keys are the raw instruction index, values are the closure_capture instruction.10412 /// Keys are the raw instruction index, values are the closure_capture instruction.
10335 captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},10413 captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
1033610414
10415 /// If this GenZir corresponds to a `catch { ... }` or `else |err| { ... }` block,
10416 /// this err_trace_index can be restored to "pop" the trace entries for the block.
10337 saved_err_trace_index: Zir.Inst.Ref = .none,10417 saved_err_trace_index: Zir.Inst.Ref = .none,
10418 /// When returning from a function with a non-error, we must pop all trace entries
10419 /// from any containing `catch { ... }` or `else |err| { ... }` blocks.
10420 outermost_err_trace_index: Zir.Inst.Ref = .none,
1033810421
10339 const unstacked_top = std.math.maxInt(usize);10422 const unstacked_top = std.math.maxInt(usize);
10340 /// Call unstack before adding any new instructions to containing GenZir.10423 /// Call unstack before adding any new instructions to containing GenZir.
...@@ -10380,7 +10463,7 @@ const GenZir = struct {...@@ -10380,7 +10463,7 @@ const GenZir = struct {
10380 .any_defer_node = gz.any_defer_node,10463 .any_defer_node = gz.any_defer_node,
10381 .instructions = gz.instructions,10464 .instructions = gz.instructions,
10382 .instructions_top = gz.instructions.items.len,10465 .instructions_top = gz.instructions.items.len,
10383 .saved_err_trace_index = gz.saved_err_trace_index,10466 .outermost_err_trace_index = gz.outermost_err_trace_index,
10384 };10467 };
10385 }10468 }
1038610469
src/Sema.zig+8-4
...@@ -16190,9 +16190,14 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -16190,9 +16190,14 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
16190 // This is only relevant at runtime.16190 // This is only relevant at runtime.
16191 if (block.is_comptime) return Air.Inst.Ref.zero_usize;16191 if (block.is_comptime) return Air.Inst.Ref.zero_usize;
1619216192
16193 // In the corner case that `catch { ... }` or `else |err| { ... }` is used in a function
16194 // that does *not* make any errorable calls, we still need an error trace to interact with
16195 // the AIR instructions we've already emitted.
16196 if (sema.owner_func != null)
16197 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
16198
16193 const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;16199 const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
16194 const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and16200 const ok = sema.mod.comp.bin_file.options.error_return_tracing and
16195 sema.mod.comp.bin_file.options.error_return_tracing and
16196 backend_supports_error_return_tracing;16201 backend_supports_error_return_tracing;
16197 if (!ok) return Air.Inst.Ref.zero_usize;16202 if (!ok) return Air.Inst.Ref.zero_usize;
1619816203
...@@ -16211,8 +16216,7 @@ fn zirRestoreErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi...@@ -16211,8 +16216,7 @@ fn zirRestoreErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
16211 if (block.is_comptime) return;16216 if (block.is_comptime) return;
1621216217
16213 const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;16218 const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
16214 const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and16219 const ok = sema.mod.comp.bin_file.options.error_return_tracing and
16215 sema.mod.comp.bin_file.options.error_return_tracing and
16216 backend_supports_error_return_tracing;16220 backend_supports_error_return_tracing;
16217 if (!ok) return;16221 if (!ok) return;
1621816222
test/stack_traces.zig+185
...@@ -98,6 +98,191 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -98,6 +98,191 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
98 },98 },
99 });99 });
100100
101 cases.addCase(.{
102 .name = "try return + handled catch/if-else",
103 .source =
104 \\fn foo() !void {
105 \\ return error.TheSkyIsFalling;
106 \\}
107 \\
108 \\pub fn main() !void {
109 \\ foo() catch {}; // should not affect error trace
110 \\ if (foo()) |_| {} else |_| {
111 \\ // should also not affect error trace
112 \\ }
113 \\ try foo();
114 \\}
115 ,
116 .Debug = .{
117 .expect =
118 \\error: TheSkyIsFalling
119 \\source.zig:2:5: [address] in foo (test)
120 \\ return error.TheSkyIsFalling;
121 \\ ^
122 \\source.zig:10:5: [address] in main (test)
123 \\ try foo();
124 \\ ^
125 \\
126 ,
127 },
128 .ReleaseSafe = .{
129 .exclude_os = .{
130 .windows, // TODO
131 .linux, // defeated by aggressive inlining
132 },
133 .expect =
134 \\error: TheSkyIsFalling
135 \\source.zig:2:5: [address] in [function]
136 \\ return error.TheSkyIsFalling;
137 \\ ^
138 \\source.zig:10:5: [address] in [function]
139 \\ try foo();
140 \\ ^
141 \\
142 ,
143 },
144 .ReleaseFast = .{
145 .expect =
146 \\error: TheSkyIsFalling
147 \\
148 ,
149 },
150 .ReleaseSmall = .{
151 .expect =
152 \\error: TheSkyIsFalling
153 \\
154 ,
155 },
156 });
157
158 cases.addCase(.{
159 .name = "try return from within catch",
160 .source =
161 \\fn foo() !void {
162 \\ return error.TheSkyIsFalling;
163 \\}
164 \\
165 \\fn bar() !void {
166 \\ return error.AndMyCarIsOutOfGas;
167 \\}
168 \\
169 \\pub fn main() !void {
170 \\ foo() catch { // error trace should include foo()
171 \\ try bar();
172 \\ };
173 \\}
174 ,
175 .Debug = .{
176 .expect =
177 \\error: AndMyCarIsOutOfGas
178 \\source.zig:2:5: [address] in foo (test)
179 \\ return error.TheSkyIsFalling;
180 \\ ^
181 \\source.zig:6:5: [address] in bar (test)
182 \\ return error.AndMyCarIsOutOfGas;
183 \\ ^
184 \\source.zig:11:9: [address] in main (test)
185 \\ try bar();
186 \\ ^
187 \\
188 ,
189 },
190 .ReleaseSafe = .{
191 .exclude_os = .{
192 .windows, // TODO
193 },
194 .expect =
195 \\error: AndMyCarIsOutOfGas
196 \\source.zig:2:5: [address] in [function]
197 \\ return error.TheSkyIsFalling;
198 \\ ^
199 \\source.zig:6:5: [address] in [function]
200 \\ return error.AndMyCarIsOutOfGas;
201 \\ ^
202 \\source.zig:11:9: [address] in [function]
203 \\ try bar();
204 \\ ^
205 \\
206 ,
207 },
208 .ReleaseFast = .{
209 .expect =
210 \\error: AndMyCarIsOutOfGas
211 \\
212 ,
213 },
214 .ReleaseSmall = .{
215 .expect =
216 \\error: AndMyCarIsOutOfGas
217 \\
218 ,
219 },
220 });
221
222 cases.addCase(.{
223 .name = "try return from within if-else",
224 .source =
225 \\fn foo() !void {
226 \\ return error.TheSkyIsFalling;
227 \\}
228 \\
229 \\fn bar() !void {
230 \\ return error.AndMyCarIsOutOfGas;
231 \\}
232 \\
233 \\pub fn main() !void {
234 \\ if (foo()) |_| {} else |_| { // error trace should include foo()
235 \\ try bar();
236 \\ }
237 \\}
238 ,
239 .Debug = .{
240 .expect =
241 \\error: AndMyCarIsOutOfGas
242 \\source.zig:2:5: [address] in foo (test)
243 \\ return error.TheSkyIsFalling;
244 \\ ^
245 \\source.zig:6:5: [address] in bar (test)
246 \\ return error.AndMyCarIsOutOfGas;
247 \\ ^
248 \\source.zig:11:9: [address] in main (test)
249 \\ try bar();
250 \\ ^
251 \\
252 ,
253 },
254 .ReleaseSafe = .{
255 .exclude_os = .{
256 .windows, // TODO
257 },
258 .expect =
259 \\error: AndMyCarIsOutOfGas
260 \\source.zig:2:5: [address] in [function]
261 \\ return error.TheSkyIsFalling;
262 \\ ^
263 \\source.zig:6:5: [address] in [function]
264 \\ return error.AndMyCarIsOutOfGas;
265 \\ ^
266 \\source.zig:11:9: [address] in [function]
267 \\ try bar();
268 \\ ^
269 \\
270 ,
271 },
272 .ReleaseFast = .{
273 .expect =
274 \\error: AndMyCarIsOutOfGas
275 \\
276 ,
277 },
278 .ReleaseSmall = .{
279 .expect =
280 \\error: AndMyCarIsOutOfGas
281 \\
282 ,
283 },
284 });
285
101 cases.addCase(.{286 cases.addCase(.{
102 .name = "try try return return",287 .name = "try try return return",
103 .source = 288 .source =