authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-09-12 23:09:14-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-21 10:44:19-07:00
log0c3a50fe1c1370c975d7de1f2f00458b4a3ec299
tree0e282d7a4702780f75ccebba7b9e7937d4e59985
parenteda3eb1561ec9a68e692821d5de71d03e6f50d42

stage2: Do not pop error trace if result is an error

This allows for errors to be "re-thrown" by yielding any error as the result of a catch block. For example: ```zig fn errorable() !void { return error.FallingOutOfPlane; } fn foo(have_parachute: bool) !void { return errorable() catch |err| b: { if (have_parachute) { // error trace will include the call to errorable() break :b error.NoParachute; } else { return; } }; } pub fn main() !void { // Anything that returns a non-error does not pollute the error trace. try foo(true); // This error trace will still include errorable(), whose error was "re-thrown" by foo() try foo(false); } ``` This is piece (2/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574

2 files changed, 200 insertions(+), 89 deletions(-)

src/AstGen.zig+147-89
...@@ -223,6 +223,10 @@ pub const ResultLoc = union(enum) {...@@ -223,6 +223,10 @@ pub const ResultLoc = union(enum) {
223 /// The expression must generate a pointer rather than a value. For example, the left hand side223 /// The expression must generate a pointer rather than a value. For example, the left hand side
224 /// of an assignment uses this kind of result location.224 /// of an assignment uses this kind of result location.
225 ref,225 ref,
226 /// Exactly like `none`, except also indicates this is an error-handling expr (try/catch/return etc.)
227 catch_none,
228 /// Exactly like `ref`, except also indicates this is an error-handling expr (try/catch/return etc.)
229 catch_ref,
226 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.230 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
227 ty: Zir.Inst.Ref,231 ty: Zir.Inst.Ref,
228 /// Same as `ty` but for shift operands.232 /// Same as `ty` but for shift operands.
...@@ -265,7 +269,7 @@ pub const ResultLoc = union(enum) {...@@ -265,7 +269,7 @@ pub const ResultLoc = union(enum) {
265 fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {269 fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
266 switch (rl) {270 switch (rl) {
267 // In this branch there will not be any store_to_block_ptr instructions.271 // In this branch there will not be any store_to_block_ptr instructions.
268 .none, .ty, .ty_shift_operand, .coerced_ty, .ref => return .{272 .none, .catch_none, .ty, .ty_shift_operand, .coerced_ty, .ref, .catch_ref => return .{
269 .tag = .break_operand,273 .tag = .break_operand,
270 .elide_store_to_block_ptr_instructions = false,274 .elide_store_to_block_ptr_instructions = false,
271 },275 },
...@@ -838,7 +842,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -838,7 +842,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
838 const lhs = try expr(gz, scope, .none, node_datas[node].lhs);842 const lhs = try expr(gz, scope, .none, node_datas[node].lhs);
839 _ = try gz.addUnNode(.validate_deref, lhs, node);843 _ = try gz.addUnNode(.validate_deref, lhs, node);
840 switch (rl) {844 switch (rl) {
841 .ref => return lhs,845 .ref, .catch_ref => return lhs,
842 else => {846 else => {
843 const result = try gz.addUnNode(.load, lhs, node);847 const result = try gz.addUnNode(.load, lhs, node);
844 return rvalue(gz, rl, result, node);848 return rvalue(gz, rl, result, node);
...@@ -855,7 +859,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -855,7 +859,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
855 return rvalue(gz, rl, result, node);859 return rvalue(gz, rl, result, node);
856 },860 },
857 .unwrap_optional => switch (rl) {861 .unwrap_optional => switch (rl) {
858 .ref => return gz.addUnNode(862 .ref, .catch_ref => return gz.addUnNode(
859 .optional_payload_safe_ptr,863 .optional_payload_safe_ptr,
860 try expr(gz, scope, .ref, node_datas[node].lhs),864 try expr(gz, scope, .ref, node_datas[node].lhs),
861 node,865 node,
...@@ -900,7 +904,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -900,7 +904,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
900 else904 else
901 null;905 null;
902 switch (rl) {906 switch (rl) {
903 .ref => return orelseCatchExpr(907 .ref, .catch_ref => return orelseCatchExpr(
904 gz,908 gz,
905 scope,909 scope,
906 rl,910 rl,
...@@ -927,7 +931,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -927,7 +931,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
927 }931 }
928 },932 },
929 .@"orelse" => switch (rl) {933 .@"orelse" => switch (rl) {
930 .ref => return orelseCatchExpr(934 .ref, .catch_ref => return orelseCatchExpr(
931 gz,935 gz,
932 scope,936 scope,
933 rl,937 rl,
...@@ -1372,11 +1376,11 @@ fn arrayInitExpr(...@@ -1372,11 +1376,11 @@ fn arrayInitExpr(
1372 }1376 }
1373 return Zir.Inst.Ref.void_value;1377 return Zir.Inst.Ref.void_value;
1374 },1378 },
1375 .ref => {1379 .ref, .catch_ref => {
1376 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;1380 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;
1377 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);1381 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1378 },1382 },
1379 .none => {1383 .none, .catch_none => {
1380 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;1384 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
1381 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);1385 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1382 },1386 },
...@@ -1608,7 +1612,7 @@ fn structInitExpr(...@@ -1608,7 +1612,7 @@ fn structInitExpr(
1608 }1612 }
1609 return Zir.Inst.Ref.void_value;1613 return Zir.Inst.Ref.void_value;
1610 },1614 },
1611 .ref => {1615 .ref, .catch_ref => {
1612 if (struct_init.ast.type_expr != 0) {1616 if (struct_init.ast.type_expr != 0) {
1613 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);1617 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1614 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);1618 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
...@@ -1617,7 +1621,7 @@ fn structInitExpr(...@@ -1617,7 +1621,7 @@ fn structInitExpr(
1617 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);1621 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);
1618 }1622 }
1619 },1623 },
1620 .none => {1624 .none, .catch_none => {
1621 if (struct_init.ast.type_expr != 0) {1625 if (struct_init.ast.type_expr != 0) {
1622 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);1626 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1623 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);1627 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
...@@ -1891,15 +1895,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1891,15 +1895,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18911895
1892 // As our last action before the break, "pop" the error trace if needed1896 // As our last action before the break, "pop" the error trace if needed
1893 if (err_trace_index_to_restore != .none) {1897 if (err_trace_index_to_restore != .none) {
1894 // TODO: error-liveness and is_non_err1898 // void is a non-error so we always pop - no need to call `popErrorReturnTrace`
18951899 _ = try parent_gz.addUnNode(.restore_err_ret_index, err_trace_index_to_restore, node);
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 }1900 }
19041901
1905 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);1902 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
...@@ -1914,15 +1911,15 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1914,15 +1911,15 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
19141911
1915 // As our last action before the break, "pop" the error trace if needed1912 // As our last action before the break, "pop" the error trace if needed
1916 if (err_trace_index_to_restore != .none) {1913 if (err_trace_index_to_restore != .none) {
1917 // TODO: error-liveness and is_non_err1914 // Pop the error trace, unless the operand is an error and breaking to an error-handling expr.
19181915 try popErrorReturnTrace(
1919 _ = try parent_gz.add(.{1916 parent_gz,
1920 .tag = .restore_err_ret_index,1917 scope,
1921 .data = .{ .un_node = .{1918 block_gz.break_result_loc,
1922 .operand = err_trace_index_to_restore,1919 rhs,
1923 .src_node = parent_gz.nodeIndexToRelative(node),1920 operand,
1924 } },1921 err_trace_index_to_restore,
1925 });1922 );
1926 }1923 }
19271924
1928 switch (block_gz.break_result_loc) {1925 switch (block_gz.break_result_loc) {
...@@ -2177,7 +2174,7 @@ fn labeledBlockExpr(...@@ -2177,7 +2174,7 @@ fn labeledBlockExpr(
2177 try block_scope.setBlockBody(block_inst);2174 try block_scope.setBlockBody(block_inst);
2178 const block_ref = indexToRef(block_inst);2175 const block_ref = indexToRef(block_inst);
2179 switch (rl) {2176 switch (rl) {
2180 .ref => return block_ref,2177 .ref, .catch_ref => return block_ref,
2181 else => return rvalue(gz, rl, block_ref, block_node),2178 else => return rvalue(gz, rl, block_ref, block_node),
2182 }2179 }
2183 },2180 },
...@@ -5141,14 +5138,14 @@ fn tryExpr(...@@ -5141,14 +5138,14 @@ fn tryExpr(
5141 const try_column = astgen.source_column;5138 const try_column = astgen.source_column;
51425139
5143 const operand_rl: ResultLoc = switch (rl) {5140 const operand_rl: ResultLoc = switch (rl) {
5144 .ref => .ref,5141 .ref, .catch_ref => .catch_ref,
5145 else => .none,5142 else => .catch_none,
5146 };5143 };
5147 // This could be a pointer or value depending on the `rl` parameter.5144 // This could be a pointer or value depending on the `rl` parameter.
5148 const operand = try reachableExpr(parent_gz, scope, operand_rl, operand_node, node);5145 const operand = try reachableExpr(parent_gz, scope, operand_rl, operand_node, node);
5149 const is_inline = parent_gz.force_comptime;5146 const is_inline = parent_gz.force_comptime;
5150 const is_inline_bit = @as(u2, @boolToInt(is_inline));5147 const is_inline_bit = @as(u2, @boolToInt(is_inline));
5151 const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;5148 const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref or operand_rl == .catch_ref)) << 1;
5152 const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {5149 const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
5153 0b00 => .@"try",5150 0b00 => .@"try",
5154 0b01 => .@"try",5151 0b01 => .@"try",
...@@ -5164,7 +5161,7 @@ fn tryExpr(...@@ -5164,7 +5161,7 @@ fn tryExpr(
5164 defer else_scope.unstack();5161 defer else_scope.unstack();
51655162
5166 const err_tag = switch (rl) {5163 const err_tag = switch (rl) {
5167 .ref => Zir.Inst.Tag.err_union_code_ptr,5164 .ref, .catch_ref => Zir.Inst.Tag.err_union_code_ptr,
5168 else => Zir.Inst.Tag.err_union_code,5165 else => Zir.Inst.Tag.err_union_code,
5169 };5166 };
5170 const err_code = try else_scope.addUnNode(err_tag, operand, node);5167 const err_code = try else_scope.addUnNode(err_tag, operand, node);
...@@ -5175,11 +5172,86 @@ fn tryExpr(...@@ -5175,11 +5172,86 @@ fn tryExpr(
5175 try else_scope.setTryBody(try_inst, operand);5172 try else_scope.setTryBody(try_inst, operand);
5176 const result = indexToRef(try_inst);5173 const result = indexToRef(try_inst);
5177 switch (rl) {5174 switch (rl) {
5178 .ref => return result,5175 .ref, .catch_ref => return result,
5179 else => return rvalue(parent_gz, rl, result, node),5176 else => return rvalue(parent_gz, rl, result, node),
5180 }5177 }
5181}5178}
51825179
5180/// Pops the error return trace, unless:
5181/// 1. the result is a non-error, AND
5182/// 2. the result location corresponds to an error-handling expression
5183///
5184/// For reference, the full list of error-handling expressions is:
5185/// - try X
5186/// - X catch ...
5187/// - if (X) |_| { ... } |_| { ... }
5188/// - return X
5189///
5190fn popErrorReturnTrace(
5191 gz: *GenZir,
5192 scope: *Scope,
5193 rl: ResultLoc,
5194 node: Ast.Node.Index,
5195 result_inst: Zir.Inst.Ref,
5196 error_trace_index: Zir.Inst.Ref,
5197) InnerError!void {
5198 const astgen = gz.astgen;
5199 const tree = astgen.tree;
5200
5201 const result_is_err = nodeMayEvalToError(tree, node);
5202
5203 // If we are breaking to a try/catch/error-union-if/return, the error trace propagates.
5204 const propagate_error_trace = switch (rl) {
5205 .catch_none, .catch_ref => true, // Propagate to try/catch/error-union-if
5206 .ptr, .ty => |ref| b: { // Otherwise, propagate if result loc is a return
5207 const inst = refToIndex(ref) orelse break :b false;
5208 const zir_tags = astgen.instructions.items(.tag);
5209 break :b zir_tags[inst] == .ret_ptr or zir_tags[inst] == .ret_type;
5210 },
5211 else => false,
5212 };
5213
5214 if (result_is_err == .never or !propagate_error_trace) {
5215 // We are returning a non-error, or returning to a non-error-handling operator.
5216 // In either case, we need to pop the error trace.
5217 _ = try gz.addUnNode(.restore_err_ret_index, error_trace_index, node);
5218 } else if (result_is_err == .maybe) {
5219 // We are returning to an error-handling operator with a maybe-error.
5220 // Restore only if it's a non-error, implying the catch was successfully handled.
5221 var block_scope = gz.makeSubBlock(scope);
5222 block_scope.setBreakResultLoc(.discard);
5223 defer block_scope.unstack();
5224
5225 // Emit conditional branch for restoring error trace index
5226 const is_non_err = switch (rl) {
5227 .catch_ref => try block_scope.addUnNode(.is_non_err_ptr, result_inst, node),
5228 .ptr => |ptr| try block_scope.addUnNode(.is_non_err_ptr, ptr, node),
5229 .ty, .catch_none => try block_scope.addUnNode(.is_non_err, result_inst, node),
5230 else => unreachable, // Error-handling operators only generate the above result locations
5231 };
5232 const condbr = try block_scope.addCondBr(.condbr, node);
5233
5234 const block = try gz.makeBlockInst(.block, node);
5235 try block_scope.setBlockBody(block);
5236 // block_scope unstacked now, can add new instructions to gz
5237
5238 try gz.instructions.append(astgen.gpa, block);
5239
5240 var then_scope = block_scope.makeSubBlock(scope);
5241 defer then_scope.unstack();
5242
5243 _ = try then_scope.addUnNode(.restore_err_ret_index, error_trace_index, node);
5244 const then_break = try then_scope.makeBreak(.@"break", block, .void_value);
5245
5246 var else_scope = block_scope.makeSubBlock(scope);
5247 defer else_scope.unstack();
5248
5249 const else_break = try else_scope.makeBreak(.@"break", block, .void_value);
5250
5251 try setCondBrPayload(condbr, is_non_err, &then_scope, then_break, &else_scope, else_break);
5252 }
5253}
5254
5183fn orelseCatchExpr(5255fn orelseCatchExpr(
5184 parent_gz: *GenZir,5256 parent_gz: *GenZir,
5185 scope: *Scope,5257 scope: *Scope,
...@@ -5204,8 +5276,8 @@ fn orelseCatchExpr(...@@ -5204,8 +5276,8 @@ fn orelseCatchExpr(
5204 const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none;5276 const saved_err_trace_index = if (do_err_trace) try parent_gz.addNode(.save_err_ret_index, node) else .none;
52055277
5206 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {5278 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
5207 .ref => .ref,5279 .ref, .catch_ref => if (do_err_trace) ResultLoc{ .catch_ref = {} } else .ref,
5208 else => .none,5280 else => if (do_err_trace) ResultLoc{ .catch_none = {} } else .none,
5209 };5281 };
5210 block_scope.break_count += 1;5282 block_scope.break_count += 1;
5211 // This could be a pointer or value depending on the `operand_rl` parameter.5283 // This could be a pointer or value depending on the `operand_rl` parameter.
...@@ -5227,7 +5299,7 @@ fn orelseCatchExpr(...@@ -5227,7 +5299,7 @@ fn orelseCatchExpr(
5227 // This could be a pointer or value depending on `unwrap_op`.5299 // This could be a pointer or value depending on `unwrap_op`.
5228 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);5300 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
5229 const then_result = switch (rl) {5301 const then_result = switch (rl) {
5230 .ref => unwrapped_payload,5302 .ref, .catch_ref => unwrapped_payload,
5231 else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),5303 else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
5232 };5304 };
52335305
...@@ -5266,15 +5338,15 @@ fn orelseCatchExpr(...@@ -5266,15 +5338,15 @@ fn orelseCatchExpr(
5266 if (!else_scope.endsWithNoReturn()) {5338 if (!else_scope.endsWithNoReturn()) {
5267 block_scope.break_count += 1;5339 block_scope.break_count += 1;
52685340
5269 // TODO: Add is_non_err and break check
5270 if (do_err_trace) {5341 if (do_err_trace) {
5271 _ = try else_scope.add(.{5342 try popErrorReturnTrace(
5272 .tag = .restore_err_ret_index,5343 &else_scope,
5273 .data = .{ .un_node = .{5344 else_sub_scope,
5274 .operand = saved_err_trace_index,5345 block_scope.break_result_loc,
5275 .src_node = parent_gz.nodeIndexToRelative(node),5346 rhs,
5276 } },5347 else_result,
5277 });5348 saved_err_trace_index,
5349 );
5278 }5350 }
5279 }5351 }
5280 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);5352 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
...@@ -5351,7 +5423,7 @@ fn finishThenElseBlock(...@@ -5351,7 +5423,7 @@ fn finishThenElseBlock(
5351 }5423 }
5352 const block_ref = indexToRef(main_block);5424 const block_ref = indexToRef(main_block);
5353 switch (rl) {5425 switch (rl) {
5354 .ref => return block_ref,5426 .ref, .catch_ref => return block_ref,
5355 else => return rvalue(parent_gz, rl, block_ref, node),5427 else => return rvalue(parent_gz, rl, block_ref, node),
5356 }5428 }
5357 },5429 },
...@@ -5375,7 +5447,7 @@ fn fieldAccess(...@@ -5375,7 +5447,7 @@ fn fieldAccess(
5375 node: Ast.Node.Index,5447 node: Ast.Node.Index,
5376) InnerError!Zir.Inst.Ref {5448) InnerError!Zir.Inst.Ref {
5377 switch (rl) {5449 switch (rl) {
5378 .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),5450 .ref, .catch_ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
5379 else => {5451 else => {
5380 const access = try addFieldAccess(.field_val, gz, scope, .none, node);5452 const access = try addFieldAccess(.field_val, gz, scope, .none, node);
5381 return rvalue(gz, rl, access, node);5453 return rvalue(gz, rl, access, node);
...@@ -5416,7 +5488,7 @@ fn arrayAccess(...@@ -5416,7 +5488,7 @@ fn arrayAccess(
5416 const tree = astgen.tree;5488 const tree = astgen.tree;
5417 const node_datas = tree.nodes.items(.data);5489 const node_datas = tree.nodes.items(.data);
5418 switch (rl) {5490 switch (rl) {
5419 .ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{5491 .ref, .catch_ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{
5420 .lhs = try expr(gz, scope, .ref, node_datas[node].lhs),5492 .lhs = try expr(gz, scope, .ref, node_datas[node].lhs),
5421 .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),5493 .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
5422 }),5494 }),
...@@ -5514,7 +5586,7 @@ fn ifExpr(...@@ -5514,7 +5586,7 @@ fn ifExpr(
5514 bool_bit: Zir.Inst.Ref,5586 bool_bit: Zir.Inst.Ref,
5515 } = c: {5587 } = c: {
5516 if (if_full.error_token) |_| {5588 if (if_full.error_token) |_| {
5517 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;5589 const cond_rl: ResultLoc = if (payload_is_ref) .catch_ref else .catch_none;
5518 const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);5590 const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
5519 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;5591 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
5520 break :c .{5592 break :c .{
...@@ -5660,6 +5732,17 @@ fn ifExpr(...@@ -5660,6 +5732,17 @@ fn ifExpr(
5660 const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);5732 const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);
5661 if (!else_scope.endsWithNoReturn()) {5733 if (!else_scope.endsWithNoReturn()) {
5662 block_scope.break_count += 1;5734 block_scope.break_count += 1;
5735
5736 if (do_err_trace) {
5737 try popErrorReturnTrace(
5738 &else_scope,
5739 sub_scope,
5740 block_scope.break_result_loc,
5741 else_node,
5742 e,
5743 saved_err_trace_index,
5744 );
5745 }
5663 }5746 }
5664 try checkUsed(parent_gz, &else_scope.base, sub_scope);5747 try checkUsed(parent_gz, &else_scope.base, sub_scope);
5665 try else_scope.addDbgBlockEnd();5748 try else_scope.addDbgBlockEnd();
...@@ -5676,18 +5759,6 @@ fn ifExpr(...@@ -5676,18 +5759,6 @@ fn ifExpr(
5676 },5759 },
5677 };5760 };
56785761
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
5691 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";5762 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
5692 const result = try finishThenElseBlock(5763 const result = try finishThenElseBlock(
5693 parent_gz,5764 parent_gz,
...@@ -6760,7 +6831,7 @@ fn switchExpr(...@@ -6760,7 +6831,7 @@ fn switchExpr(
6760 }6831 }
67616832
6762 const block_ref = indexToRef(switch_block);6833 const block_ref = indexToRef(switch_block);
6763 if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and rl != .ref)6834 if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and rl != .ref and rl != .catch_ref)
6764 return rvalue(parent_gz, rl, block_ref, switch_node);6835 return rvalue(parent_gz, rl, block_ref, switch_node);
6765 return block_ref;6836 return block_ref;
6766}6837}
...@@ -6839,19 +6910,12 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6839,19 +6910,12 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6839 .never => {6910 .never => {
6840 // Returning a value that cannot be an error; skip error defers.6911 // Returning a value that cannot be an error; skip error defers.
6841 try genDefers(gz, defer_outer, scope, .normal_only);6912 try genDefers(gz, defer_outer, scope, .normal_only);
6842 try emitDbgStmt(gz, ret_line, ret_column);
68436913
6844 // As our last action before the return, "pop" the error trace if needed6914 // As our last action before the return, "pop" the error trace if needed
6845 if (gz.outermost_err_trace_index != .none) {6915 if (gz.outermost_err_trace_index != .none)
6846 _ = try gz.add(.{6916 _ = try gz.addUnNode(.restore_err_ret_index, gz.outermost_err_trace_index, node);
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 }
68546917
6918 try emitDbgStmt(gz, ret_line, ret_column);
6855 try gz.addRet(rl, operand, node);6919 try gz.addRet(rl, operand, node);
6856 return Zir.Inst.Ref.unreachable_value;6920 return Zir.Inst.Ref.unreachable_value;
6857 },6921 },
...@@ -6882,6 +6946,11 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6882,6 +6946,11 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6882 defer then_scope.unstack();6946 defer then_scope.unstack();
68836947
6884 try genDefers(&then_scope, defer_outer, scope, .normal_only);6948 try genDefers(&then_scope, defer_outer, scope, .normal_only);
6949
6950 // As our last action before the return, "pop" the error trace if needed
6951 if (then_scope.outermost_err_trace_index != .none)
6952 _ = try then_scope.addUnNode(.restore_err_ret_index, then_scope.outermost_err_trace_index, node);
6953
6885 try emitDbgStmt(&then_scope, ret_line, ret_column);6954 try emitDbgStmt(&then_scope, ret_line, ret_column);
6886 try then_scope.addRet(rl, operand, node);6955 try then_scope.addRet(rl, operand, node);
68876956
...@@ -6893,17 +6962,6 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6893,17 +6962,6 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6893 };6962 };
6894 try genDefers(&else_scope, defer_outer, scope, which_ones);6963 try genDefers(&else_scope, defer_outer, scope, which_ones);
6895 try emitDbgStmt(&else_scope, ret_line, ret_column);6964 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 }
6907 try else_scope.addRet(rl, operand, node);6965 try else_scope.addRet(rl, operand, node);
69086966
6909 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);6967 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
...@@ -7068,7 +7126,7 @@ fn localVarRef(...@@ -7068,7 +7126,7 @@ fn localVarRef(
7068 );7126 );
70697127
7070 switch (rl) {7128 switch (rl) {
7071 .ref => return ptr_inst,7129 .ref, .catch_ref => return ptr_inst,
7072 else => {7130 else => {
7073 const loaded = try gz.addUnNode(.load, ptr_inst, ident);7131 const loaded = try gz.addUnNode(.load, ptr_inst, ident);
7074 return rvalue(gz, rl, loaded, ident);7132 return rvalue(gz, rl, loaded, ident);
...@@ -7105,7 +7163,7 @@ fn localVarRef(...@@ -7105,7 +7163,7 @@ fn localVarRef(
7105 // Decl references happen by name rather than ZIR index so that when unrelated7163 // Decl references happen by name rather than ZIR index so that when unrelated
7106 // decls are modified, ZIR code containing references to them can be unmodified.7164 // decls are modified, ZIR code containing references to them can be unmodified.
7107 switch (rl) {7165 switch (rl) {
7108 .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),7166 .ref, .catch_ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
7109 else => {7167 else => {
7110 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);7168 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
7111 return rvalue(gz, rl, result, ident);7169 return rvalue(gz, rl, result, ident);
...@@ -7452,7 +7510,7 @@ fn as(...@@ -7452,7 +7510,7 @@ fn as(
7452) InnerError!Zir.Inst.Ref {7510) InnerError!Zir.Inst.Ref {
7453 const dest_type = try typeExpr(gz, scope, lhs);7511 const dest_type = try typeExpr(gz, scope, lhs);
7454 switch (rl) {7512 switch (rl) {
7455 .none, .discard, .ref, .ty, .ty_shift_operand, .coerced_ty => {7513 .none, .catch_none, .discard, .ref, .catch_ref, .ty, .ty_shift_operand, .coerced_ty => {
7456 const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);7514 const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
7457 return rvalue(gz, rl, result, node);7515 return rvalue(gz, rl, result, node);
7458 },7516 },
...@@ -7652,7 +7710,7 @@ fn builtinCall(...@@ -7652,7 +7710,7 @@ fn builtinCall(
7652 return rvalue(gz, rl, result, node);7710 return rvalue(gz, rl, result, node);
7653 },7711 },
7654 .field => {7712 .field => {
7655 if (rl == .ref) {7713 if (rl == .ref or rl == .catch_ref) {
7656 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{7714 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
7657 .lhs = try expr(gz, scope, .ref, params[0]),7715 .lhs = try expr(gz, scope, .ref, params[0]),
7658 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),7716 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
...@@ -9600,13 +9658,13 @@ fn rvalue(...@@ -9600,13 +9658,13 @@ fn rvalue(
9600 };9658 };
9601 if (gz.endsWithNoReturn()) return result;9659 if (gz.endsWithNoReturn()) return result;
9602 switch (rl) {9660 switch (rl) {
9603 .none, .coerced_ty => return result,9661 .none, .catch_none, .coerced_ty => return result,
9604 .discard => {9662 .discard => {
9605 // Emit a compile error for discarding error values.9663 // Emit a compile error for discarding error values.
9606 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);9664 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
9607 return result;9665 return result;
9608 },9666 },
9609 .ref => {9667 .ref, .catch_ref => {
9610 // We need a pointer but we have a value.9668 // We need a pointer but we have a value.
9611 // Unfortunately it's not quite as simple as directly emitting a ref9669 // Unfortunately it's not quite as simple as directly emitting a ref
9612 // instruction here because we need subsequent address-of operator on9670 // instruction here because we need subsequent address-of operator on
...@@ -10575,7 +10633,7 @@ const GenZir = struct {...@@ -10575,7 +10633,7 @@ const GenZir = struct {
10575 gz.break_result_loc = parent_rl;10633 gz.break_result_loc = parent_rl;
10576 },10634 },
1057710635
10578 .discard, .none, .ref => {10636 .discard, .none, .catch_none, .ref, .catch_ref => {
10579 gz.rl_ty_inst = .none;10637 gz.rl_ty_inst = .none;
10580 gz.break_result_loc = parent_rl;10638 gz.break_result_loc = parent_rl;
10581 },10639 },
test/stack_traces.zig+53
...@@ -155,6 +155,59 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -155,6 +155,59 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
155 },155 },
156 });156 });
157157
158 cases.addCase(.{
159 .name = "catch and re-throw error",
160 .source =
161 \\fn foo() !void {
162 \\ return error.TheSkyIsFalling;
163 \\}
164 \\
165 \\pub fn main() !void {
166 \\ return foo() catch error.AndMyCarIsOutOfGas;
167 \\}
168 ,
169 .Debug = .{
170 .expect =
171 \\error: AndMyCarIsOutOfGas
172 \\source.zig:2:5: [address] in foo (test)
173 \\ return error.TheSkyIsFalling;
174 \\ ^
175 \\source.zig:6:5: [address] in main (test)
176 \\ return foo() catch error.AndMyCarIsOutOfGas;
177 \\ ^
178 \\
179 ,
180 },
181 .ReleaseSafe = .{
182 .exclude_os = .{
183 .windows, // TODO
184 .linux, // defeated by aggressive inlining
185 },
186 .expect =
187 \\error: AndMyCarIsOutOfGas
188 \\source.zig:2:5: [address] in [function]
189 \\ return error.TheSkyIsFalling;
190 \\ ^
191 \\source.zig:6:5: [address] in [function]
192 \\ return foo() catch error.AndMyCarIsOutOfGas;
193 \\ ^
194 \\
195 ,
196 },
197 .ReleaseFast = .{
198 .expect =
199 \\error: AndMyCarIsOutOfGas
200 \\
201 ,
202 },
203 .ReleaseSmall = .{
204 .expect =
205 \\error: AndMyCarIsOutOfGas
206 \\
207 ,
208 },
209 });
210
158 cases.addCase(.{211 cases.addCase(.{
159 .name = "try return from within catch",212 .name = "try return from within catch",
160 .source = 213 .source =