| author | |
| committer | |
| log | eee8fffec70b1d3e2900970dbe836e346e499231 |
| tree | ba6418a557532cbe2aee2be1045f84ff0f3ee0ce |
| parent | 5888446c03b1f77a031f5a8093488a6a2f6decb6 |
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 | 846 | } |
| 847 | 847 | } |
| 848 | 848 | |
| 849 | pub noinline fn returnError(maybe_st: ?*StackTrace) void { | |
| 850 | @setCold(true); | |
| 851 | const st = maybe_st orelse return; | |
| 852 | addErrRetTraceAddr(st, @returnAddress()); | |
| 853 | } | |
| 854 | ||
| 855 | pub 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 | ||
| 849 | 860 | const std = @import("std.zig"); |
| 850 | 861 | const root = @import("root"); |
src/Air.zig+8| ... | ... | @@ -649,6 +649,12 @@ pub const Inst = struct { |
| 649 | 649 | /// flush(). |
| 650 | 650 | cmp_lt_errors_len, |
| 651 | 651 | |
| 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 | 658 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 653 | 659 | return switch (op) { |
| 654 | 660 | .lt => .cmp_lt, |
| ... | ... | @@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 961 | 967 | .alloc, |
| 962 | 968 | .ret_ptr, |
| 963 | 969 | .arg, |
| 970 | .err_return_trace, | |
| 964 | 971 | => return datas[inst].ty, |
| 965 | 972 | |
| 966 | 973 | .assembly, |
| ... | ... | @@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1048 | 1055 | .memcpy, |
| 1049 | 1056 | .set_union_tag, |
| 1050 | 1057 | .prefetch, |
| 1058 | .set_err_return_trace, | |
| 1051 | 1059 | => return Type.void, |
| 1052 | 1060 | |
| 1053 | 1061 | .ptrtoint, |
src/Liveness.zig+2| ... | ... | @@ -362,6 +362,7 @@ fn analyzeInst( |
| 362 | 362 | .ret_addr, |
| 363 | 363 | .frame_addr, |
| 364 | 364 | .wasm_memory_size, |
| 365 | .err_return_trace, | |
| 365 | 366 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), |
| 366 | 367 | |
| 367 | 368 | .not, |
| ... | ... | @@ -434,6 +435,7 @@ fn analyzeInst( |
| 434 | 435 | .round, |
| 435 | 436 | .trunc_float, |
| 436 | 437 | .cmp_lt_errors_len, |
| 438 | .set_err_return_trace, | |
| 437 | 439 | => { |
| 438 | 440 | const operand = inst_datas[inst].un_op; |
| 439 | 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 | 1427 | state: Analysis, |
| 1428 | 1428 | is_cold: bool = false, |
| 1429 | 1429 | is_noinline: bool = false, |
| 1430 | calls_or_awaits_errorable_fn: bool = false, | |
| 1430 | 1431 | |
| 1431 | 1432 | /// Any inferred error sets that this function owns, both its own inferred error set and |
| 1432 | 1433 | /// 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 | 4839 | }; |
| 4839 | 4840 | defer sema.deinit(); |
| 4840 | 4841 | |
| 4842 | // reset in case case calls to errorable functions are removed. | |
| 4843 | func.calls_or_awaits_errorable_fn = false; | |
| 4844 | ||
| 4841 | 4845 | // First few indexes of extra are reserved and set at the end. |
| 4842 | 4846 | const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len; |
| 4843 | 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 | 4940 | func.state = .in_progress; |
| 4937 | 4941 | log.debug("set {s} to in_progress", .{decl.name}); |
| 4938 | 4942 | |
| 4943 | const last_arg_index = inner_block.instructions.items.len; | |
| 4944 | ||
| 4939 | 4945 | sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) { |
| 4940 | 4946 | // TODO make these unreachable instead of @panic |
| 4941 | 4947 | error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), |
| ... | ... | @@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { |
| 4944 | 4950 | else => |e| return e, |
| 4945 | 4951 | }; |
| 4946 | 4952 | |
| 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 | 4968 | try wip_captures.finalize(); |
| 4948 | 4969 | |
| 4949 | 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 | 1411 | return ty.copy(sema.arena); |
| 1412 | 1412 | } |
| 1413 | 1413 | |
| 1414 | pub 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 | 1446 | /// May return Value Tags: `variable`, `undef`. |
| 1415 | 1447 | /// See `resolveConstValue` for an alternative. |
| 1416 | 1448 | /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. |
| ... | ... | @@ -5236,6 +5268,13 @@ fn analyzeCall( |
| 5236 | 5268 | } |
| 5237 | 5269 | |
| 5238 | 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 | } | |
| 5239 | 5278 | |
| 5240 | 5279 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 5241 | 5280 | args.len); |
| ... | ... | @@ -5645,6 +5684,15 @@ fn instantiateGenericCall( |
| 5645 | 5684 | |
| 5646 | 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 | 5696 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 5649 | 5697 | runtime_args_len); |
| 5650 | 5698 | const func_inst = try block.addInst(.{ |
| ... | ... | @@ -12607,6 +12655,16 @@ fn analyzeRet( |
| 12607 | 12655 | return always_noreturn; |
| 12608 | 12656 | } |
| 12609 | 12657 | |
| 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 | 12668 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); |
| 12611 | 12669 | _ = try block.addUnOp(.ret, operand); |
| 12612 | 12670 | return always_noreturn; |
| ... | ... | @@ -13338,9 +13396,14 @@ fn zirErrorReturnTrace( |
| 13338 | 13396 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; |
| 13339 | 13397 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); |
| 13340 | 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); | |
| 13342 | // https://github.com/ziglang/zig/issues/11259 | |
| 13343 | return sema.addConstant(opt_stack_trace_ty, Value.@"null"); | |
| 13399 | const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); | |
| 13400 | if (sema.owner_func != null and | |
| 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 | } |
| 13345 | 13408 | |
| 13346 | 13409 | fn zirFrame( |
| ... | ... | @@ -21817,11 +21880,7 @@ fn resolvePeerTypes( |
| 21817 | 21880 | info.data.sentinel = chosen_child_ty.sentinel(); |
| 21818 | 21881 | info.data.size = .Slice; |
| 21819 | 21882 | 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 | }; | |
| 21883 | info.data.pointee_type = chosen_child_ty.elemType2(); | |
| 21825 | 21884 | |
| 21826 | 21885 | const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); |
| 21827 | 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 | 718 | .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), |
| 719 | 719 | .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), |
| 720 | 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), | |
| 721 | 723 | |
| 722 | 724 | .wrap_optional => try self.airWrapOptional(inst), |
| 723 | 725 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), |
| ... | ... | @@ -2330,6 +2332,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 2330 | 2332 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2331 | 2333 | } |
| 2332 | 2334 | |
| 2335 | fn 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 | ||
| 2344 | fn 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 | ||
| 2333 | 2353 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | 2354 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2335 | 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 | 725 | .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), |
| 726 | 726 | .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), |
| 727 | 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), | |
| 728 | 730 | |
| 729 | 731 | .wrap_optional => try self.airWrapOptional(inst), |
| 730 | 732 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), |
| ... | ... | @@ -1843,6 +1845,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1843 | 1845 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1844 | 1846 | } |
| 1845 | 1847 | |
| 1848 | fn 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 | ||
| 1857 | fn 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 | 1866 | /// T to E!T |
| 1847 | 1867 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1848 | 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 | 654 | .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), |
| 655 | 655 | .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), |
| 656 | 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), | |
| 657 | 659 | |
| 658 | 660 | .wrap_optional => try self.airWrapOptional(inst), |
| 659 | 661 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), |
| ... | ... | @@ -1267,6 +1269,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1267 | 1269 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1268 | 1270 | } |
| 1269 | 1271 | |
| 1272 | fn 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 | ||
| 1281 | fn 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 | ||
| 1270 | 1290 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 1271 | 1291 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1272 | 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 | 630 | .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), |
| 631 | 631 | .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), |
| 632 | 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)"), | |
| 633 | 635 | |
| 634 | 636 | .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), |
| 635 | 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 | 1612 | .atomic_store_seq_cst, |
| 1613 | 1613 | .atomic_rmw, |
| 1614 | 1614 | .tag_name, |
| 1615 | .err_return_trace, | |
| 1616 | .set_err_return_trace, | |
| 1615 | 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 | 749 | .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), |
| 750 | 750 | .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), |
| 751 | 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), | |
| 752 | 754 | |
| 753 | 755 | .wrap_optional => try self.airWrapOptional(inst), |
| 754 | 756 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), |
| ... | ... | @@ -1855,6 +1857,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { |
| 1855 | 1857 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1856 | 1858 | } |
| 1857 | 1859 | |
| 1860 | fn 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 | ||
| 1869 | fn 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 | ||
| 1858 | 1878 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 1859 | 1879 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1860 | 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 | 1911 | .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), |
| 1912 | 1912 | .wrap_errunion_err => try airWrapErrUnionErr(f, inst), |
| 1913 | 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), | |
| 1914 | 1916 | |
| 1915 | 1917 | .wasm_memory_size => try airWasmMemorySize(f, inst), |
| 1916 | 1918 | .wasm_memory_grow => try airWasmMemoryGrow(f, inst), |
| ... | ... | @@ -3447,6 +3449,38 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3447 | 3449 | return local; |
| 3448 | 3450 | } |
| 3449 | 3451 | |
| 3452 | fn 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 | ||
| 3468 | fn 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 | ||
| 3450 | 3484 | fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3451 | 3485 | if (f.liveness.isUnused(inst)) |
| 3452 | 3486 | return CValue.none; |
src/codegen/llvm.zig+82-2| ... | ... | @@ -636,10 +636,18 @@ pub const Object = struct { |
| 636 | 636 | const ret_ptr = if (sret) llvm_func.getParam(0) else null; |
| 637 | 637 | const gpa = dg.gpa; |
| 638 | 638 | |
| 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 | 647 | var args = std.ArrayList(*const llvm.Value).init(gpa); |
| 640 | 648 | defer args.deinit(); |
| 641 | 649 | |
| 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 | 651 | for (fn_info.param_types) |param_ty| { |
| 644 | 652 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 645 | 653 | |
| ... | ... | @@ -711,6 +719,7 @@ pub const Object = struct { |
| 711 | 719 | .base_line = dg.decl.src_line, |
| 712 | 720 | .prev_dbg_line = 0, |
| 713 | 721 | .prev_dbg_column = 0, |
| 722 | .err_ret_trace = err_ret_trace, | |
| 714 | 723 | }; |
| 715 | 724 | defer fg.deinit(); |
| 716 | 725 | |
| ... | ... | @@ -1755,6 +1764,17 @@ pub const Object = struct { |
| 1755 | 1764 | try param_di_types.append(try o.lowerDebugType(Type.void, .full)); |
| 1756 | 1765 | } |
| 1757 | 1766 | |
| 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 | 1778 | for (fn_info.param_types) |param_ty| { |
| 1759 | 1779 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1760 | 1780 | |
| ... | ... | @@ -1824,6 +1844,27 @@ pub const Object = struct { |
| 1824 | 1844 | "", // 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 | }; |
| 1828 | 1869 | |
| 1829 | 1870 | pub const DeclGen = struct { |
| ... | ... | @@ -1976,8 +2017,15 @@ pub const DeclGen = struct { |
| 1976 | 2017 | llvm_fn.addSretAttr(0, raw_llvm_ret_ty); |
| 1977 | 2018 | } |
| 1978 | 2019 | |
| 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 | 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 | 2029 | for (fn_info.param_types) |param_ty| { |
| 1982 | 2030 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1983 | 2031 | |
| ... | ... | @@ -2435,6 +2483,17 @@ pub const DeclGen = struct { |
| 2435 | 2483 | try llvm_params.append(llvm_sret_ty.pointerType(0)); |
| 2436 | 2484 | } |
| 2437 | 2485 | |
| 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 | 2497 | for (fn_info.param_types) |param_ty| { |
| 2439 | 2498 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2440 | 2499 | |
| ... | ... | @@ -3449,6 +3508,8 @@ pub const FuncGen = struct { |
| 3449 | 3508 | |
| 3450 | 3509 | llvm_func: *const llvm.Value, |
| 3451 | 3510 | |
| 3511 | err_ret_trace: ?*const llvm.Value = null, | |
| 3512 | ||
| 3452 | 3513 | /// This data structure is used to implement breaking to blocks. |
| 3453 | 3514 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| 3454 | 3515 | parent_bb: *const llvm.BasicBlock, |
| ... | ... | @@ -3678,6 +3739,8 @@ pub const FuncGen = struct { |
| 3678 | 3739 | .unwrap_errunion_err => try self.airErrUnionErr(inst, false), |
| 3679 | 3740 | .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true), |
| 3680 | 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), | |
| 3681 | 3744 | |
| 3682 | 3745 | .wrap_optional => try self.airWrapOptional(inst), |
| 3683 | 3746 | .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), |
| ... | ... | @@ -3732,6 +3795,12 @@ pub const FuncGen = struct { |
| 3732 | 3795 | break :blk ret_ptr; |
| 3733 | 3796 | }; |
| 3734 | 3797 | |
| 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 | 3804 | for (args) |arg| { |
| 3736 | 3805 | const param_ty = self.air.typeOf(arg); |
| 3737 | 3806 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| ... | ... | @@ -5149,6 +5218,17 @@ pub const FuncGen = struct { |
| 5149 | 5218 | return self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); |
| 5150 | 5219 | } |
| 5151 | 5220 | |
| 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 | 5232 | fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 5153 | 5233 | if (self.liveness.isUnused(inst)) return null; |
| 5154 | 5234 |
src/print_air.zig+2| ... | ... | @@ -170,6 +170,7 @@ const Writer = struct { |
| 170 | 170 | .round, |
| 171 | 171 | .trunc_float, |
| 172 | 172 | .cmp_lt_errors_len, |
| 173 | .set_err_return_trace, | |
| 173 | 174 | => try w.writeUnOp(s, inst), |
| 174 | 175 | |
| 175 | 176 | .breakpoint, |
| ... | ... | @@ -182,6 +183,7 @@ const Writer = struct { |
| 182 | 183 | .alloc, |
| 183 | 184 | .ret_ptr, |
| 184 | 185 | .arg, |
| 186 | .err_return_trace, | |
| 185 | 187 | => try w.writeTy(s, inst), |
| 186 | 188 | |
| 187 | 189 | .not, |
src/type.zig+7| ... | ... | @@ -4093,6 +4093,13 @@ pub const Type = extern union { |
| 4093 | 4093 | }; |
| 4094 | 4094 | } |
| 4095 | 4095 | |
| 4096 | pub fn isError(ty: Type) bool { | |
| 4097 | return switch (ty.zigTypeTag()) { | |
| 4098 | .ErrorUnion, .ErrorSet => true, | |
| 4099 | else => false, | |
| 4100 | }; | |
| 4101 | } | |
| 4102 | ||
| 4096 | 4103 | /// Returns whether ty, which must be an error set, includes an error `name`. |
| 4097 | 4104 | /// Might return a false negative if `ty` is an inferred error set and not fully |
| 4098 | 4105 | /// resolved yet. |