| ... | ... | @@ -1834,6 +1834,13 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1834 | 1834 | const break_label = node_datas[node].lhs; |
| 1835 | 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 | 1844 | // Look for the label in the scope. |
| 1838 | 1845 | var scope = parent_scope; |
| 1839 | 1846 | while (true) { |
| ... | ... | @@ -1842,6 +1849,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1842 | 1849 | const block_gz = scope.cast(GenZir).?; |
| 1843 | 1850 | |
| 1844 | 1851 | if (block_gz.cur_defer_node != 0) { |
| 1852 | // We are breaking out of a `defer` block. |
| 1845 | 1853 | return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ |
| 1846 | 1854 | try astgen.errNoteNode( |
| 1847 | 1855 | block_gz.cur_defer_node, |
| ... | ... | @@ -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 | 1867 | const block_inst = blk: { |
| 1855 | 1868 | if (break_label != 0) { |
| 1856 | 1869 | if (block_gz.label) |*label| { |
| ... | ... | @@ -1862,9 +1875,11 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1862 | 1875 | } else if (block_gz.break_block != 0) { |
| 1863 | 1876 | break :blk block_gz.break_block; |
| 1864 | 1877 | } |
| 1878 | // If not the target, start over with the parent |
| 1865 | 1879 | scope = block_gz.parent; |
| 1866 | 1880 | continue; |
| 1867 | 1881 | }; |
| 1882 | // If we made it here, this block is the target of the break expr |
| 1868 | 1883 | |
| 1869 | 1884 | const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime) |
| 1870 | 1885 | .break_inline |
| ... | ... | @@ -1874,6 +1889,19 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 1874 | 1889 | if (rhs == 0) { |
| 1875 | 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 | 1905 | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); |
| 1878 | 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 | 1912 | |
| 1885 | 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 | 1928 | switch (block_gz.break_result_loc) { |
| 1888 | 1929 | .block_ptr => { |
| 1889 | 1930 | const br = try parent_gz.addBreak(break_tag, block_inst, operand); |
| ... | ... | @@ -5160,9 +5201,7 @@ fn orelseCatchExpr( |
| 5160 | 5201 | block_scope.setBreakResultLoc(rl); |
| 5161 | 5202 | defer block_scope.unstack(); |
| 5162 | 5203 | |
| 5163 | | if (do_err_trace) { |
| 5164 | | block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node); |
| 5165 | | } |
| 5204 | const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none; |
| 5166 | 5205 | |
| 5167 | 5206 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { |
| 5168 | 5207 | .ref => .ref, |
| ... | ... | @@ -5195,6 +5234,12 @@ fn orelseCatchExpr( |
| 5195 | 5234 | var else_scope = block_scope.makeSubBlock(scope); |
| 5196 | 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 | 5243 | var err_val_scope: Scope.LocalVal = undefined; |
| 5199 | 5244 | const else_sub_scope = blk: { |
| 5200 | 5245 | const payload = payload_token orelse break :blk &else_scope.base; |
| ... | ... | @@ -5220,6 +5265,17 @@ fn orelseCatchExpr( |
| 5220 | 5265 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs); |
| 5221 | 5266 | if (!else_scope.endsWithNoReturn()) { |
| 5222 | 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 | 5280 | try checkUsed(parent_gz, &else_scope.base, else_sub_scope); |
| 5225 | 5281 | |
| ... | ... | @@ -5243,15 +5299,6 @@ fn orelseCatchExpr( |
| 5243 | 5299 | block, |
| 5244 | 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 | 5302 | return result; |
| 5256 | 5303 | } |
| 5257 | 5304 | |
| ... | ... | @@ -5454,9 +5501,7 @@ fn ifExpr( |
| 5454 | 5501 | block_scope.setBreakResultLoc(rl); |
| 5455 | 5502 | defer block_scope.unstack(); |
| 5456 | 5503 | |
| 5457 | | if (do_err_trace) { |
| 5458 | | block_scope.saved_err_trace_index = try parent_gz.addNode(.save_err_ret_index, node); |
| 5459 | | } |
| 5504 | const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none; |
| 5460 | 5505 | |
| 5461 | 5506 | const payload_is_ref = if (if_full.payload_token) |payload_token| |
| 5462 | 5507 | token_tags[payload_token] == .asterisk |
| ... | ... | @@ -5574,6 +5619,12 @@ fn ifExpr( |
| 5574 | 5619 | var else_scope = parent_gz.makeSubBlock(scope); |
| 5575 | 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 | 5628 | const else_node = if_full.ast.else_expr; |
| 5578 | 5629 | const else_info: struct { |
| 5579 | 5630 | src: Ast.Node.Index, |
| ... | ... | @@ -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 | 5691 | const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; |
| 5629 | 5692 | const result = try finishThenElseBlock( |
| 5630 | 5693 | parent_gz, |
| ... | ... | @@ -5641,15 +5704,6 @@ fn ifExpr( |
| 5641 | 5704 | block, |
| 5642 | 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 | 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 | 6834 | const operand = try reachableExpr(gz, scope, rl, operand_node, node); |
| 6781 | 6835 | gz.anon_name_strategy = prev_anon_name_strategy; |
| 6782 | 6836 | |
| 6837 | // TODO: This should be almost identical for every break/ret |
| 6783 | 6838 | switch (nodeMayEvalToError(tree, operand_node)) { |
| 6784 | 6839 | .never => { |
| 6785 | 6840 | // Returning a value that cannot be an error; skip error defers. |
| 6786 | 6841 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6787 | 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 | 6855 | try gz.addRet(rl, operand, node); |
| 6789 | 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 | 6893 | }; |
| 6827 | 6894 | try genDefers(&else_scope, defer_outer, scope, which_ones); |
| 6828 | 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 | 6907 | try else_scope.addRet(rl, operand, node); |
| 6830 | 6908 | |
| 6831 | 6909 | try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); |
| ... | ... | @@ -10334,7 +10412,12 @@ const GenZir = struct { |
| 10334 | 10412 | /// Keys are the raw instruction index, values are the closure_capture instruction. |
| 10335 | 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 | 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 | 10422 | const unstacked_top = std.math.maxInt(usize); |
| 10340 | 10423 | /// Call unstack before adding any new instructions to containing GenZir. |
| ... | ... | @@ -10380,7 +10463,7 @@ const GenZir = struct { |
| 10380 | 10463 | .any_defer_node = gz.any_defer_node, |
| 10381 | 10464 | .instructions = gz.instructions, |
| 10382 | 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 | |