| ... | @@ -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; |
| 1836 | | 1836 | |
| | 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).?; |
| 1843 | | 1850 | |
| 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 | } |
| 1853 | | 1861 | |
| | 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 |
| 1868 | | 1883 | |
| 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_inline | 1885 | .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); |
| 1876 | | 1891 | |
| | 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 |
| 1884 | | 1912 | |
| 1885 | try genDefers(parent_gz, scope, parent_scope, .normal_only); | 1913 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 1886 | | 1914 | |
| | 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(); |
| 5162 | | 5203 | |
| 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 | } | | |
| 5166 | | 5205 | |
| 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(); |
| 5197 | | 5236 | |
| | 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); |
| 5225 | | 5281 | |
| ... | @@ -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 | } |
| 5257 | | 5304 | |
| ... | @@ -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(); |
| 5456 | | 5503 | |
| 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 | } | | |
| 5460 | | 5505 | |
| 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] == .asterisk | 5507 | 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(); |
| 5576 | | 5621 | |
| | 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 | }; |
| 5627 | | 5678 | |
| | 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 | } |
| 5655 | | 5709 | |
| ... | @@ -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; |
| 6782 | | 6836 | |
| | 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); |
| 6830 | | 6908 | |
| 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) = .{}, |
| 10336 | | 10414 | |
| | 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, |
| 10338 | | 10421 | |
| 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 | } |
| 10386 | | 10469 | |