authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-04-22 21:30:54+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 17:42:51-07:00
logeee8fffec70b1d3e2900970dbe836e346e499231
treeba6418a557532cbe2aee2be1045f84ff0f3ee0ce
parent5888446c03b1f77a031f5a8093488a6a2f6decb6

stage2: implement error return traces


15 files changed, 318 insertions(+), 10 deletions(-)

lib/std/builtin.zig+11
...@@ -846,5 +846,16 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -846,5 +846,16 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846 }846 }
847}847}
848848
849pub noinline fn returnError(maybe_st: ?*StackTrace) void {
850 @setCold(true);
851 const st = maybe_st orelse return;
852 addErrRetTraceAddr(st, @returnAddress());
853}
854
855pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
856 st.instruction_addresses[st.index & (st.instruction_addresses.len - 1)] = addr;
857 st.index +%= 1;
858}
859
849const std = @import("std.zig");860const std = @import("std.zig");
850const root = @import("root");861const root = @import("root");
src/Air.zig+8
...@@ -649,6 +649,12 @@ pub const Inst = struct {...@@ -649,6 +649,12 @@ pub const Inst = struct {
649 /// flush().649 /// flush().
650 cmp_lt_errors_len,650 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
652 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {658 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
653 return switch (op) {659 return switch (op) {
654 .lt => .cmp_lt,660 .lt => .cmp_lt,
...@@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
961 .alloc,967 .alloc,
962 .ret_ptr,968 .ret_ptr,
963 .arg,969 .arg,
970 .err_return_trace,
964 => return datas[inst].ty,971 => return datas[inst].ty,
965972
966 .assembly,973 .assembly,
...@@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1048 .memcpy,1055 .memcpy,
1049 .set_union_tag,1056 .set_union_tag,
1050 .prefetch,1057 .prefetch,
1058 .set_err_return_trace,
1051 => return Type.void,1059 => return Type.void,
10521060
1053 .ptrtoint,1061 .ptrtoint,
src/Liveness.zig+2
...@@ -362,6 +362,7 @@ fn analyzeInst(...@@ -362,6 +362,7 @@ fn analyzeInst(
362 .ret_addr,362 .ret_addr,
363 .frame_addr,363 .frame_addr,
364 .wasm_memory_size,364 .wasm_memory_size,
365 .err_return_trace,
365 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),366 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
366367
367 .not,368 .not,
...@@ -434,6 +435,7 @@ fn analyzeInst(...@@ -434,6 +435,7 @@ fn analyzeInst(
434 .round,435 .round,
435 .trunc_float,436 .trunc_float,
436 .cmp_lt_errors_len,437 .cmp_lt_errors_len,
438 .set_err_return_trace,
437 => {439 => {
438 const operand = inst_datas[inst].un_op;440 const operand = inst_datas[inst].un_op;
439 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });441 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Module.zig+21
...@@ -1427,6 +1427,7 @@ pub const Fn = struct {...@@ -1427,6 +1427,7 @@ pub const Fn = struct {
1427 state: Analysis,1427 state: Analysis,
1428 is_cold: bool = false,1428 is_cold: bool = false,
1429 is_noinline: bool = false,1429 is_noinline: bool = false,
1430 calls_or_awaits_errorable_fn: bool = false,
14301431
1431 /// Any inferred error sets that this function owns, both its own inferred error set and1432 /// Any inferred error sets that this function owns, both its own inferred error set and
1432 /// inferred error sets of any inline/comptime functions called. Not to be confused1433 /// 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 {...@@ -4838,6 +4839,9 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
4838 };4839 };
4839 defer sema.deinit();4840 defer sema.deinit();
48404841
4842 // reset in case case calls to errorable functions are removed.
4843 func.calls_or_awaits_errorable_fn = false;
4844
4841 // First few indexes of extra are reserved and set at the end.4845 // First few indexes of extra are reserved and set at the end.
4842 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;4846 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
4843 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);4847 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
...@@ -4936,6 +4940,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {...@@ -4936,6 +4940,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
4936 func.state = .in_progress;4940 func.state = .in_progress;
4937 log.debug("set {s} to in_progress", .{decl.name});4941 log.debug("set {s} to in_progress", .{decl.name});
49384942
4943 const last_arg_index = inner_block.instructions.items.len;
4944
4939 sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {4945 sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
4940 // TODO make these unreachable instead of @panic4946 // TODO make these unreachable instead of @panic
4941 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),4947 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
...@@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {...@@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
4944 else => |e| return e,4950 else => |e| return e,
4945 };4951 };
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
4947 try wip_captures.finalize();4968 try wip_captures.finalize();
49484969
4949 // Copy the block into place and mark that as the main block.4970 // Copy the block into place and mark that as the main block.
src/Sema.zig+67-8
...@@ -1411,6 +1411,38 @@ fn analyzeAsType(...@@ -1411,6 +1411,38 @@ fn analyzeAsType(
1411 return ty.copy(sema.arena);1411 return ty.copy(sema.arena);
1412}1412}
14131413
1414pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
1415 var err_trace_block = block.makeSubBlock();
1416 err_trace_block.is_comptime = false;
1417 defer err_trace_block.instructions.deinit(sema.gpa);
1418
1419 const src: LazySrcLoc = .unneeded;
1420
1421 // var addrs: [err_return_trace_addr_count]usize = undefined;
1422 const err_return_trace_addr_count = 32;
1423 const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod);
1424 const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty));
1425
1426 // var st: StackTrace = undefined;
1427 const unresolved_stack_trace_ty = try sema.getBuiltinType(&err_trace_block, src, "StackTrace");
1428 const stack_trace_ty = try sema.resolveTypeFields(&err_trace_block, src, unresolved_stack_trace_ty);
1429 const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
1430
1431 // st.instruction_addresses = &addrs;
1432 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src);
1433 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
1434
1435 // st.index = 0;
1436 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src);
1437 const zero = try sema.addConstant(Type.usize, Value.zero);
1438 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
1439
1440 // @errorReturnTrace() = &st;
1441 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
1442
1443 try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items);
1444}
1445
1414/// May return Value Tags: `variable`, `undef`.1446/// May return Value Tags: `variable`, `undef`.
1415/// See `resolveConstValue` for an alternative.1447/// See `resolveConstValue` for an alternative.
1416/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.1448/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
...@@ -5236,6 +5268,13 @@ fn analyzeCall(...@@ -5236,6 +5268,13 @@ fn analyzeCall(
5236 }5268 }
52375269
5238 try sema.queueFullTypeResolution(func_ty_info.return_type);5270 try sema.queueFullTypeResolution(func_ty_info.return_type);
5271 if (sema.owner_func != null and func_ty_info.return_type.isError()) {
5272 if (!sema.owner_func.?.calls_or_awaits_errorable_fn) {
5273 // Ensure the type exists so that backends can assume that.
5274 _ = try sema.getBuiltinType(block, call_src, "StackTrace");
5275 }
5276 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
5277 }
52395278
5240 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +5279 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
5241 args.len);5280 args.len);
...@@ -5645,6 +5684,15 @@ fn instantiateGenericCall(...@@ -5645,6 +5684,15 @@ fn instantiateGenericCall(
56455684
5646 try sema.queueFullTypeResolution(new_fn_info.return_type);5685 try sema.queueFullTypeResolution(new_fn_info.return_type);
5647 }5686 }
5687
5688 if (sema.owner_func != null and new_fn_info.return_type.isError()) {
5689 if (!sema.owner_func.?.calls_or_awaits_errorable_fn) {
5690 // Ensure the type exists so that backends can assume that.
5691 _ = try sema.getBuiltinType(block, call_src, "StackTrace");
5692 }
5693 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
5694 }
5695
5648 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +5696 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
5649 runtime_args_len);5697 runtime_args_len);
5650 const func_inst = try block.addInst(.{5698 const func_inst = try block.addInst(.{
...@@ -12607,6 +12655,16 @@ fn analyzeRet(...@@ -12607,6 +12655,16 @@ fn analyzeRet(
12607 return always_noreturn;12655 return always_noreturn;
12608 }12656 }
1260912657
12658 if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing) {
12659 const return_err_fn = try sema.getBuiltin(block, src, "returnError");
12660 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
12661 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
12662 const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
12663 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
12664 const args: [1]Air.Inst.Ref = .{err_return_trace};
12665 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args);
12666 }
12667
12610 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);12668 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
12611 _ = try block.addUnOp(.ret, operand);12669 _ = try block.addUnOp(.ret, operand);
12612 return always_noreturn;12670 return always_noreturn;
...@@ -13338,9 +13396,14 @@ fn zirErrorReturnTrace(...@@ -13338,9 +13396,14 @@ fn zirErrorReturnTrace(
13338 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };13396 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
13339 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");13397 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
13340 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);13398 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);13399 const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
13342 // https://github.com/ziglang/zig/issues/1125913400 if (sema.owner_func != null and
13343 return sema.addConstant(opt_stack_trace_ty, Value.@"null");13401 sema.owner_func.?.calls_or_awaits_errorable_fn and
13402 sema.mod.comp.bin_file.options.error_return_tracing)
13403 {
13404 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
13405 }
13406 return sema.addConstant(opt_ptr_stack_trace_ty, Value.@"null");
13344}13407}
1334513408
13346fn zirFrame(13409fn zirFrame(
...@@ -21817,11 +21880,7 @@ fn resolvePeerTypes(...@@ -21817,11 +21880,7 @@ fn resolvePeerTypes(
21817 info.data.sentinel = chosen_child_ty.sentinel();21880 info.data.sentinel = chosen_child_ty.sentinel();
21818 info.data.size = .Slice;21881 info.data.size = .Slice;
21819 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());21882 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
21820 info.data.pointee_type = switch (chosen_child_ty.tag()) {21883 info.data.pointee_type = chosen_child_ty.elemType2();
21821 .array => chosen_child_ty.elemType2(),
21822 .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),
21823 else => unreachable,
21824 };
2182521884
21826 const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);21885 const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
21827 const opt_ptr_ty = if (any_are_null)21886 const opt_ptr_ty = if (any_are_null)
src/arch/aarch64/CodeGen.zig+20
...@@ -718,6 +718,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -718,6 +718,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
718 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),718 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
719 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),719 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
720 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),720 .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
722 .wrap_optional => try self.airWrapOptional(inst),724 .wrap_optional => try self.airWrapOptional(inst),
723 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),725 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
...@@ -2330,6 +2332,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2330,6 +2332,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
2330 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2332 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2331}2333}
23322334
2335fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2336 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
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, .{ ty_op.operand, .none, .none });
2342}
2343
2344fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2345 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2346 const result: MCValue = if (self.liveness.isUnused(inst))
2347 .dead
2348 else
2349 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
2350 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2351}
2352
2333fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {2353fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
2334 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2354 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2335 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2355 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/arch/arm/CodeGen.zig+20
...@@ -725,6 +725,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -725,6 +725,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
725 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),725 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
726 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),726 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
727 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),727 .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
729 .wrap_optional => try self.airWrapOptional(inst),731 .wrap_optional => try self.airWrapOptional(inst),
730 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),732 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
...@@ -1843,6 +1845,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1843,6 +1845,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
1843 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1845 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1844}1846}
18451847
1848fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1849 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
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, .{ ty_op.operand, .none, .none });
1855}
1856
1857fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1858 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1859 const result: MCValue = if (self.liveness.isUnused(inst))
1860 .dead
1861 else
1862 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1863 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1864}
1865
1846/// T to E!T1866/// T to E!T
1847fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {1867fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1848 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1868 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
src/arch/riscv64/CodeGen.zig+20
...@@ -654,6 +654,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -654,6 +654,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
654 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),654 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
655 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),655 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
656 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),656 .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
658 .wrap_optional => try self.airWrapOptional(inst),660 .wrap_optional => try self.airWrapOptional(inst),
659 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),661 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
...@@ -1267,6 +1269,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1267,6 +1269,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
1267 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1269 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1268}1270}
12691271
1272fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1273 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
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, .{ ty_op.operand, .none, .none });
1279}
1280
1281fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1282 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1283 const result: MCValue = if (self.liveness.isUnused(inst))
1284 .dead
1285 else
1286 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1287 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1288}
1289
1270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {1290fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1271 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1291 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1272 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1292 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/arch/sparc64/CodeGen.zig+2
...@@ -630,6 +630,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -630,6 +630,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
630 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),630 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
631 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),631 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
632 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),632 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
633 .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"),
634 .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"),
633635
634 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),636 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
635 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),637 .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 {...@@ -1612,6 +1612,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1612 .atomic_store_seq_cst,1612 .atomic_store_seq_cst,
1613 .atomic_rmw,1613 .atomic_rmw,
1614 .tag_name,1614 .tag_name,
1615 .err_return_trace,
1616 .set_err_return_trace,
1615 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),1617 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
1616 };1618 };
1617}1619}
src/arch/x86_64/CodeGen.zig+20
...@@ -749,6 +749,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -749,6 +749,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
749 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),749 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
750 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),750 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
751 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),751 .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
753 .wrap_optional => try self.airWrapOptional(inst),755 .wrap_optional => try self.airWrapOptional(inst),
754 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),756 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
...@@ -1855,6 +1857,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1855,6 +1857,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
1855 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1857 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1856}1858}
18571859
1860fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1861 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
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, .{ ty_op.operand, .none, .none });
1867}
1868
1869fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1870 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1871 const result: MCValue = if (self.liveness.isUnused(inst))
1872 .dead
1873 else
1874 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1875 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1876}
1877
1858fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {1878fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1859 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1879 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1860 if (self.liveness.isUnused(inst)) {1880 if (self.liveness.isUnused(inst)) {
src/codegen/c.zig+34
...@@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1911 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),1911 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
1912 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),1912 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
1913 .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),1913 .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
1915 .wasm_memory_size => try airWasmMemorySize(f, inst),1917 .wasm_memory_size => try airWasmMemorySize(f, inst),
1916 .wasm_memory_grow => try airWasmMemoryGrow(f, inst),1918 .wasm_memory_grow => try airWasmMemoryGrow(f, inst),
...@@ -3447,6 +3449,38 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3447,6 +3449,38 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
3447 return local;3449 return local;
3448}3450}
34493451
3452fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
3453 if (f.liveness.isUnused(inst)) return CValue.none;
3454
3455 const un_op = f.air.instructions.items(.data)[inst].un_op;
3456 const writer = f.object.writer();
3457 const inst_ty = f.air.typeOfIndex(inst);
3458 const operand = try f.resolveInst(un_op);
3459 const local = try f.allocLocal(inst_ty, .Const);
3460
3461 try writer.writeAll(" = ");
3462
3463 _ = operand;
3464 _ = local;
3465 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
3466}
3467
3468fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
3469 if (f.liveness.isUnused(inst)) return CValue.none;
3470
3471 const un_op = f.air.instructions.items(.data)[inst].un_op;
3472 const writer = f.object.writer();
3473 const inst_ty = f.air.typeOfIndex(inst);
3474 const operand = try f.resolveInst(un_op);
3475 const local = try f.allocLocal(inst_ty, .Const);
3476
3477 try writer.writeAll(" = ");
3478
3479 _ = operand;
3480 _ = local;
3481 return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
3482}
3483
3450fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {3484fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
3451 if (f.liveness.isUnused(inst))3485 if (f.liveness.isUnused(inst))
3452 return CValue.none;3486 return CValue.none;
src/codegen/llvm.zig+82-2
...@@ -636,10 +636,18 @@ pub const Object = struct {...@@ -636,10 +636,18 @@ pub const Object = struct {
636 const ret_ptr = if (sret) llvm_func.getParam(0) else null;636 const ret_ptr = if (sret) llvm_func.getParam(0) else null;
637 const gpa = dg.gpa;637 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
639 var args = std.ArrayList(*const llvm.Value).init(gpa);647 var args = std.ArrayList(*const llvm.Value).init(gpa);
640 defer args.deinit();648 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);
643 for (fn_info.param_types) |param_ty| {651 for (fn_info.param_types) |param_ty| {
644 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;652 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
645653
...@@ -711,6 +719,7 @@ pub const Object = struct {...@@ -711,6 +719,7 @@ pub const Object = struct {
711 .base_line = dg.decl.src_line,719 .base_line = dg.decl.src_line,
712 .prev_dbg_line = 0,720 .prev_dbg_line = 0,
713 .prev_dbg_column = 0,721 .prev_dbg_column = 0,
722 .err_ret_trace = err_ret_trace,
714 };723 };
715 defer fg.deinit();724 defer fg.deinit();
716725
...@@ -1755,6 +1764,17 @@ pub const Object = struct {...@@ -1755,6 +1764,17 @@ pub const Object = struct {
1755 try param_di_types.append(try o.lowerDebugType(Type.void, .full));1764 try param_di_types.append(try o.lowerDebugType(Type.void, .full));
1756 }1765 }
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
1758 for (fn_info.param_types) |param_ty| {1778 for (fn_info.param_types) |param_ty| {
1759 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;1779 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
17601780
...@@ -1824,6 +1844,27 @@ pub const Object = struct {...@@ -1824,6 +1844,27 @@ pub const Object = struct {
1824 "", // unique id1844 "", // unique id
1825 );1845 );
1826 }1846 }
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 }
1827};1868};
18281869
1829pub const DeclGen = struct {1870pub const DeclGen = struct {
...@@ -1976,8 +2017,15 @@ pub const DeclGen = struct {...@@ -1976,8 +2017,15 @@ pub const DeclGen = struct {
1976 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);2017 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
1977 }2018 }
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
1979 // Set parameter attributes.2027 // 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);
1981 for (fn_info.param_types) |param_ty| {2029 for (fn_info.param_types) |param_ty| {
1982 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;2030 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
19832031
...@@ -2435,6 +2483,17 @@ pub const DeclGen = struct {...@@ -2435,6 +2483,17 @@ pub const DeclGen = struct {
2435 try llvm_params.append(llvm_sret_ty.pointerType(0));2483 try llvm_params.append(llvm_sret_ty.pointerType(0));
2436 }2484 }
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
2438 for (fn_info.param_types) |param_ty| {2497 for (fn_info.param_types) |param_ty| {
2439 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;2498 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
24402499
...@@ -3449,6 +3508,8 @@ pub const FuncGen = struct {...@@ -3449,6 +3508,8 @@ pub const FuncGen = struct {
34493508
3450 llvm_func: *const llvm.Value,3509 llvm_func: *const llvm.Value,
34513510
3511 err_ret_trace: ?*const llvm.Value = null,
3512
3452 /// This data structure is used to implement breaking to blocks.3513 /// This data structure is used to implement breaking to blocks.
3453 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {3514 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
3454 parent_bb: *const llvm.BasicBlock,3515 parent_bb: *const llvm.BasicBlock,
...@@ -3678,6 +3739,8 @@ pub const FuncGen = struct {...@@ -3678,6 +3739,8 @@ pub const FuncGen = struct {
3678 .unwrap_errunion_err => try self.airErrUnionErr(inst, false),3739 .unwrap_errunion_err => try self.airErrUnionErr(inst, false),
3679 .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),3740 .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
3680 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),3741 .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
3682 .wrap_optional => try self.airWrapOptional(inst),3745 .wrap_optional => try self.airWrapOptional(inst),
3683 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),3746 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
...@@ -3732,6 +3795,12 @@ pub const FuncGen = struct {...@@ -3732,6 +3795,12 @@ pub const FuncGen = struct {
3732 break :blk ret_ptr;3795 break :blk ret_ptr;
3733 };3796 };
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
3735 for (args) |arg| {3804 for (args) |arg| {
3736 const param_ty = self.air.typeOf(arg);3805 const param_ty = self.air.typeOf(arg);
3737 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;3806 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
...@@ -5149,6 +5218,17 @@ pub const FuncGen = struct {...@@ -5149,6 +5218,17 @@ pub const FuncGen = struct {
5149 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");5218 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
5150 }5219 }
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
5152 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5232 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
5153 if (self.liveness.isUnused(inst)) return null;5233 if (self.liveness.isUnused(inst)) return null;
51545234
src/print_air.zig+2
...@@ -170,6 +170,7 @@ const Writer = struct {...@@ -170,6 +170,7 @@ const Writer = struct {
170 .round,170 .round,
171 .trunc_float,171 .trunc_float,
172 .cmp_lt_errors_len,172 .cmp_lt_errors_len,
173 .set_err_return_trace,
173 => try w.writeUnOp(s, inst),174 => try w.writeUnOp(s, inst),
174175
175 .breakpoint,176 .breakpoint,
...@@ -182,6 +183,7 @@ const Writer = struct {...@@ -182,6 +183,7 @@ const Writer = struct {
182 .alloc,183 .alloc,
183 .ret_ptr,184 .ret_ptr,
184 .arg,185 .arg,
186 .err_return_trace,
185 => try w.writeTy(s, inst),187 => try w.writeTy(s, inst),
186188
187 .not,189 .not,
src/type.zig+7
...@@ -4093,6 +4093,13 @@ pub const Type = extern union {...@@ -4093,6 +4093,13 @@ pub const Type = extern union {
4093 };4093 };
4094 }4094 }
40954095
4096 pub fn isError(ty: Type) bool {
4097 return switch (ty.zigTypeTag()) {
4098 .ErrorUnion, .ErrorSet => true,
4099 else => false,
4100 };
4101 }
4102
4096 /// Returns whether ty, which must be an error set, includes an error `name`.4103 /// Returns whether ty, which must be an error set, includes an error `name`.
4097 /// Might return a false negative if `ty` is an inferred error set and not fully4104 /// Might return a false negative if `ty` is an inferred error set and not fully
4098 /// resolved yet.4105 /// resolved yet.