authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-17 03:27:03-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-17 03:27:03-04:00
logdf74c45fa74f1247f434064d25b54998fe144906
tree490fab3dbc21d4825a304d62123d5573acba0e25
parent3fde14035b013646f42519189dbaa4534564d78b
parent0a7f3be42e96361ab8a9a567a11782fb81ea17da
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11501 from Vexu/stage2-err-return-trace

stage2: error return traces

19 files changed, 458 insertions(+), 49 deletions(-)

lib/compiler_rt.zig+11-13
......@@ -198,19 +198,17 @@ comptime {
198198 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
199199 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
200200
201 if (builtin.zig_backend == .stage1) { // TODO
202 switch (arch) {
203 .i386,
204 .x86_64,
205 => {
206 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
207 @export(zig_probe_stack, .{
208 .name = "__zig_probe_stack",
209 .linkage = linkage,
210 });
211 },
212 else => {},
213 }
201 switch (arch) {
202 .i386,
203 .x86_64,
204 => {
205 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
206 @export(zig_probe_stack, .{
207 .name = "__zig_probe_stack",
208 .linkage = linkage,
209 });
210 },
211 else => {},
214212 }
215213
216214 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
lib/std/builtin.zig+21
......@@ -846,5 +846,26 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846846 }
847847}
848848
849pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850 @setCold(true);
851 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
852}
853
854pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
855 @setCold(true);
856 std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len });
857}
858
859pub noinline fn returnError(maybe_st: ?*StackTrace) void {
860 @setCold(true);
861 const st = maybe_st orelse return;
862 addErrRetTraceAddr(st, @returnAddress());
863}
864
865pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
866 st.instruction_addresses[st.index & (st.instruction_addresses.len - 1)] = addr;
867 st.index +%= 1;
868}
869
849870const std = @import("std.zig");
850871const root = @import("root");
lib/test_runner.zig+2-2
......@@ -92,9 +92,9 @@ pub fn main() void {
9292 fail_count += 1;
9393 progress.log("FAIL ({s})\n", .{@errorName(err)});
9494 if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)});
95 if (builtin.zig_backend != .stage2_llvm) if (@errorReturnTrace()) |trace| {
95 if (@errorReturnTrace()) |trace| {
9696 std.debug.dumpStackTrace(trace.*);
97 };
97 }
9898 test_node.end();
9999 },
100100 }
src/Air.zig+8
......@@ -649,6 +649,12 @@ pub const Inst = struct {
649649 /// flush().
650650 cmp_lt_errors_len,
651651
652 /// Returns pointer to current error return trace.
653 err_return_trace,
654
655 /// Sets the operand as the current error return trace,
656 set_err_return_trace,
657
652658 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
653659 return switch (op) {
654660 .lt => .cmp_lt,
......@@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
961967 .alloc,
962968 .ret_ptr,
963969 .arg,
970 .err_return_trace,
964971 => return datas[inst].ty,
965972
966973 .assembly,
......@@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
10481055 .memcpy,
10491056 .set_union_tag,
10501057 .prefetch,
1058 .set_err_return_trace,
10511059 => return Type.void,
10521060
10531061 .ptrtoint,
src/AstGen.zig+27
......@@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
856856 catch_token + 2
857857 else
858858 null;
859
860 var rhs = node_datas[node].rhs;
861 while (true) switch (node_tags[rhs]) {
862 .grouped_expression => rhs = node_datas[rhs].lhs,
863 .unreachable_literal => {
864 if (payload_token != null and mem.eql(u8, tree.tokenSlice(payload_token.?), "_")) {
865 return astgen.failTok(payload_token.?, "discard of error capture; omit it instead", .{});
866 } else if (payload_token != null) {
867 return astgen.failTok(payload_token.?, "unused capture", .{});
868 }
869 const lhs = node_datas[node].lhs;
870
871 const operand = try reachableExpr(gz, scope, switch (rl) {
872 .ref => .ref,
873 else => .none,
874 }, lhs, lhs);
875 const result = try gz.addUnNode(switch (rl) {
876 .ref => .err_union_payload_safe_ptr,
877 else => .err_union_payload_safe,
878 }, operand, node);
879 switch (rl) {
880 .none, .coerced_ty, .discard, .ref => return result,
881 else => return rvalue(gz, rl, result, lhs),
882 }
883 },
884 else => break,
885 };
859886 switch (rl) {
860887 .ref => return orelseCatchExpr(
861888 gz,
src/Compilation.zig+2-1
......@@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
14571457 errdefer if (module) |zm| zm.deinit();
14581458
14591459 const error_return_tracing = !strip and switch (options.optimize_mode) {
1460 .Debug, .ReleaseSafe => true,
1460 .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and
1461 !options.target.cpu.arch.isBpf(),
14611462 .ReleaseFast, .ReleaseSmall => false,
14621463 };
14631464
src/Liveness.zig+2
......@@ -362,6 +362,7 @@ fn analyzeInst(
362362 .ret_addr,
363363 .frame_addr,
364364 .wasm_memory_size,
365 .err_return_trace,
365366 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
366367
367368 .not,
......@@ -434,6 +435,7 @@ fn analyzeInst(
434435 .round,
435436 .trunc_float,
436437 .cmp_lt_errors_len,
438 .set_err_return_trace,
437439 => {
438440 const operand = inst_datas[inst].un_op;
439441 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Module.zig+21
......@@ -1427,6 +1427,7 @@ pub const Fn = struct {
14271427 state: Analysis,
14281428 is_cold: bool = false,
14291429 is_noinline: bool = false,
1430 calls_or_awaits_errorable_fn: bool = false,
14301431
14311432 /// Any inferred error sets that this function owns, both its own inferred error set and
14321433 /// inferred error sets of any inline/comptime functions called. Not to be confused
......@@ -4838,6 +4839,9 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
48384839 };
48394840 defer sema.deinit();
48404841
4842 // reset in case calls to errorable functions are removed.
4843 func.calls_or_awaits_errorable_fn = false;
4844
48414845 // First few indexes of extra are reserved and set at the end.
48424846 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
48434847 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
......@@ -4936,6 +4940,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
49364940 func.state = .in_progress;
49374941 log.debug("set {s} to in_progress", .{decl.name});
49384942
4943 const last_arg_index = inner_block.instructions.items.len;
4944
49394945 sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
49404946 // TODO make these unreachable instead of @panic
49414947 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
......@@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
49444950 else => |e| return e,
49454951 };
49464952
4953 // If we don't get an error return trace from a caller, create our own.
4954 if (func.calls_or_awaits_errorable_fn and
4955 mod.comp.bin_file.options.error_return_tracing and
4956 !sema.fn_ret_ty.isError())
4957 {
4958 sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
4959 // TODO make these unreachable instead of @panic
4960 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
4961 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
4962 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
4963 error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"),
4964 else => |e| return e,
4965 };
4966 }
4967
49474968 try wip_captures.finalize();
49484969
49494970 // Copy the block into place and mark that as the main block.
src/Sema.zig+193-31
......@@ -1411,6 +1411,45 @@ fn analyzeAsType(
14111411 return ty.copy(sema.arena);
14121412}
14131413
1414pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
1415 const backend_supports_error_return_tracing =
1416 sema.mod.comp.bin_file.options.use_llvm;
1417 if (!backend_supports_error_return_tracing) {
1418 // TODO implement this feature in all the backends and then delete this branch
1419 return;
1420 }
1421
1422 var err_trace_block = block.makeSubBlock();
1423 err_trace_block.is_comptime = false;
1424 defer err_trace_block.instructions.deinit(sema.gpa);
1425
1426 const src: LazySrcLoc = .unneeded;
1427
1428 // var addrs: [err_return_trace_addr_count]usize = undefined;
1429 const err_return_trace_addr_count = 32;
1430 const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod);
1431 const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty));
1432
1433 // var st: StackTrace = undefined;
1434 const unresolved_stack_trace_ty = try sema.getBuiltinType(&err_trace_block, src, "StackTrace");
1435 const stack_trace_ty = try sema.resolveTypeFields(&err_trace_block, src, unresolved_stack_trace_ty);
1436 const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
1437
1438 // st.instruction_addresses = &addrs;
1439 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src);
1440 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
1441
1442 // st.index = 0;
1443 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src);
1444 const zero = try sema.addConstant(Type.usize, Value.zero);
1445 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
1446
1447 // @errorReturnTrace() = &st;
1448 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
1449
1450 try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items);
1451}
1452
14141453/// May return Value Tags: `variable`, `undef`.
14151454/// See `resolveConstValue` for an alternative.
14161455/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
......@@ -5236,6 +5275,9 @@ fn analyzeCall(
52365275 }
52375276
52385277 try sema.queueFullTypeResolution(func_ty_info.return_type);
5278 if (sema.owner_func != null and func_ty_info.return_type.isError()) {
5279 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
5280 }
52395281
52405282 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
52415283 args.len);
......@@ -5645,6 +5687,11 @@ fn instantiateGenericCall(
56455687
56465688 try sema.queueFullTypeResolution(new_fn_info.return_type);
56475689 }
5690
5691 if (sema.owner_func != null and new_fn_info.return_type.isError()) {
5692 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
5693 }
5694
56485695 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
56495696 runtime_args_len);
56505697 const func_inst = try block.addInst(.{
......@@ -6201,8 +6248,7 @@ fn zirErrUnionPayload(
62016248 }
62026249 try sema.requireRuntimeBlock(block, src);
62036250 if (safety_check and block.wantSafety()) {
6204 const is_non_err = try block.addUnOp(.is_err, operand);
6205 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6251 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
62066252 }
62076253 const result_ty = operand_ty.errorUnionPayload();
62086254 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
......@@ -6283,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(
62836329
62846330 try sema.requireRuntimeBlock(block, src);
62856331 if (safety_check and block.wantSafety()) {
6286 const is_non_err = try block.addUnOp(.is_err, operand);
6287 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6332 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
62886333 }
62896334 const air_tag: Air.Inst.Tag = if (initializing)
62906335 .errunion_payload_ptr_set
......@@ -12607,6 +12652,23 @@ fn analyzeRet(
1260712652 return always_noreturn;
1260812653 }
1260912654
12655 // TODO implement this feature in all the backends and then delete this check.
12656 const backend_supports_error_return_tracing =
12657 sema.mod.comp.bin_file.options.use_llvm;
12658
12659 if ((sema.fn_ret_ty.zigTypeTag() == .ErrorSet or sema.typeOf(uncasted_operand).zigTypeTag() == .ErrorUnion) and
12660 sema.mod.comp.bin_file.options.error_return_tracing and
12661 backend_supports_error_return_tracing)
12662 {
12663 const return_err_fn = try sema.getBuiltin(block, src, "returnError");
12664 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
12665 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
12666 const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
12667 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
12668 const args: [1]Air.Inst.Ref = .{err_return_trace};
12669 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args);
12670 }
12671
1261012672 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
1261112673 _ = try block.addUnOp(.ret, operand);
1261212674 return always_noreturn;
......@@ -13336,11 +13398,26 @@ fn zirErrorReturnTrace(
1333613398 extended: Zir.Inst.Extended.InstData,
1333713399) CompileError!Air.Inst.Ref {
1333813400 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
13401 return sema.getErrorReturnTrace(block, src);
13402}
13403
13404fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref {
1333913405 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
1334013406 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
13341 const opt_stack_trace_ty = try Type.optional(sema.arena, stack_trace_ty);
13342 // https://github.com/ziglang/zig/issues/11259
13343 return sema.addConstant(opt_stack_trace_ty, Value.@"null");
13407 const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
13408
13409 // TODO implement this feature in all the backends and then delete this check.
13410 const backend_supports_error_return_tracing =
13411 sema.mod.comp.bin_file.options.use_llvm;
13412
13413 if (sema.owner_func != null and
13414 sema.owner_func.?.calls_or_awaits_errorable_fn and
13415 sema.mod.comp.bin_file.options.error_return_tracing and
13416 backend_supports_error_return_tracing)
13417 {
13418 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
13419 }
13420 return sema.addConstant(opt_ptr_stack_trace_ty, Value.@"null");
1334413421}
1334513422
1334613423fn zirFrame(
......@@ -16777,11 +16854,9 @@ fn explainWhyTypeIsComptime(
1677716854pub const PanicId = enum {
1677816855 unreach,
1677916856 unwrap_null,
16780 unwrap_errunion,
1678116857 cast_to_null,
1678216858 incorrect_alignment,
1678316859 invalid_error_code,
16784 index_out_of_bounds,
1678516860 cast_truncated_data,
1678616861 integer_overflow,
1678716862 shl_overflow,
......@@ -16810,6 +16885,17 @@ fn addSafetyCheck(
1681016885
1681116886 _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);
1681216887
16888 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
16889}
16890
16891fn addSafetyCheckExtra(
16892 sema: *Sema,
16893 parent_block: *Block,
16894 ok: Air.Inst.Ref,
16895 fail_block: *Block,
16896) !void {
16897 const gpa = sema.gpa;
16898
1681316899 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
1681416900
1681516901 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
......@@ -16887,12 +16973,95 @@ fn panicWithMsg(
1688716973 try Type.optional(arena, ptr_stack_trace_ty),
1688816974 Value.@"null",
1688916975 );
16890 const args = try arena.create([2]Air.Inst.Ref);
16891 args.* = .{ msg_inst, null_stack_trace };
16892 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
16976 const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace };
16977 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args);
1689316978 return always_noreturn;
1689416979}
1689516980
16981fn panicUnwrapError(
16982 sema: *Sema,
16983 parent_block: *Block,
16984 src: LazySrcLoc,
16985 operand: Air.Inst.Ref,
16986 unwrap_err_tag: Air.Inst.Tag,
16987 is_non_err_tag: Air.Inst.Tag,
16988) !void {
16989 const ok = try parent_block.addUnOp(is_non_err_tag, operand);
16990 const gpa = sema.gpa;
16991
16992 var fail_block: Block = .{
16993 .parent = parent_block,
16994 .sema = sema,
16995 .src_decl = parent_block.src_decl,
16996 .namespace = parent_block.namespace,
16997 .wip_capture_scope = parent_block.wip_capture_scope,
16998 .instructions = .{},
16999 .inlining = parent_block.inlining,
17000 .is_comptime = parent_block.is_comptime,
17001 };
17002
17003 defer fail_block.instructions.deinit(gpa);
17004
17005 {
17006 const this_feature_is_implemented_in_the_backend =
17007 sema.mod.comp.bin_file.options.use_llvm;
17008
17009 if (!this_feature_is_implemented_in_the_backend) {
17010 // TODO implement this feature in all the backends and then delete this branch
17011 _ = try fail_block.addNoOp(.breakpoint);
17012 _ = try fail_block.addNoOp(.unreach);
17013 } else {
17014 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError");
17015 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
17016 const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src);
17017 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
17018 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
17019 }
17020 }
17021 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
17022}
17023
17024fn panicIndexOutOfBounds(
17025 sema: *Sema,
17026 parent_block: *Block,
17027 src: LazySrcLoc,
17028 index: Air.Inst.Ref,
17029 len: Air.Inst.Ref,
17030 cmp_op: Air.Inst.Tag,
17031) !void {
17032 const ok = try parent_block.addBinOp(cmp_op, index, len);
17033 const gpa = sema.gpa;
17034
17035 var fail_block: Block = .{
17036 .parent = parent_block,
17037 .sema = sema,
17038 .src_decl = parent_block.src_decl,
17039 .namespace = parent_block.namespace,
17040 .wip_capture_scope = parent_block.wip_capture_scope,
17041 .instructions = .{},
17042 .inlining = parent_block.inlining,
17043 .is_comptime = parent_block.is_comptime,
17044 };
17045
17046 defer fail_block.instructions.deinit(gpa);
17047
17048 {
17049 const this_feature_is_implemented_in_the_backend =
17050 sema.mod.comp.bin_file.options.use_llvm;
17051
17052 if (!this_feature_is_implemented_in_the_backend) {
17053 // TODO implement this feature in all the backends and then delete this branch
17054 _ = try fail_block.addNoOp(.breakpoint);
17055 _ = try fail_block.addNoOp(.unreach);
17056 } else {
17057 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicOutOfBounds");
17058 const args: [2]Air.Inst.Ref = .{ index, len };
17059 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
17060 }
17061 }
17062 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
17063}
17064
1689617065fn safetyPanic(
1689717066 sema: *Sema,
1689817067 block: *Block,
......@@ -16902,11 +17071,9 @@ fn safetyPanic(
1690217071 const msg = switch (panic_id) {
1690317072 .unreach => "reached unreachable code",
1690417073 .unwrap_null => "attempt to use null value",
16905 .unwrap_errunion => "unreachable error occurred",
1690617074 .cast_to_null => "cast causes pointer to be null",
1690717075 .incorrect_alignment => "incorrect alignment",
1690817076 .invalid_error_code => "invalid error code",
16909 .index_out_of_bounds => "attempt to index out of bounds",
1691017077 .cast_truncated_data => "integer cast truncated bits",
1691117078 .integer_overflow => "integer overflow",
1691217079 .shl_overflow => "left shift overflowed bits",
......@@ -18098,8 +18265,7 @@ fn elemValArray(
1809818265 if (maybe_index_val == null) {
1809918266 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
1810018267 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
18101 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
18102 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18268 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1810318269 }
1810418270 }
1810518271 return block.addBinOp(.array_elem_val, array, elem_index);
......@@ -18154,8 +18320,7 @@ fn elemPtrArray(
1815418320 if (maybe_index_val == null) {
1815518321 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
1815618322 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
18157 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
18158 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18323 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1815918324 }
1816018325 }
1816118326 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
......@@ -18208,8 +18373,7 @@ fn elemValSlice(
1820818373 else
1820918374 try block.addTyOp(.slice_len, Type.usize, slice);
1821018375 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
18211 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
18212 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18376 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1821318377 }
1821418378 try sema.queueFullTypeResolution(sema.typeOf(slice));
1821518379 return block.addBinOp(.slice_elem_val, slice, elem_index);
......@@ -18262,8 +18426,7 @@ fn elemPtrSlice(
1826218426 break :len try block.addTyOp(.slice_len, Type.usize, slice);
1826318427 };
1826418428 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
18265 const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
18266 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18429 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1826718430 }
1826818431 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
1826918432}
......@@ -20948,13 +21111,11 @@ fn analyzeSlice(
2094821111 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
2094921112 } else null;
2095021113 if (opt_len_inst) |len_inst| {
20951 const end_is_in_bounds = try block.addBinOp(.cmp_lte, end, len_inst);
20952 try sema.addSafetyCheck(block, end_is_in_bounds, .index_out_of_bounds);
21114 try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);
2095321115 }
2095421116
2095521117 // requirement: start <= end
20956 const start_is_in_bounds = try block.addBinOp(.cmp_lte, start, end);
20957 try sema.addSafetyCheck(block, start_is_in_bounds, .index_out_of_bounds);
21118 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
2095821119 }
2095921120 return block.addInst(.{
2096021121 .tag = .slice,
......@@ -21817,11 +21978,7 @@ fn resolvePeerTypes(
2181721978 info.data.sentinel = chosen_child_ty.sentinel();
2181821979 info.data.size = .Slice;
2181921980 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
21820 info.data.pointee_type = switch (chosen_child_ty.tag()) {
21821 .array => chosen_child_ty.elemType2(),
21822 .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),
21823 else => unreachable,
21824 };
21981 info.data.pointee_type = chosen_child_ty.elemType2();
2182521982
2182621983 const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
2182721984 const opt_ptr_ty = if (any_are_null)
......@@ -21891,6 +22048,11 @@ pub fn resolveFnTypes(
2189122048) CompileError!void {
2189222049 try sema.resolveTypeFully(block, src, fn_info.return_type);
2189322050
22051 if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) {
22052 // Ensure the type exists so that backends can assume that.
22053 _ = try sema.getBuiltinType(block, src, "StackTrace");
22054 }
22055
2189422056 for (fn_info.param_types) |param_ty| {
2189522057 try sema.resolveTypeFully(block, src, param_ty);
2189622058 }
src/arch/aarch64/CodeGen.zig+16
......@@ -718,6 +718,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
718718 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
719719 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
720720 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
721 .err_return_trace => try self.airErrReturnTrace(inst),
722 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
721723
722724 .wrap_optional => try self.airWrapOptional(inst),
723725 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
......@@ -2330,6 +2332,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
23302332 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
23312333}
23322334
2335fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2336 _ = inst;
2337 const result: MCValue = if (self.liveness.isUnused(inst))
2338 .dead
2339 else
2340 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2341 return self.finishAir(inst, result, .{ .none, .none, .none });
2342}
2343
2344fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2345 _ = inst;
2346 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
2347}
2348
23332349fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
23342350 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
23352351 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/arch/arm/CodeGen.zig+16
......@@ -725,6 +725,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
725725 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
726726 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
727727 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
728 .err_return_trace => try self.airErrReturnTrace(inst),
729 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
728730
729731 .wrap_optional => try self.airWrapOptional(inst),
730732 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
......@@ -1843,6 +1845,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
18431845 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18441846}
18451847
1848fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1849 _ = inst;
1850 const result: MCValue = if (self.liveness.isUnused(inst))
1851 .dead
1852 else
1853 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1854 return self.finishAir(inst, result, .{ .none, .none, .none });
1855}
1856
1857fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1858 _ = inst;
1859 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1860}
1861
18461862/// T to E!T
18471863fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
18481864 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
src/arch/riscv64/CodeGen.zig+16
......@@ -654,6 +654,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
654654 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
655655 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
656656 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
657 .err_return_trace => try self.airErrReturnTrace(inst),
658 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
657659
658660 .wrap_optional => try self.airWrapOptional(inst),
659661 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
......@@ -1267,6 +1269,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
12671269 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12681270}
12691271
1272fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1273 _ = inst;
1274 const result: MCValue = if (self.liveness.isUnused(inst))
1275 .dead
1276 else
1277 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1278 return self.finishAir(inst, result, .{ .none, .none, .none });
1279}
1280
1281fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1282 _ = inst;
1283 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1284}
1285
12701286fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
12711287 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
12721288 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/arch/sparc64/CodeGen.zig+2
......@@ -655,6 +655,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
655655 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
656656 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
657657 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
658 .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"),
659 .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"),
658660
659661 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
660662 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
src/arch/wasm/CodeGen.zig+2
......@@ -1612,6 +1612,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
16121612 .atomic_store_seq_cst,
16131613 .atomic_rmw,
16141614 .tag_name,
1615 .err_return_trace,
1616 .set_err_return_trace,
16151617 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
16161618 };
16171619}
src/arch/x86_64/CodeGen.zig+16
......@@ -749,6 +749,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
749749 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
750750 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
751751 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
752 .err_return_trace => try self.airErrReturnTrace(inst),
753 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
752754
753755 .wrap_optional => try self.airWrapOptional(inst),
754756 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
......@@ -1855,6 +1857,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
18551857 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18561858}
18571859
1860fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1861 _ = inst;
1862 const result: MCValue = if (self.liveness.isUnused(inst))
1863 .dead
1864 else
1865 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1866 return self.finishAir(inst, result, .{ .none, .none, .none });
1867}
1868
1869fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1870 _ = inst;
1871 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1872}
1873
18581874fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
18591875 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
18601876 if (self.liveness.isUnused(inst)) {
src/codegen/c.zig+12
......@@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
19111911 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
19121912 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
19131913 .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
1914 .err_return_trace => try airErrReturnTrace(f, inst),
1915 .set_err_return_trace => try airSetErrReturnTrace(f, inst),
19141916
19151917 .wasm_memory_size => try airWasmMemorySize(f, inst),
19161918 .wasm_memory_grow => try airWasmMemoryGrow(f, inst),
......@@ -3447,6 +3449,16 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
34473449 return local;
34483450}
34493451
3452fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
3453 if (f.liveness.isUnused(inst)) return CValue.none;
3454 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
3455}
3456
3457fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
3458 _ = inst;
3459 return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
3460}
3461
34503462fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
34513463 if (f.liveness.isUnused(inst))
34523464 return CValue.none;
src/codegen/llvm.zig+82-2
......@@ -636,10 +636,18 @@ pub const Object = struct {
636636 const ret_ptr = if (sret) llvm_func.getParam(0) else null;
637637 const gpa = dg.gpa;
638638
639 const err_return_tracing = fn_info.return_type.isError() and
640 dg.module.comp.bin_file.options.error_return_tracing;
641
642 const err_ret_trace = if (err_return_tracing)
643 llvm_func.getParam(@boolToInt(ret_ptr != null))
644 else
645 null;
646
639647 var args = std.ArrayList(*const llvm.Value).init(gpa);
640648 defer args.deinit();
641649
642 const param_offset: c_uint = @boolToInt(ret_ptr != null);
650 const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
643651 for (fn_info.param_types) |param_ty| {
644652 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
645653
......@@ -711,6 +719,7 @@ pub const Object = struct {
711719 .base_line = dg.decl.src_line,
712720 .prev_dbg_line = 0,
713721 .prev_dbg_column = 0,
722 .err_ret_trace = err_ret_trace,
714723 };
715724 defer fg.deinit();
716725
......@@ -1755,6 +1764,17 @@ pub const Object = struct {
17551764 try param_di_types.append(try o.lowerDebugType(Type.void, .full));
17561765 }
17571766
1767 if (fn_info.return_type.isError() and
1768 o.module.comp.bin_file.options.error_return_tracing)
1769 {
1770 var ptr_ty_payload: Type.Payload.ElemType = .{
1771 .base = .{ .tag = .single_mut_pointer },
1772 .data = o.getStackTraceType(),
1773 };
1774 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
1775 try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
1776 }
1777
17581778 for (fn_info.param_types) |param_ty| {
17591779 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
17601780
......@@ -1824,6 +1844,27 @@ pub const Object = struct {
18241844 "", // unique id
18251845 );
18261846 }
1847
1848 fn getStackTraceType(o: *Object) Type {
1849 const mod = o.module;
1850
1851 const std_pkg = mod.main_pkg.table.get("std").?;
1852 const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
1853
1854 const builtin_str: []const u8 = "builtin";
1855 const std_namespace = mod.declPtr(std_file.root_decl.unwrap().?).src_namespace;
1856 const builtin_decl = std_namespace.decls
1857 .getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?;
1858
1859 const stack_trace_str: []const u8 = "StackTrace";
1860 // buffer is only used for int_type, `builtin` is a struct.
1861 const builtin_ty = mod.declPtr(builtin_decl).val.toType(undefined);
1862 const builtin_namespace = builtin_ty.getNamespace().?;
1863 const stack_trace_decl = builtin_namespace.decls
1864 .getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?;
1865
1866 return mod.declPtr(stack_trace_decl).val.toType(undefined);
1867 }
18271868};
18281869
18291870pub const DeclGen = struct {
......@@ -1976,8 +2017,15 @@ pub const DeclGen = struct {
19762017 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
19772018 }
19782019
2020 const err_return_tracing = fn_info.return_type.isError() and
2021 dg.module.comp.bin_file.options.error_return_tracing;
2022
2023 if (err_return_tracing) {
2024 dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull");
2025 }
2026
19792027 // Set parameter attributes.
1980 var llvm_param_i: c_uint = @boolToInt(sret);
2028 var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
19812029 for (fn_info.param_types) |param_ty| {
19822030 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
19832031
......@@ -2435,6 +2483,17 @@ pub const DeclGen = struct {
24352483 try llvm_params.append(llvm_sret_ty.pointerType(0));
24362484 }
24372485
2486 if (fn_info.return_type.isError() and
2487 dg.module.comp.bin_file.options.error_return_tracing)
2488 {
2489 var ptr_ty_payload: Type.Payload.ElemType = .{
2490 .base = .{ .tag = .single_mut_pointer },
2491 .data = dg.object.getStackTraceType(),
2492 };
2493 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2494 try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty));
2495 }
2496
24382497 for (fn_info.param_types) |param_ty| {
24392498 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
24402499
......@@ -3449,6 +3508,8 @@ pub const FuncGen = struct {
34493508
34503509 llvm_func: *const llvm.Value,
34513510
3511 err_ret_trace: ?*const llvm.Value = null,
3512
34523513 /// This data structure is used to implement breaking to blocks.
34533514 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
34543515 parent_bb: *const llvm.BasicBlock,
......@@ -3678,6 +3739,8 @@ pub const FuncGen = struct {
36783739 .unwrap_errunion_err => try self.airErrUnionErr(inst, false),
36793740 .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
36803741 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
3742 .err_return_trace => try self.airErrReturnTrace(inst),
3743 .set_err_return_trace => try self.airSetErrReturnTrace(inst),
36813744
36823745 .wrap_optional => try self.airWrapOptional(inst),
36833746 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
......@@ -3732,6 +3795,12 @@ pub const FuncGen = struct {
37323795 break :blk ret_ptr;
37333796 };
37343797
3798 if (fn_info.return_type.isError() and
3799 self.dg.module.comp.bin_file.options.error_return_tracing)
3800 {
3801 try llvm_args.append(self.err_ret_trace.?);
3802 }
3803
37353804 for (args) |arg| {
37363805 const param_ty = self.air.typeOf(arg);
37373806 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
......@@ -5149,6 +5218,17 @@ pub const FuncGen = struct {
51495218 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
51505219 }
51515220
5221 fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !?*const llvm.Value {
5222 return self.err_ret_trace.?;
5223 }
5224
5225 fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
5226 const un_op = self.air.instructions.items(.data)[inst].un_op;
5227 const operand = try self.resolveInst(un_op);
5228 self.err_ret_trace = operand;
5229 return null;
5230 }
5231
51525232 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
51535233 if (self.liveness.isUnused(inst)) return null;
51545234
src/print_air.zig+2
......@@ -170,6 +170,7 @@ const Writer = struct {
170170 .round,
171171 .trunc_float,
172172 .cmp_lt_errors_len,
173 .set_err_return_trace,
173174 => try w.writeUnOp(s, inst),
174175
175176 .breakpoint,
......@@ -182,6 +183,7 @@ const Writer = struct {
182183 .alloc,
183184 .ret_ptr,
184185 .arg,
186 .err_return_trace,
185187 => try w.writeTy(s, inst),
186188
187189 .not,
src/type.zig+7
......@@ -4093,6 +4093,13 @@ pub const Type = extern union {
40934093 };
40944094 }
40954095
4096 pub fn isError(ty: Type) bool {
4097 return switch (ty.zigTypeTag()) {
4098 .ErrorUnion, .ErrorSet => true,
4099 else => false,
4100 };
4101 }
4102
40964103 /// Returns whether ty, which must be an error set, includes an error `name`.
40974104 /// Might return a false negative if `ty` is an inferred error set and not fully
40984105 /// resolved yet.