| author | |
| committer | |
| log | 85a3f9b0544c1b90a77ce26f99f9fc26116d756b |
| tree | a42093d83336a1dbbf90447154ec9e83dd2dcebf |
| parent | c76b5c1a31ebe09726cd29e7a6da69613eabfd10 |
| parent | feb90f6ed4f64eb0faef8fbbcf8c51fad5392b39 |
| signature |
several improvements to error return tracing in the self-hosted compiler4 files changed, 193 insertions(+), 24 deletions(-)
lib/std/builtin.zig+1-2| ... | @@ -867,10 +867,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn { | ... | @@ -867,10 +867,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn { |
| 867 | std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len }); | 867 | std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len }); |
| 868 | } | 868 | } |
| 869 | 869 | ||
| 870 | pub noinline fn returnError(maybe_st: ?*StackTrace) void { | 870 | pub noinline fn returnError(st: *StackTrace) void { |
| 871 | @setCold(true); | 871 | @setCold(true); |
| 872 | @setRuntimeSafety(false); | 872 | @setRuntimeSafety(false); |
| 873 | const st = maybe_st orelse return; | ||
| 874 | addErrRetTraceAddr(st, @returnAddress()); | 873 | addErrRetTraceAddr(st, @returnAddress()); |
| 875 | } | 874 | } |
| 876 | 875 |
src/AstGen.zig+52| ... | @@ -3070,6 +3070,19 @@ fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { | ... | @@ -3070,6 +3070,19 @@ fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { |
| 3070 | const line = astgen.source_line - gz.decl_line; | 3070 | const line = astgen.source_line - gz.decl_line; |
| 3071 | const column = astgen.source_column; | 3071 | const column = astgen.source_column; |
| 3072 | 3072 | ||
| 3073 | if (gz.instructions.items.len > 0) { | ||
| 3074 | const last = gz.instructions.items[gz.instructions.items.len - 1]; | ||
| 3075 | const zir_tags = astgen.instructions.items(.tag); | ||
| 3076 | if (zir_tags[last] == .dbg_stmt) { | ||
| 3077 | const zir_datas = astgen.instructions.items(.data); | ||
| 3078 | zir_datas[last].dbg_stmt = .{ | ||
| 3079 | .line = line, | ||
| 3080 | .column = column, | ||
| 3081 | }; | ||
| 3082 | return; | ||
| 3083 | } | ||
| 3084 | } | ||
| 3085 | |||
| 3073 | _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ | 3086 | _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ |
| 3074 | .dbg_stmt = .{ | 3087 | .dbg_stmt = .{ |
| 3075 | .line = line, | 3088 | .line = line, |
| ... | @@ -5050,6 +5063,16 @@ fn tryExpr( | ... | @@ -5050,6 +5063,16 @@ fn tryExpr( |
| 5050 | 5063 | ||
| 5051 | if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); | 5064 | if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); |
| 5052 | 5065 | ||
| 5066 | // Ensure debug line/column information is emitted for this try expression. | ||
| 5067 | // Then we will save the line/column so that we can emit another one that goes | ||
| 5068 | // "backwards" because we want to evaluate the operand, but then put the debug | ||
| 5069 | // info back at the try keyword for error return tracing. | ||
| 5070 | if (!parent_gz.force_comptime) { | ||
| 5071 | try emitDbgNode(parent_gz, node); | ||
| 5072 | } | ||
| 5073 | const try_line = astgen.source_line - parent_gz.decl_line; | ||
| 5074 | const try_column = astgen.source_column; | ||
| 5075 | |||
| 5053 | const operand_rl: ResultLoc = switch (rl) { | 5076 | const operand_rl: ResultLoc = switch (rl) { |
| 5054 | .ref => .ref, | 5077 | .ref => .ref, |
| 5055 | else => .none, | 5078 | else => .none, |
| ... | @@ -5079,6 +5102,7 @@ fn tryExpr( | ... | @@ -5079,6 +5102,7 @@ fn tryExpr( |
| 5079 | }; | 5102 | }; |
| 5080 | const err_code = try else_scope.addUnNode(err_tag, operand, node); | 5103 | const err_code = try else_scope.addUnNode(err_tag, operand, node); |
| 5081 | try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); | 5104 | try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); |
| 5105 | try emitDbgStmt(&else_scope, try_line, try_column); | ||
| 5082 | _ = try else_scope.addUnNode(.ret_node, err_code, node); | 5106 | _ = try else_scope.addUnNode(.ret_node, err_code, node); |
| 5083 | 5107 | ||
| 5084 | try else_scope.setTryBody(try_inst, operand); | 5108 | try else_scope.setTryBody(try_inst, operand); |
| ... | @@ -6585,6 +6609,16 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6585,6 +6609,16 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6585 | 6609 | ||
| 6586 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); | 6610 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); |
| 6587 | 6611 | ||
| 6612 | // Ensure debug line/column information is emitted for this return expression. | ||
| 6613 | // Then we will save the line/column so that we can emit another one that goes | ||
| 6614 | // "backwards" because we want to evaluate the operand, but then put the debug | ||
| 6615 | // info back at the return keyword for error return tracing. | ||
| 6616 | if (!gz.force_comptime) { | ||
| 6617 | try emitDbgNode(gz, node); | ||
| 6618 | } | ||
| 6619 | const ret_line = astgen.source_line - gz.decl_line; | ||
| 6620 | const ret_column = astgen.source_column; | ||
| 6621 | |||
| 6588 | const defer_outer = &astgen.fn_block.?.base; | 6622 | const defer_outer = &astgen.fn_block.?.base; |
| 6589 | 6623 | ||
| 6590 | const operand_node = node_datas[node].lhs; | 6624 | const operand_node = node_datas[node].lhs; |
| ... | @@ -6603,11 +6637,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6603,11 +6637,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6603 | const defer_counts = countDefers(astgen, defer_outer, scope); | 6637 | const defer_counts = countDefers(astgen, defer_outer, scope); |
| 6604 | if (!defer_counts.need_err_code) { | 6638 | if (!defer_counts.need_err_code) { |
| 6605 | try genDefers(gz, defer_outer, scope, .both_sans_err); | 6639 | try genDefers(gz, defer_outer, scope, .both_sans_err); |
| 6640 | try emitDbgStmt(gz, ret_line, ret_column); | ||
| 6606 | _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); | 6641 | _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); |
| 6607 | return Zir.Inst.Ref.unreachable_value; | 6642 | return Zir.Inst.Ref.unreachable_value; |
| 6608 | } | 6643 | } |
| 6609 | const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); | 6644 | const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); |
| 6610 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); | 6645 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); |
| 6646 | try emitDbgStmt(gz, ret_line, ret_column); | ||
| 6611 | _ = try gz.addUnNode(.ret_node, err_code, node); | 6647 | _ = try gz.addUnNode(.ret_node, err_code, node); |
| 6612 | return Zir.Inst.Ref.unreachable_value; | 6648 | return Zir.Inst.Ref.unreachable_value; |
| 6613 | } | 6649 | } |
| ... | @@ -6626,6 +6662,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6626,6 +6662,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6626 | .never => { | 6662 | .never => { |
| 6627 | // Returning a value that cannot be an error; skip error defers. | 6663 | // Returning a value that cannot be an error; skip error defers. |
| 6628 | try genDefers(gz, defer_outer, scope, .normal_only); | 6664 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6665 | try emitDbgStmt(gz, ret_line, ret_column); | ||
| 6629 | try gz.addRet(rl, operand, node); | 6666 | try gz.addRet(rl, operand, node); |
| 6630 | return Zir.Inst.Ref.unreachable_value; | 6667 | return Zir.Inst.Ref.unreachable_value; |
| 6631 | }, | 6668 | }, |
| ... | @@ -6633,6 +6670,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6633,6 +6670,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6633 | // Value is always an error. Emit both error defers and regular defers. | 6670 | // Value is always an error. Emit both error defers and regular defers. |
| 6634 | const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; | 6671 | const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; |
| 6635 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); | 6672 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); |
| 6673 | try emitDbgStmt(gz, ret_line, ret_column); | ||
| 6636 | try gz.addRet(rl, operand, node); | 6674 | try gz.addRet(rl, operand, node); |
| 6637 | return Zir.Inst.Ref.unreachable_value; | 6675 | return Zir.Inst.Ref.unreachable_value; |
| 6638 | }, | 6676 | }, |
| ... | @@ -6641,6 +6679,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6641,6 +6679,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6641 | if (!defer_counts.have_err) { | 6679 | if (!defer_counts.have_err) { |
| 6642 | // Only regular defers; no branch needed. | 6680 | // Only regular defers; no branch needed. |
| 6643 | try genDefers(gz, defer_outer, scope, .normal_only); | 6681 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6682 | try emitDbgStmt(gz, ret_line, ret_column); | ||
| 6644 | try gz.addRet(rl, operand, node); | 6683 | try gz.addRet(rl, operand, node); |
| 6645 | return Zir.Inst.Ref.unreachable_value; | 6684 | return Zir.Inst.Ref.unreachable_value; |
| 6646 | } | 6685 | } |
| ... | @@ -6654,6 +6693,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6654,6 +6693,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6654 | defer then_scope.unstack(); | 6693 | defer then_scope.unstack(); |
| 6655 | 6694 | ||
| 6656 | try genDefers(&then_scope, defer_outer, scope, .normal_only); | 6695 | try genDefers(&then_scope, defer_outer, scope, .normal_only); |
| 6696 | try emitDbgStmt(&then_scope, ret_line, ret_column); | ||
| 6657 | try then_scope.addRet(rl, operand, node); | 6697 | try then_scope.addRet(rl, operand, node); |
| 6658 | 6698 | ||
| 6659 | var else_scope = gz.makeSubBlock(scope); | 6699 | var else_scope = gz.makeSubBlock(scope); |
| ... | @@ -6663,6 +6703,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6663,6 +6703,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6663 | .both = try else_scope.addUnNode(.err_union_code, result, node), | 6703 | .both = try else_scope.addUnNode(.err_union_code, result, node), |
| 6664 | }; | 6704 | }; |
| 6665 | try genDefers(&else_scope, defer_outer, scope, which_ones); | 6705 | try genDefers(&else_scope, defer_outer, scope, which_ones); |
| 6706 | try emitDbgStmt(&else_scope, ret_line, ret_column); | ||
| 6666 | try else_scope.addRet(rl, operand, node); | 6707 | try else_scope.addRet(rl, operand, node); |
| 6667 | 6708 | ||
| 6668 | try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); | 6709 | try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); |
| ... | @@ -11702,3 +11743,14 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { | ... | @@ -11702,3 +11743,14 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { |
| 11702 | } | 11743 | } |
| 11703 | return @intCast(u32, count); | 11744 | return @intCast(u32, count); |
| 11704 | } | 11745 | } |
| 11746 | |||
| 11747 | fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { | ||
| 11748 | if (gz.force_comptime) return; | ||
| 11749 | |||
| 11750 | _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ | ||
| 11751 | .dbg_stmt = .{ | ||
| 11752 | .line = line, | ||
| 11753 | .column = column, | ||
| 11754 | }, | ||
| 11755 | } }); | ||
| 11756 | } |
src/Sema.zig+139-22| ... | @@ -14489,6 +14489,20 @@ fn zirBoolBr( | ... | @@ -14489,6 +14489,20 @@ fn zirBoolBr( |
| 14489 | const rhs_result = try sema.resolveBody(rhs_block, body, inst); | 14489 | const rhs_result = try sema.resolveBody(rhs_block, body, inst); |
| 14490 | _ = try rhs_block.addBr(block_inst, rhs_result); | 14490 | _ = try rhs_block.addBr(block_inst, rhs_result); |
| 14491 | 14491 | ||
| 14492 | return finishCondBr(sema, parent_block, &child_block, &then_block, &else_block, lhs, block_inst); | ||
| 14493 | } | ||
| 14494 | |||
| 14495 | fn finishCondBr( | ||
| 14496 | sema: *Sema, | ||
| 14497 | parent_block: *Block, | ||
| 14498 | child_block: *Block, | ||
| 14499 | then_block: *Block, | ||
| 14500 | else_block: *Block, | ||
| 14501 | cond: Air.Inst.Ref, | ||
| 14502 | block_inst: Air.Inst.Index, | ||
| 14503 | ) !Air.Inst.Ref { | ||
| 14504 | const gpa = sema.gpa; | ||
| 14505 | |||
| 14492 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + | 14506 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + |
| 14493 | then_block.instructions.items.len + else_block.instructions.items.len + | 14507 | then_block.instructions.items.len + else_block.instructions.items.len + |
| 14494 | @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); | 14508 | @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); |
| ... | @@ -14501,7 +14515,7 @@ fn zirBoolBr( | ... | @@ -14501,7 +14515,7 @@ fn zirBoolBr( |
| 14501 | sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); | 14515 | sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); |
| 14502 | 14516 | ||
| 14503 | _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ | 14517 | _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ |
| 14504 | .operand = lhs, | 14518 | .operand = cond, |
| 14505 | .payload = cond_br_payload, | 14519 | .payload = cond_br_payload, |
| 14506 | } } }); | 14520 | } } }); |
| 14507 | 14521 | ||
| ... | @@ -14871,10 +14885,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir | ... | @@ -14871,10 +14885,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir |
| 14871 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); | 14885 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); |
| 14872 | return sema.analyzeRet(block, operand, src); | 14886 | return sema.analyzeRet(block, operand, src); |
| 14873 | } | 14887 | } |
| 14888 | |||
| 14889 | if (sema.wantErrorReturnTracing()) { | ||
| 14890 | const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); | ||
| 14891 | return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr); | ||
| 14892 | } | ||
| 14893 | |||
| 14874 | _ = try block.addUnOp(.ret_load, ret_ptr); | 14894 | _ = try block.addUnOp(.ret_load, ret_ptr); |
| 14875 | return always_noreturn; | 14895 | return always_noreturn; |
| 14876 | } | 14896 | } |
| 14877 | 14897 | ||
| 14898 | fn retWithErrTracing( | ||
| 14899 | sema: *Sema, | ||
| 14900 | block: *Block, | ||
| 14901 | src: LazySrcLoc, | ||
| 14902 | is_non_err: Air.Inst.Ref, | ||
| 14903 | ret_tag: Air.Inst.Tag, | ||
| 14904 | operand: Air.Inst.Ref, | ||
| 14905 | ) CompileError!Zir.Inst.Index { | ||
| 14906 | const need_check = switch (is_non_err) { | ||
| 14907 | .bool_true => { | ||
| 14908 | _ = try block.addUnOp(ret_tag, operand); | ||
| 14909 | return always_noreturn; | ||
| 14910 | }, | ||
| 14911 | .bool_false => false, | ||
| 14912 | else => true, | ||
| 14913 | }; | ||
| 14914 | const gpa = sema.gpa; | ||
| 14915 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); | ||
| 14916 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | ||
| 14917 | const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); | ||
| 14918 | const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); | ||
| 14919 | const return_err_fn = try sema.getBuiltin(block, src, "returnError"); | ||
| 14920 | const args: [1]Air.Inst.Ref = .{err_return_trace}; | ||
| 14921 | |||
| 14922 | if (!need_check) { | ||
| 14923 | _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); | ||
| 14924 | _ = try block.addUnOp(ret_tag, operand); | ||
| 14925 | return always_noreturn; | ||
| 14926 | } | ||
| 14927 | |||
| 14928 | var then_block = block.makeSubBlock(); | ||
| 14929 | defer then_block.instructions.deinit(gpa); | ||
| 14930 | _ = try then_block.addUnOp(ret_tag, operand); | ||
| 14931 | |||
| 14932 | var else_block = block.makeSubBlock(); | ||
| 14933 | defer else_block.instructions.deinit(gpa); | ||
| 14934 | _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null); | ||
| 14935 | _ = try else_block.addUnOp(ret_tag, operand); | ||
| 14936 | |||
| 14937 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + | ||
| 14938 | then_block.instructions.items.len + else_block.instructions.items.len + | ||
| 14939 | @typeInfo(Air.Block).Struct.fields.len + 1); | ||
| 14940 | |||
| 14941 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ | ||
| 14942 | .then_body_len = @intCast(u32, then_block.instructions.items.len), | ||
| 14943 | .else_body_len = @intCast(u32, else_block.instructions.items.len), | ||
| 14944 | }); | ||
| 14945 | sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); | ||
| 14946 | sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); | ||
| 14947 | |||
| 14948 | _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ | ||
| 14949 | .operand = is_non_err, | ||
| 14950 | .payload = cond_br_payload, | ||
| 14951 | } } }); | ||
| 14952 | |||
| 14953 | return always_noreturn; | ||
| 14954 | } | ||
| 14955 | |||
| 14956 | fn wantErrorReturnTracing(sema: *Sema) bool { | ||
| 14957 | // TODO implement this feature in all the backends and then delete this check. | ||
| 14958 | const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; | ||
| 14959 | |||
| 14960 | return sema.fn_ret_ty.isError() and | ||
| 14961 | sema.mod.comp.bin_file.options.error_return_tracing and | ||
| 14962 | backend_supports_error_return_tracing; | ||
| 14963 | } | ||
| 14964 | |||
| 14878 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { | 14965 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { |
| 14879 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); | 14966 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); |
| 14880 | 14967 | ||
| ... | @@ -14920,27 +15007,15 @@ fn analyzeRet( | ... | @@ -14920,27 +15007,15 @@ fn analyzeRet( |
| 14920 | return always_noreturn; | 15007 | return always_noreturn; |
| 14921 | } | 15008 | } |
| 14922 | 15009 | ||
| 14923 | // TODO implement this feature in all the backends and then delete this check. | 15010 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); |
| 14924 | const backend_supports_error_return_tracing = | ||
| 14925 | sema.mod.comp.bin_file.options.use_llvm; | ||
| 14926 | 15011 | ||
| 14927 | if (sema.fn_ret_ty.isError() and | 15012 | if (sema.wantErrorReturnTracing()) { |
| 14928 | sema.mod.comp.bin_file.options.error_return_tracing and | 15013 | // Avoid adding a frame to the error return trace in case the value is comptime-known |
| 14929 | backend_supports_error_return_tracing) | 15014 | // to be not an error. |
| 14930 | ret_err: { | 15015 | const is_non_err = try sema.analyzeIsNonErr(block, src, operand); |
| 14931 | if (try sema.resolveMaybeUndefVal(block, src, operand)) |ret_val| { | 15016 | return retWithErrTracing(sema, block, src, is_non_err, .ret, operand); |
| 14932 | if (ret_val.tag() != .@"error") break :ret_err; | ||
| 14933 | } | ||
| 14934 | const return_err_fn = try sema.getBuiltin(block, src, "returnError"); | ||
| 14935 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); | ||
| 14936 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | ||
| 14937 | const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); | ||
| 14938 | const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); | ||
| 14939 | const args: [1]Air.Inst.Ref = .{err_return_trace}; | ||
| 14940 | _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); | ||
| 14941 | } | 15017 | } |
| 14942 | 15018 | ||
| 14943 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); | ||
| 14944 | _ = try block.addUnOp(.ret, operand); | 15019 | _ = try block.addUnOp(.ret, operand); |
| 14945 | return always_noreturn; | 15020 | return always_noreturn; |
| 14946 | } | 15021 | } |
| ... | @@ -25418,6 +25493,27 @@ fn analyzeIsNull( | ... | @@ -25418,6 +25493,27 @@ fn analyzeIsNull( |
| 25418 | return block.addUnOp(air_tag, operand); | 25493 | return block.addUnOp(air_tag, operand); |
| 25419 | } | 25494 | } |
| 25420 | 25495 | ||
| 25496 | fn analyzePtrIsNonErrComptimeOnly( | ||
| 25497 | sema: *Sema, | ||
| 25498 | block: *Block, | ||
| 25499 | src: LazySrcLoc, | ||
| 25500 | operand: Air.Inst.Ref, | ||
| 25501 | ) CompileError!Air.Inst.Ref { | ||
| 25502 | const ptr_ty = sema.typeOf(operand); | ||
| 25503 | assert(ptr_ty.zigTypeTag() == .Pointer); | ||
| 25504 | const child_ty = ptr_ty.childType(); | ||
| 25505 | |||
| 25506 | const child_tag = child_ty.zigTypeTag(); | ||
| 25507 | if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; | ||
| 25508 | if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; | ||
| 25509 | assert(child_tag == .ErrorUnion); | ||
| 25510 | |||
| 25511 | _ = block; | ||
| 25512 | _ = src; | ||
| 25513 | |||
| 25514 | return Air.Inst.Ref.none; | ||
| 25515 | } | ||
| 25516 | |||
| 25421 | fn analyzeIsNonErrComptimeOnly( | 25517 | fn analyzeIsNonErrComptimeOnly( |
| 25422 | sema: *Sema, | 25518 | sema: *Sema, |
| 25423 | block: *Block, | 25519 | block: *Block, |
| ... | @@ -25431,10 +25527,16 @@ fn analyzeIsNonErrComptimeOnly( | ... | @@ -25431,10 +25527,16 @@ fn analyzeIsNonErrComptimeOnly( |
| 25431 | assert(ot == .ErrorUnion); | 25527 | assert(ot == .ErrorUnion); |
| 25432 | 25528 | ||
| 25433 | if (Air.refToIndex(operand)) |operand_inst| { | 25529 | if (Air.refToIndex(operand)) |operand_inst| { |
| 25434 | const air_tags = sema.air_instructions.items(.tag); | 25530 | switch (sema.air_instructions.items(.tag)[operand_inst]) { |
| 25435 | if (air_tags[operand_inst] == .wrap_errunion_payload) { | 25531 | .wrap_errunion_payload => return Air.Inst.Ref.bool_true, |
| 25436 | return Air.Inst.Ref.bool_true; | 25532 | .wrap_errunion_err => return Air.Inst.Ref.bool_false, |
| 25533 | else => {}, | ||
| 25437 | } | 25534 | } |
| 25535 | } else if (operand == .undef) { | ||
| 25536 | return sema.addConstUndef(Type.bool); | ||
| 25537 | } else { | ||
| 25538 | // None of the ref tags can be errors. | ||
| 25539 | return Air.Inst.Ref.bool_true; | ||
| 25438 | } | 25540 | } |
| 25439 | 25541 | ||
| 25440 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); | 25542 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); |
| ... | @@ -25510,6 +25612,21 @@ fn analyzeIsNonErr( | ... | @@ -25510,6 +25612,21 @@ fn analyzeIsNonErr( |
| 25510 | } | 25612 | } |
| 25511 | } | 25613 | } |
| 25512 | 25614 | ||
| 25615 | fn analyzePtrIsNonErr( | ||
| 25616 | sema: *Sema, | ||
| 25617 | block: *Block, | ||
| 25618 | src: LazySrcLoc, | ||
| 25619 | operand: Air.Inst.Ref, | ||
| 25620 | ) CompileError!Air.Inst.Ref { | ||
| 25621 | const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); | ||
| 25622 | if (result == .none) { | ||
| 25623 | try sema.requireRuntimeBlock(block, src, null); | ||
| 25624 | return block.addUnOp(.is_non_err_ptr, operand); | ||
| 25625 | } else { | ||
| 25626 | return result; | ||
| 25627 | } | ||
| 25628 | } | ||
| 25629 | |||
| 25513 | fn analyzeSlice( | 25630 | fn analyzeSlice( |
| 25514 | sema: *Sema, | 25631 | sema: *Sema, |
| 25515 | block: *Block, | 25632 | block: *Block, |
test/stack_traces.zig+1| ... | @@ -22,6 +22,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { | ... | @@ -22,6 +22,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { |
| 22 | .ReleaseSafe = .{ | 22 | .ReleaseSafe = .{ |
| 23 | .exclude_os = .{ | 23 | .exclude_os = .{ |
| 24 | .windows, // segfault | 24 | .windows, // segfault |
| 25 | .linux, // defeated by aggressive inlining | ||
| 25 | }, | 26 | }, |
| 26 | .expect = | 27 | .expect = |
| 27 | \\error: TheSkyIsFalling | 28 | \\error: TheSkyIsFalling |