diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 8004f94d7f65..0b0a87664646 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -425,6 +425,9 @@ fn callFn(comptime f: anytype, args: anytype) switch (Impl) { @call(.auto, f, args) catch |err| { std.debug.print("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { + if (builtin.zig_backend != .stage1) + trace.index -= 1; // exclude this error-handling block + std.debug.dumpStackTrace(trace.*); } }; diff --git a/lib/std/start.zig b/lib/std/start.zig index 6edebde122e8..941780feb2e9 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -532,6 +532,9 @@ inline fn initEventLoopAndCallMain() u8 { loop.init() catch |err| { std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { + if (builtin.zig_backend != .stage1) + trace.index -= 1; // exclude this error-handling block + std.debug.dumpStackTrace(trace.*); } return 1; @@ -561,6 +564,9 @@ inline fn initEventLoopAndCallWinMain() std.os.windows.INT { loop.init() catch |err| { std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { + if (builtin.zig_backend != .stage1) + trace.index -= 1; // exclude this error-handling block + std.debug.dumpStackTrace(trace.*); } return 1; @@ -617,6 +623,9 @@ pub fn callMain() u8 { const result = root.main() catch |err| { std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { + if (builtin.zig_backend != .stage1) + trace.index -= 1; // exclude this error-handling block + std.debug.dumpStackTrace(trace.*); } return 1; diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 5968fdaa5433..ec290688793b 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -78,6 +78,9 @@ pub fn main() void { fail_count += 1; progress.log("FAIL ({s})\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { + if (builtin.zig_backend != .stage1) + trace.index -= 1; // exclude this error-handling block + std.debug.dumpStackTrace(trace.*); } test_node.end(); diff --git a/src/AstGen.zig b/src/AstGen.zig index de259521bce7..406c9f5a23af 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2965,7 +2965,7 @@ fn varDecl( // The const init expression may have modified the error return trace, so signal // to Sema that it should save the new index for restoring later. if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) - _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); + _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }, false); const sub_scope = try block_arena.create(Scope.LocalVal); sub_scope.* = .{ @@ -3038,7 +3038,7 @@ fn varDecl( // The const init expression may have modified the error return trace, so signal // to Sema that it should save the new index for restoring later. if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) - _ = try init_scope.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); + _ = try init_scope.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }, false); const zir_tags = astgen.instructions.items(.tag); const zir_datas = astgen.instructions.items(.data); @@ -5395,10 +5395,21 @@ fn orelseCatchExpr( var else_scope = block_scope.makeSubBlock(scope); defer else_scope.unstack(); - // We know that the operand (almost certainly) modified the error return trace, - // so signal to Sema that it should save the new index for restoring later. - if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs)) - _ = try else_scope.addSaveErrRetIndex(.always); + if (do_err_trace) { + const catch_token = tree.nodes.items(.main_token)[node]; + const catch_token_start = tree.tokens.items(.start)[catch_token]; + astgen.advanceSourceCursor(catch_token_start); + const catch_line = astgen.source_line - else_scope.decl_line; + const catch_column = astgen.source_column; + + // Add an entry to the error return trace, unless this is `catch unreachable` + const emit_ret_trace_entry = tree.nodes.items(.tag)[rhs] != .unreachable_literal; + + // We know that the operand (almost certainly) modified the error return trace, + // so signal to Sema that it should save the new index for restoring later. + try emitDbgStmt(&else_scope, catch_line, catch_column); + _ = try else_scope.addSaveErrRetIndex(.always, emit_ret_trace_entry); + } var err_val_scope: Scope.LocalVal = undefined; const else_sub_scope = blk: { @@ -5817,10 +5828,18 @@ fn ifExpr( var else_scope = parent_gz.makeSubBlock(scope); defer else_scope.unstack(); - // We know that the operand (almost certainly) modified the error return trace, - // so signal to Sema that it should save the new index for restoring later. - if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr)) - _ = try else_scope.addSaveErrRetIndex(.always); + if (do_err_trace) { + const else_token = if_full.else_token; + const else_token_start = tree.tokens.items(.start)[else_token]; + astgen.advanceSourceCursor(else_token_start); + const else_line = astgen.source_line - else_scope.decl_line; + const else_column = astgen.source_column; + + // We know that the operand (almost certainly) modified the error return trace, + // so signal to Sema that it should save the new index for restoring later. + try emitDbgStmt(&else_scope, else_line, else_column); + _ = try else_scope.addSaveErrRetIndex(.always, true); + } const else_node = if_full.ast.else_expr; const else_info: struct { @@ -11733,10 +11752,12 @@ const GenZir = struct { always: void, if_of_error_type: Zir.Inst.Ref, }, + emit_ret_trace_entry: bool, ) !Zir.Inst.Index { return gz.addAsIndex(.{ .tag = .save_err_ret_index, .data = .{ .save_err_ret_index = .{ + .emit_ret_trace_entry = emit_ret_trace_entry, .operand = if (cond == .if_of_error_type) cond.if_of_error_type else .none, } }, }); diff --git a/src/Sema.zig b/src/Sema.zig index 41e5fdc20e48..bb16a5db43d5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17162,8 +17162,33 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE break :b operand_ty.isError(); }; - if (save_index) - block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); + if (save_index) { + const src = sema.src; + + const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); + const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); + const field_index = try sema.structFieldIndex(block, stack_trace_ty, "index", src); + + if (inst_data.emit_ret_trace_entry and sema.owner_func.?.calls_or_awaits_errorable_fn) { + // This .save_err_ret_index is from a catch { ... } or else |err| { ... } block. + // Add an entry to the error return trace, so that it's obvious how we made it into the block. + + const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); + const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); + const return_err_fn = try sema.getBuiltin(block, src, "returnError"); + const args: [1]Air.Inst.Ref = .{err_return_trace}; + + _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + } + + block.error_return_trace_index = try block.addInst(.{ + .tag = .save_err_return_trace_index, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(stack_trace_ty), + .payload = @intCast(u32, field_index), + } }, + }); + } } fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { diff --git a/src/Zir.zig b/src/Zir.zig index 4dd2386c511b..ca416a9e6985 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2638,6 +2638,7 @@ pub const Inst = struct { }, save_err_ret_index: struct { operand: Ref, // If error type (or .none), save new trace index + emit_ret_trace_entry: bool, // Add this location to the error trace }, restore_err_ret_index: struct { block: Ref, // If restored, the index is from this block's entrypoint diff --git a/src/print_zir.zig b/src/print_zir.zig index fb9031296d4d..24eaa6c16e8b 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -2306,7 +2306,7 @@ const Writer = struct { const inst_data = self.code.instructions.items(.data)[inst].save_err_ret_index; try self.writeInstRef(stream, inst_data.operand); - try stream.writeAll(")"); + try stream.print(", emit_ret_trace_entry={})", .{inst_data.emit_ret_trace_entry}); } fn writeRestoreErrRetIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 0ace004bc64c..c3a84ad210c5 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -329,6 +329,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:6:18: [address] in main (test) + \\ return foo() catch error.AndMyCarIsOutOfGas; + \\ ^ \\source.zig:6:5: [address] in main (test) \\ return foo() catch error.AndMyCarIsOutOfGas; \\ ^ @@ -345,6 +348,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:6:18: [address] in [function] + \\ return foo() catch error.AndMyCarIsOutOfGas; + \\ ^ \\source.zig:6:5: [address] in [function] \\ return foo() catch error.AndMyCarIsOutOfGas; \\ ^ @@ -585,6 +591,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:10:11: [address] in main (test) + \\ foo() catch { // error trace should include foo() + \\ ^ \\source.zig:6:5: [address] in bar (test) \\ return error.AndMyCarIsOutOfGas; \\ ^ @@ -603,6 +612,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:10:11: [address] in [function] + \\ foo() catch { // error trace should include foo() + \\ ^ \\source.zig:6:5: [address] in [function] \\ return error.AndMyCarIsOutOfGas; \\ ^ @@ -649,6 +661,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:10:23: [address] in main (test) + \\ if (foo()) |_| {} else |_| { // error trace should include foo() + \\ ^ \\source.zig:6:5: [address] in bar (test) \\ return error.AndMyCarIsOutOfGas; \\ ^ @@ -667,6 +682,9 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ + \\source.zig:10:23: [address] in [function] + \\ if (foo()) |_| {} else |_| { // error trace should include foo() + \\ ^ \\source.zig:6:5: [address] in [function] \\ return error.AndMyCarIsOutOfGas; \\ ^