@@ -1819,6 +1819,13 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18191819 const break_label = node_datas [node ].lhs ;
18201820 const rhs = node_datas [node ].rhs ;
18211821
1822+ // Breaking out of a `catch { ... }` or `else |err| { ... }` block with a non-error value
1823+ // means that the corresponding error was correctly handled, and the error trace index
1824+ // needs to be restored so that any entries from the caught error are effectively "popped"
1825+ //
1826+ // Note: We only restore for the outermost block, since that will "pop" any nested blocks.
1827+ var err_trace_index_to_restore : Zir.Inst.Ref = .none ;
1828+
18221829 // Look for the label in the scope.
18231830 var scope = parent_scope ;
18241831 while (true ) {
@@ -1827,6 +1834,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18271834 const block_gz = scope .cast (GenZir ).? ;
18281835
18291836 if (block_gz .cur_defer_node != 0 ) {
1837+ // We are breaking out of a `defer` block.
18301838 return astgen .failNodeNotes (node , "cannot break out of defer expression" , .{}, &.{
18311839 try astgen .errNoteNode (
18321840 block_gz .cur_defer_node ,
@@ -1836,6 +1844,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18361844 });
18371845 }
18381846
1847+ if (block_gz .saved_err_trace_index != .none ) {
1848+ // We are breaking out of a `catch { ... }` or `else |err| { ... }`.
1849+ err_trace_index_to_restore = block_gz .saved_err_trace_index ;
1850+ }
1851+
18391852 const block_inst = blk : {
18401853 if (break_label != 0 ) {
18411854 if (block_gz .label ) | * label | {
@@ -1847,9 +1860,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18471860 } else if (block_gz .break_block != 0 ) {
18481861 break :blk block_gz .break_block ;
18491862 }
1863+ // If not the target, start over with the parent
18501864 scope = block_gz .parent ;
18511865 continue ;
18521866 };
1867+ // If we made it here, this block is the target of the break expr
18531868
18541869 const break_tag : Zir.Inst.Tag = if (block_gz .is_inline or block_gz .force_comptime )
18551870 .break_inline
@@ -1859,6 +1874,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18591874 if (rhs == 0 ) {
18601875 try genDefers (parent_gz , scope , parent_scope , .normal_only );
18611876
1877+ // As our last action before the break, "pop" the error trace if needed
1878+ if (err_trace_index_to_restore != .none ) {
1879+ // TODO: error-liveness and is_non_err
1880+
1881+ _ = try parent_gz .add (.{
1882+ .tag = .restore_err_ret_index ,
1883+ .data = .{ .un_node = .{
1884+ .operand = err_trace_index_to_restore ,
1885+ .src_node = parent_gz .nodeIndexToRelative (node ),
1886+ } },
1887+ });
1888+ }
1889+
18621890 _ = try parent_gz .addBreak (break_tag , block_inst , .void_value );
18631891 return Zir .Inst .Ref .unreachable_value ;
18641892 }
@@ -1876,6 +1904,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18761904
18771905 try genDefers (parent_gz , scope , parent_scope , .normal_only );
18781906
1907+ // As our last action before the break, "pop" the error trace if needed
1908+ if (err_trace_index_to_restore != .none ) {
1909+ // TODO: error-liveness and is_non_err
1910+
1911+ _ = try parent_gz .add (.{
1912+ .tag = .restore_err_ret_index ,
1913+ .data = .{ .un_node = .{
1914+ .operand = err_trace_index_to_restore ,
1915+ .src_node = parent_gz .nodeIndexToRelative (node ),
1916+ } },
1917+ });
1918+ }
1919+
18791920 switch (block_gz .break_result_loc ) {
18801921 .block_ptr = > {
18811922 const br = try parent_gz .addBreak (break_tag , block_inst , operand );
@@ -5146,9 +5187,7 @@ fn orelseCatchExpr(
51465187 block_scope .setBreakResultLoc (rl );
51475188 defer block_scope .unstack ();
51485189
5149- if (do_err_trace ) {
5150- block_scope .saved_err_trace_index = try parent_gz .addNode (.save_err_ret_index , node );
5151- }
5190+ const saved_err_trace_index = if (do_err_trace ) try parent_gz .addNode (.save_err_ret_index , node ) else .none ;
51525191
51535192 const operand_rl : ResultLoc = switch (block_scope .break_result_loc ) {
51545193 .ref = > .ref ,
@@ -5181,6 +5220,12 @@ fn orelseCatchExpr(
51815220 var else_scope = block_scope .makeSubBlock (scope );
51825221 defer else_scope .unstack ();
51835222
5223+ // Any break (of a non-error value) that navigates out of this scope means
5224+ // that the error was handled successfully, so this index will be restored.
5225+ else_scope .saved_err_trace_index = saved_err_trace_index ;
5226+ if (else_scope .outermost_err_trace_index == .none )
5227+ else_scope .outermost_err_trace_index = saved_err_trace_index ;
5228+
51845229 var err_val_scope : Scope.LocalVal = undefined ;
51855230 const else_sub_scope = blk : {
51865231 const payload = payload_token orelse break :blk & else_scope .base ;
@@ -5206,6 +5251,17 @@ fn orelseCatchExpr(
52065251 const else_result = try expr (& else_scope , else_sub_scope , block_scope .break_result_loc , rhs );
52075252 if (! else_scope .endsWithNoReturn ()) {
52085253 block_scope .break_count += 1 ;
5254+
5255+ // TODO: Add is_non_err and break check
5256+ if (do_err_trace ) {
5257+ _ = try else_scope .add (.{
5258+ .tag = .restore_err_ret_index ,
5259+ .data = .{ .un_node = .{
5260+ .operand = saved_err_trace_index ,
5261+ .src_node = parent_gz .nodeIndexToRelative (node ),
5262+ } },
5263+ });
5264+ }
52095265 }
52105266 try checkUsed (parent_gz , & else_scope .base , else_sub_scope );
52115267
@@ -5229,15 +5285,6 @@ fn orelseCatchExpr(
52295285 block ,
52305286 break_tag ,
52315287 );
5232- if (do_err_trace ) {
5233- _ = try parent_gz .add (.{
5234- .tag = .restore_err_ret_index ,
5235- .data = .{ .un_node = .{
5236- .operand = parent_gz .saved_err_trace_index ,
5237- .src_node = parent_gz .nodeIndexToRelative (node ),
5238- } },
5239- });
5240- }
52415288 return result ;
52425289}
52435290
@@ -5440,9 +5487,7 @@ fn ifExpr(
54405487 block_scope .setBreakResultLoc (rl );
54415488 defer block_scope .unstack ();
54425489
5443- if (do_err_trace ) {
5444- block_scope .saved_err_trace_index = try parent_gz .addNode (.save_err_ret_index , node );
5445- }
5490+ const saved_err_trace_index = if (do_err_trace ) try parent_gz .addNode (.save_err_ret_index , node ) else .none ;
54465491
54475492 const payload_is_ref = if (if_full .payload_token ) | payload_token |
54485493 token_tags [payload_token ] == .asterisk
@@ -5559,6 +5604,12 @@ fn ifExpr(
55595604 var else_scope = parent_gz .makeSubBlock (scope );
55605605 defer else_scope .unstack ();
55615606
5607+ // Any break (of a non-error value) that navigates out of this scope means
5608+ // that the error was handled successfully, so this index will be restored.
5609+ else_scope .saved_err_trace_index = saved_err_trace_index ;
5610+ if (else_scope .outermost_err_trace_index == .none )
5611+ else_scope .outermost_err_trace_index = saved_err_trace_index ;
5612+
55625613 const else_node = if_full .ast .else_expr ;
55635614 const else_info : struct {
55645615 src : Ast.Node.Index ,
@@ -5606,6 +5657,18 @@ fn ifExpr(
56065657 .result = .none ,
56075658 };
56085659
5660+ if (do_err_trace and ! else_scope .endsWithNoReturn ()) {
5661+ // TODO: is_non_err and other checks
5662+
5663+ _ = try else_scope .add (.{
5664+ .tag = .restore_err_ret_index ,
5665+ .data = .{ .un_node = .{
5666+ .operand = saved_err_trace_index ,
5667+ .src_node = parent_gz .nodeIndexToRelative (node ),
5668+ } },
5669+ });
5670+ }
5671+
56095672 const break_tag : Zir.Inst.Tag = if (parent_gz .force_comptime ) .break_inline else .@"break" ;
56105673 const result = try finishThenElseBlock (
56115674 parent_gz ,
@@ -5622,15 +5685,6 @@ fn ifExpr(
56225685 block ,
56235686 break_tag ,
56245687 );
5625- if (do_err_trace ) {
5626- _ = try parent_gz .add (.{
5627- .tag = .restore_err_ret_index ,
5628- .data = .{ .un_node = .{
5629- .operand = parent_gz .saved_err_trace_index ,
5630- .src_node = parent_gz .nodeIndexToRelative (node ),
5631- } },
5632- });
5633- }
56345688 return result ;
56355689}
56365690
@@ -6710,11 +6764,24 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
67106764 const operand = try reachableExpr (gz , scope , rl , operand_node , node );
67116765 gz .anon_name_strategy = prev_anon_name_strategy ;
67126766
6767+ // TODO: This should be almost identical for every break/ret
67136768 switch (nodeMayEvalToError (tree , operand_node )) {
67146769 .never = > {
67156770 // Returning a value that cannot be an error; skip error defers.
67166771 try genDefers (gz , defer_outer , scope , .normal_only );
67176772 try emitDbgStmt (gz , ret_line , ret_column );
6773+
6774+ // As our last action before the return, "pop" the error trace if needed
6775+ if (gz .outermost_err_trace_index != .none ) {
6776+ _ = try gz .add (.{
6777+ .tag = .restore_err_ret_index ,
6778+ .data = .{ .un_node = .{
6779+ .operand = gz .outermost_err_trace_index ,
6780+ .src_node = gz .nodeIndexToRelative (node ),
6781+ } },
6782+ });
6783+ }
6784+
67186785 try gz .addRet (rl , operand , node );
67196786 return Zir .Inst .Ref .unreachable_value ;
67206787 },
@@ -6756,6 +6823,17 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
67566823 };
67576824 try genDefers (& else_scope , defer_outer , scope , which_ones );
67586825 try emitDbgStmt (& else_scope , ret_line , ret_column );
6826+
6827+ // As our last action before the return, "pop" the error trace if needed
6828+ if (else_scope .outermost_err_trace_index != .none ) {
6829+ _ = try else_scope .add (.{
6830+ .tag = .restore_err_ret_index ,
6831+ .data = .{ .un_node = .{
6832+ .operand = else_scope .outermost_err_trace_index ,
6833+ .src_node = else_scope .nodeIndexToRelative (node ),
6834+ } },
6835+ });
6836+ }
67596837 try else_scope .addRet (rl , operand , node );
67606838
67616839 try setCondBrPayload (condbr , is_non_err , & then_scope , 0 , & else_scope , 0 );
@@ -10225,7 +10303,12 @@ const GenZir = struct {
1022510303 /// Keys are the raw instruction index, values are the closure_capture instruction.
1022610304 captures : std .AutoHashMapUnmanaged (Zir.Inst.Index , Zir .Inst .Index ) = .{},
1022710305
10306+ /// If this GenZir corresponds to a `catch { ... }` or `else |err| { ... }` block,
10307+ /// this err_trace_index can be restored to "pop" the trace entries for the block.
1022810308 saved_err_trace_index : Zir.Inst.Ref = .none ,
10309+ /// When returning from a function with a non-error, we must pop all trace entries
10310+ /// from any containing `catch { ... }` or `else |err| { ... }` blocks.
10311+ outermost_err_trace_index : Zir.Inst.Ref = .none ,
1022910312
1023010313 const unstacked_top = std .math .maxInt (usize );
1023110314 /// Call unstack before adding any new instructions to containing GenZir.
@@ -10271,7 +10354,7 @@ const GenZir = struct {
1027110354 .any_defer_node = gz .any_defer_node ,
1027210355 .instructions = gz .instructions ,
1027310356 .instructions_top = gz .instructions .items .len ,
10274- .saved_err_trace_index = gz .saved_err_trace_index ,
10357+ .outermost_err_trace_index = gz .outermost_err_trace_index ,
1027510358 };
1027610359 }
1027710360
0 commit comments