authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-08 20:19:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-08 20:43:05-07:00
log3389890709b8470c59cadc21471e4a6c8dda48a6
tree9819644d3770e73cf350e78a7f05fe270d86019b
parent1a500b969943dc6a4c549fdcbd72659702e867a2

stage2: error return tracing handles ret better

Sema: insert an error return trace frame when appropriate when analyzing ret_load. Also optimize the instructions to avoid an unnecessary block sent to the backends. AstGen: always emit a dbg_stmt for return expressions, in between the defer instructions and the return instruction. This improves the following test case: ```zig pub fn main() !void { return foo(); } fn foo() !void { return error.Bad; } ``` The error return trace now points to the return token instead of pointing to the foo() function call, matching stage1.

2 files changed, 141 insertions(+), 53 deletions(-)

src/AstGen.zig+28
......@@ -6573,6 +6573,16 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
65736573
65746574 if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
65756575
6576 // Ensure debug line/column information is emitted for this return expression.
6577 // Then we will save the line/column so that we can emit another one that goes
6578 // "backwards" because we want to evaluate the operand, but then put the debug
6579 // info back at the return keyword for error return tracing.
6580 if (!gz.force_comptime) {
6581 try emitDbgNode(gz, node);
6582 }
6583 const ret_line = astgen.source_line - gz.decl_line;
6584 const ret_column = astgen.source_column;
6585
65766586 const defer_outer = &astgen.fn_block.?.base;
65776587
65786588 const operand_node = node_datas[node].lhs;
......@@ -6591,11 +6601,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
65916601 const defer_counts = countDefers(astgen, defer_outer, scope);
65926602 if (!defer_counts.need_err_code) {
65936603 try genDefers(gz, defer_outer, scope, .both_sans_err);
6604 try emitRetDbgStmt(gz, ret_line, ret_column);
65946605 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
65956606 return Zir.Inst.Ref.unreachable_value;
65966607 }
65976608 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
65986609 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
6610 try emitRetDbgStmt(gz, ret_line, ret_column);
65996611 _ = try gz.addUnNode(.ret_node, err_code, node);
66006612 return Zir.Inst.Ref.unreachable_value;
66016613 }
......@@ -6614,6 +6626,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66146626 .never => {
66156627 // Returning a value that cannot be an error; skip error defers.
66166628 try genDefers(gz, defer_outer, scope, .normal_only);
6629 try emitRetDbgStmt(gz, ret_line, ret_column);
66176630 try gz.addRet(rl, operand, node);
66186631 return Zir.Inst.Ref.unreachable_value;
66196632 },
......@@ -6621,6 +6634,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66216634 // Value is always an error. Emit both error defers and regular defers.
66226635 const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
66236636 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
6637 try emitRetDbgStmt(gz, ret_line, ret_column);
66246638 try gz.addRet(rl, operand, node);
66256639 return Zir.Inst.Ref.unreachable_value;
66266640 },
......@@ -6629,6 +6643,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66296643 if (!defer_counts.have_err) {
66306644 // Only regular defers; no branch needed.
66316645 try genDefers(gz, defer_outer, scope, .normal_only);
6646 try emitRetDbgStmt(gz, ret_line, ret_column);
66326647 try gz.addRet(rl, operand, node);
66336648 return Zir.Inst.Ref.unreachable_value;
66346649 }
......@@ -6642,6 +6657,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66426657 defer then_scope.unstack();
66436658
66446659 try genDefers(&then_scope, defer_outer, scope, .normal_only);
6660 try emitRetDbgStmt(gz, ret_line, ret_column);
66456661 try then_scope.addRet(rl, operand, node);
66466662
66476663 var else_scope = gz.makeSubBlock(scope);
......@@ -6651,6 +6667,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
66516667 .both = try else_scope.addUnNode(.err_union_code, result, node),
66526668 };
66536669 try genDefers(&else_scope, defer_outer, scope, which_ones);
6670 try emitRetDbgStmt(gz, ret_line, ret_column);
66546671 try else_scope.addRet(rl, operand, node);
66556672
66566673 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
......@@ -11667,3 +11684,14 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
1166711684 }
1166811685 return @intCast(u32, count);
1166911686}
11687
11688fn emitRetDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
11689 if (gz.force_comptime) return;
11690
11691 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
11692 .dbg_stmt = .{
11693 .line = line,
11694 .column = column,
11695 },
11696 } });
11697}
src/Sema.zig+113-53
......@@ -14888,10 +14888,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
1488814888 const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
1488914889 return sema.analyzeRet(block, operand, src);
1489014890 }
14891
14892 if (sema.wantErrorReturnTracing()) {
14893 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
14894 return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr);
14895 }
14896
1489114897 _ = try block.addUnOp(.ret_load, ret_ptr);
1489214898 return always_noreturn;
1489314899}
1489414900
14901fn retWithErrTracing(
14902 sema: *Sema,
14903 block: *Block,
14904 src: LazySrcLoc,
14905 is_non_err: Air.Inst.Ref,
14906 ret_tag: Air.Inst.Tag,
14907 operand: Air.Inst.Ref,
14908) CompileError!Zir.Inst.Index {
14909 const need_check = switch (is_non_err) {
14910 .bool_true => {
14911 _ = try block.addUnOp(ret_tag, operand);
14912 return always_noreturn;
14913 },
14914 .bool_false => false,
14915 else => true,
14916 };
14917 const gpa = sema.gpa;
14918 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
14919 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
14920 const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
14921 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
14922 const return_err_fn = try sema.getBuiltin(block, src, "returnError");
14923 const args: [1]Air.Inst.Ref = .{err_return_trace};
14924
14925 if (!need_check) {
14926 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14927 _ = try block.addUnOp(ret_tag, operand);
14928 return always_noreturn;
14929 }
14930
14931 var then_block = block.makeSubBlock();
14932 defer then_block.instructions.deinit(gpa);
14933 _ = try then_block.addUnOp(ret_tag, operand);
14934
14935 var else_block = block.makeSubBlock();
14936 defer else_block.instructions.deinit(gpa);
14937 _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null);
14938 _ = try else_block.addUnOp(ret_tag, operand);
14939
14940 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
14941 then_block.instructions.items.len + else_block.instructions.items.len +
14942 @typeInfo(Air.Block).Struct.fields.len + 1);
14943
14944 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
14945 .then_body_len = @intCast(u32, then_block.instructions.items.len),
14946 .else_body_len = @intCast(u32, else_block.instructions.items.len),
14947 });
14948 sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items);
14949 sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items);
14950
14951 _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
14952 .operand = is_non_err,
14953 .payload = cond_br_payload,
14954 } } });
14955
14956 return always_noreturn;
14957}
14958
14959fn wantErrorReturnTracing(sema: *Sema) bool {
14960 // TODO implement this feature in all the backends and then delete this check.
14961 const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
14962
14963 return sema.fn_ret_ty.isError() and
14964 sema.mod.comp.bin_file.options.error_return_tracing and
14965 backend_supports_error_return_tracing;
14966}
14967
1489514968fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
1489614969 assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
1489714970
......@@ -14915,8 +14988,6 @@ fn analyzeRet(
1491514988 uncasted_operand: Air.Inst.Ref,
1491614989 src: LazySrcLoc,
1491714990) CompileError!Zir.Inst.Index {
14918 const gpa = sema.gpa;
14919
1492014991 // Special case for returning an error to an inferred error set; we need to
1492114992 // add the error tag to the inferred error set of the in-scope function, so
1492214993 // that the coercion below works correctly.
......@@ -14934,65 +15005,18 @@ fn analyzeRet(
1493415005 return error.ComptimeReturn;
1493515006 }
1493615007 // We are inlining a function call; rewrite the `ret` as a `break`.
14937 try inlining.merges.results.append(gpa, operand);
15008 try inlining.merges.results.append(sema.gpa, operand);
1493815009 _ = try block.addBr(inlining.merges.block_inst, operand);
1493915010 return always_noreturn;
1494015011 }
1494115012
1494215013 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
1494315014
14944 // TODO implement this feature in all the backends and then delete this check.
14945 const backend_supports_error_return_tracing =
14946 sema.mod.comp.bin_file.options.use_llvm;
14947
14948 if (sema.fn_ret_ty.isError() and
14949 sema.mod.comp.bin_file.options.error_return_tracing and
14950 backend_supports_error_return_tracing)
14951 ret_err: {
15015 if (sema.wantErrorReturnTracing()) {
1495215016 // Avoid adding a frame to the error return trace in case the value is comptime-known
1495315017 // to be not an error.
14954 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
14955 const need_check = switch (is_non_err) {
14956 .bool_true => break :ret_err,
14957 .bool_false => false,
14958 else => true,
14959 };
14960
14961 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
14962 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
14963 const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty);
14964 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
14965 const return_err_fn = try sema.getBuiltin(block, src, "returnError");
14966 const args: [1]Air.Inst.Ref = .{err_return_trace};
14967
14968 if (!need_check) {
14969 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14970 break :ret_err;
14971 }
14972
14973 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
14974 try sema.air_instructions.append(gpa, .{
14975 .tag = .block,
14976 .data = .{ .ty_pl = .{
14977 .ty = .void_type,
14978 .payload = undefined,
14979 } },
14980 });
14981
14982 var child_block = block.makeSubBlock();
14983 defer child_block.instructions.deinit(gpa);
14984
14985 var then_block = child_block.makeSubBlock();
14986 defer then_block.instructions.deinit(gpa);
14987 _ = try then_block.addUnOp(.ret, operand);
14988
14989 var else_block = child_block.makeSubBlock();
14990 defer else_block.instructions.deinit(gpa);
14991 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null);
14992 _ = try else_block.addUnOp(.ret, operand);
14993
14994 _ = try finishCondBr(sema, block, &child_block, &then_block, &else_block, is_non_err, block_inst);
14995 return always_noreturn;
15018 const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
15019 return retWithErrTracing(sema, block, src, is_non_err, .ret, operand);
1499615020 }
1499715021
1499815022 _ = try block.addUnOp(.ret, operand);
......@@ -25474,6 +25498,27 @@ fn analyzeIsNull(
2547425498 return block.addUnOp(air_tag, operand);
2547525499}
2547625500
25501fn analyzePtrIsNonErrComptimeOnly(
25502 sema: *Sema,
25503 block: *Block,
25504 src: LazySrcLoc,
25505 operand: Air.Inst.Ref,
25506) CompileError!Air.Inst.Ref {
25507 const ptr_ty = sema.typeOf(operand);
25508 assert(ptr_ty.zigTypeTag() == .Pointer);
25509 const child_ty = ptr_ty.childType();
25510
25511 const child_tag = child_ty.zigTypeTag();
25512 if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true;
25513 if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false;
25514 assert(child_tag == .ErrorUnion);
25515
25516 _ = block;
25517 _ = src;
25518
25519 return Air.Inst.Ref.none;
25520}
25521
2547725522fn analyzeIsNonErrComptimeOnly(
2547825523 sema: *Sema,
2547925524 block: *Block,
......@@ -25572,6 +25617,21 @@ fn analyzeIsNonErr(
2557225617 }
2557325618}
2557425619
25620fn analyzePtrIsNonErr(
25621 sema: *Sema,
25622 block: *Block,
25623 src: LazySrcLoc,
25624 operand: Air.Inst.Ref,
25625) CompileError!Air.Inst.Ref {
25626 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
25627 if (result == .none) {
25628 try sema.requireRuntimeBlock(block, src, null);
25629 return block.addUnOp(.is_non_err_ptr, operand);
25630 } else {
25631 return result;
25632 }
25633}
25634
2557525635fn analyzeSlice(
2557625636 sema: *Sema,
2557725637 block: *Block,