| author | |
| committer | |
| log | df74c45fa74f1247f434064d25b54998fe144906 |
| tree | 490fab3dbc21d4825a304d62123d5573acba0e25 |
| parent | 3fde14035b013646f42519189dbaa4534564d78b |
| parent | 0a7f3be42e96361ab8a9a567a11782fb81ea17da |
| signature |
stage2: error return traces19 files changed, 458 insertions(+), 49 deletions(-)
lib/compiler_rt.zig+11-13| ... | ... | @@ -198,19 +198,17 @@ comptime { |
| 198 | 198 | const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2; |
| 199 | 199 | @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); |
| 200 | 200 | |
| 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 => {}, | |
| 214 | 212 | } |
| 215 | 213 | |
| 216 | 214 | 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 |
| 846 | 846 | } |
| 847 | 847 | } |
| 848 | 848 | |
| 849 | pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn { | |
| 850 | @setCold(true); | |
| 851 | std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)}); | |
| 852 | } | |
| 853 | ||
| 854 | pub 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 | ||
| 859 | pub noinline fn returnError(maybe_st: ?*StackTrace) void { | |
| 860 | @setCold(true); | |
| 861 | const st = maybe_st orelse return; | |
| 862 | addErrRetTraceAddr(st, @returnAddress()); | |
| 863 | } | |
| 864 | ||
| 865 | pub 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 | ||
| 849 | 870 | const std = @import("std.zig"); |
| 850 | 871 | const root = @import("root"); |
lib/test_runner.zig+2-2| ... | ... | @@ -92,9 +92,9 @@ pub fn main() void { |
| 92 | 92 | fail_count += 1; |
| 93 | 93 | progress.log("FAIL ({s})\n", .{@errorName(err)}); |
| 94 | 94 | 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| { | |
| 96 | 96 | std.debug.dumpStackTrace(trace.*); |
| 97 | }; | |
| 97 | } | |
| 98 | 98 | test_node.end(); |
| 99 | 99 | }, |
| 100 | 100 | } |
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/AstGen.zig+27| ... | ... | @@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 856 | 856 | catch_token + 2 |
| 857 | 857 | else |
| 858 | 858 | 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 | }; | |
| 859 | 886 | switch (rl) { |
| 860 | 887 | .ref => return orelseCatchExpr( |
| 861 | 888 | gz, |
src/Compilation.zig+2-1| ... | ... | @@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1457 | 1457 | errdefer if (module) |zm| zm.deinit(); |
| 1458 | 1458 | |
| 1459 | 1459 | 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(), | |
| 1461 | 1462 | .ReleaseFast, .ReleaseSmall => false, |
| 1462 | 1463 | }; |
| 1463 | 1464 |
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 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+193-31| ... | ... | @@ -1411,6 +1411,45 @@ 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 | 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 | ||
| 1414 | 1453 | /// May return Value Tags: `variable`, `undef`. |
| 1415 | 1454 | /// See `resolveConstValue` for an alternative. |
| 1416 | 1455 | /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. |
| ... | ... | @@ -5236,6 +5275,9 @@ fn analyzeCall( |
| 5236 | 5275 | } |
| 5237 | 5276 | |
| 5238 | 5277 | 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 | } | |
| 5239 | 5281 | |
| 5240 | 5282 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 5241 | 5283 | args.len); |
| ... | ... | @@ -5645,6 +5687,11 @@ fn instantiateGenericCall( |
| 5645 | 5687 | |
| 5646 | 5688 | try sema.queueFullTypeResolution(new_fn_info.return_type); |
| 5647 | 5689 | } |
| 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 | ||
| 5648 | 5695 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 5649 | 5696 | runtime_args_len); |
| 5650 | 5697 | const func_inst = try block.addInst(.{ |
| ... | ... | @@ -6201,8 +6248,7 @@ fn zirErrUnionPayload( |
| 6201 | 6248 | } |
| 6202 | 6249 | try sema.requireRuntimeBlock(block, src); |
| 6203 | 6250 | 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); | |
| 6206 | 6252 | } |
| 6207 | 6253 | const result_ty = operand_ty.errorUnionPayload(); |
| 6208 | 6254 | return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); |
| ... | ... | @@ -6283,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr( |
| 6283 | 6329 | |
| 6284 | 6330 | try sema.requireRuntimeBlock(block, src); |
| 6285 | 6331 | 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); | |
| 6288 | 6333 | } |
| 6289 | 6334 | const air_tag: Air.Inst.Tag = if (initializing) |
| 6290 | 6335 | .errunion_payload_ptr_set |
| ... | ... | @@ -12607,6 +12652,23 @@ fn analyzeRet( |
| 12607 | 12652 | return always_noreturn; |
| 12608 | 12653 | } |
| 12609 | 12654 | |
| 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 | ||
| 12610 | 12672 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); |
| 12611 | 12673 | _ = try block.addUnOp(.ret, operand); |
| 12612 | 12674 | return always_noreturn; |
| ... | ... | @@ -13336,11 +13398,26 @@ fn zirErrorReturnTrace( |
| 13336 | 13398 | extended: Zir.Inst.Extended.InstData, |
| 13337 | 13399 | ) CompileError!Air.Inst.Ref { |
| 13338 | 13400 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; |
| 13401 | return sema.getErrorReturnTrace(block, src); | |
| 13402 | } | |
| 13403 | ||
| 13404 | fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref { | |
| 13339 | 13405 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); |
| 13340 | 13406 | 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"); | |
| 13344 | 13421 | } |
| 13345 | 13422 | |
| 13346 | 13423 | fn zirFrame( |
| ... | ... | @@ -16777,11 +16854,9 @@ fn explainWhyTypeIsComptime( |
| 16777 | 16854 | pub const PanicId = enum { |
| 16778 | 16855 | unreach, |
| 16779 | 16856 | unwrap_null, |
| 16780 | unwrap_errunion, | |
| 16781 | 16857 | cast_to_null, |
| 16782 | 16858 | incorrect_alignment, |
| 16783 | 16859 | invalid_error_code, |
| 16784 | index_out_of_bounds, | |
| 16785 | 16860 | cast_truncated_data, |
| 16786 | 16861 | integer_overflow, |
| 16787 | 16862 | shl_overflow, |
| ... | ... | @@ -16810,6 +16885,17 @@ fn addSafetyCheck( |
| 16810 | 16885 | |
| 16811 | 16886 | _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id); |
| 16812 | 16887 | |
| 16888 | try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); | |
| 16889 | } | |
| 16890 | ||
| 16891 | fn 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 | ||
| 16813 | 16899 | try parent_block.instructions.ensureUnusedCapacity(gpa, 1); |
| 16814 | 16900 | |
| 16815 | 16901 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + |
| ... | ... | @@ -16887,12 +16973,95 @@ fn panicWithMsg( |
| 16887 | 16973 | try Type.optional(arena, ptr_stack_trace_ty), |
| 16888 | 16974 | Value.@"null", |
| 16889 | 16975 | ); |
| 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); | |
| 16893 | 16978 | return always_noreturn; |
| 16894 | 16979 | } |
| 16895 | 16980 | |
| 16981 | fn 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 | ||
| 17024 | fn 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 | ||
| 16896 | 17065 | fn safetyPanic( |
| 16897 | 17066 | sema: *Sema, |
| 16898 | 17067 | block: *Block, |
| ... | ... | @@ -16902,11 +17071,9 @@ fn safetyPanic( |
| 16902 | 17071 | const msg = switch (panic_id) { |
| 16903 | 17072 | .unreach => "reached unreachable code", |
| 16904 | 17073 | .unwrap_null => "attempt to use null value", |
| 16905 | .unwrap_errunion => "unreachable error occurred", | |
| 16906 | 17074 | .cast_to_null => "cast causes pointer to be null", |
| 16907 | 17075 | .incorrect_alignment => "incorrect alignment", |
| 16908 | 17076 | .invalid_error_code => "invalid error code", |
| 16909 | .index_out_of_bounds => "attempt to index out of bounds", | |
| 16910 | 17077 | .cast_truncated_data => "integer cast truncated bits", |
| 16911 | 17078 | .integer_overflow => "integer overflow", |
| 16912 | 17079 | .shl_overflow => "left shift overflowed bits", |
| ... | ... | @@ -18098,8 +18265,7 @@ fn elemValArray( |
| 18098 | 18265 | if (maybe_index_val == null) { |
| 18099 | 18266 | const len_inst = try sema.addIntUnsigned(Type.usize, array_len); |
| 18100 | 18267 | 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); | |
| 18103 | 18269 | } |
| 18104 | 18270 | } |
| 18105 | 18271 | return block.addBinOp(.array_elem_val, array, elem_index); |
| ... | ... | @@ -18154,8 +18320,7 @@ fn elemPtrArray( |
| 18154 | 18320 | if (maybe_index_val == null) { |
| 18155 | 18321 | const len_inst = try sema.addIntUnsigned(Type.usize, array_len); |
| 18156 | 18322 | 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); | |
| 18159 | 18324 | } |
| 18160 | 18325 | } |
| 18161 | 18326 | return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); |
| ... | ... | @@ -18208,8 +18373,7 @@ fn elemValSlice( |
| 18208 | 18373 | else |
| 18209 | 18374 | try block.addTyOp(.slice_len, Type.usize, slice); |
| 18210 | 18375 | 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); | |
| 18213 | 18377 | } |
| 18214 | 18378 | try sema.queueFullTypeResolution(sema.typeOf(slice)); |
| 18215 | 18379 | return block.addBinOp(.slice_elem_val, slice, elem_index); |
| ... | ... | @@ -18262,8 +18426,7 @@ fn elemPtrSlice( |
| 18262 | 18426 | break :len try block.addTyOp(.slice_len, Type.usize, slice); |
| 18263 | 18427 | }; |
| 18264 | 18428 | 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); | |
| 18267 | 18430 | } |
| 18268 | 18431 | return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); |
| 18269 | 18432 | } |
| ... | ... | @@ -20948,13 +21111,11 @@ fn analyzeSlice( |
| 20948 | 21111 | break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); |
| 20949 | 21112 | } else null; |
| 20950 | 21113 | 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); | |
| 20953 | 21115 | } |
| 20954 | 21116 | |
| 20955 | 21117 | // 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); | |
| 20958 | 21119 | } |
| 20959 | 21120 | return block.addInst(.{ |
| 20960 | 21121 | .tag = .slice, |
| ... | ... | @@ -21817,11 +21978,7 @@ fn resolvePeerTypes( |
| 21817 | 21978 | info.data.sentinel = chosen_child_ty.sentinel(); |
| 21818 | 21979 | info.data.size = .Slice; |
| 21819 | 21980 | 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(); | |
| 21825 | 21982 | |
| 21826 | 21983 | const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); |
| 21827 | 21984 | const opt_ptr_ty = if (any_are_null) |
| ... | ... | @@ -21891,6 +22048,11 @@ pub fn resolveFnTypes( |
| 21891 | 22048 | ) CompileError!void { |
| 21892 | 22049 | try sema.resolveTypeFully(block, src, fn_info.return_type); |
| 21893 | 22050 | |
| 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 | ||
| 21894 | 22056 | for (fn_info.param_types) |param_ty| { |
| 21895 | 22057 | try sema.resolveTypeFully(block, src, param_ty); |
| 21896 | 22058 | } |
src/arch/aarch64/CodeGen.zig+16| ... | ... | @@ -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,20 @@ 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 | _ = 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 | ||
| 2344 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | |
| 2345 | _ = inst; | |
| 2346 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | |
| 2347 | } | |
| 2348 | ||
| 2333 | 2349 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2334 | 2350 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2335 | 2351 | 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 { |
| 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,20 @@ 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 | _ = 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 | ||
| 1857 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | |
| 1858 | _ = inst; | |
| 1859 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | |
| 1860 | } | |
| 1861 | ||
| 1846 | 1862 | /// T to E!T |
| 1847 | 1863 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1848 | 1864 | 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 { |
| 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,20 @@ 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 | _ = 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 | ||
| 1281 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | |
| 1282 | _ = inst; | |
| 1283 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | |
| 1284 | } | |
| 1285 | ||
| 1270 | 1286 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 1271 | 1287 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1272 | 1288 | 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 { |
| 655 | 655 | .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), |
| 656 | 656 | .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), |
| 657 | 657 | .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)"), | |
| 658 | 660 | |
| 659 | 661 | .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), |
| 660 | 662 | .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+16| ... | ... | @@ -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,20 @@ 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 | _ = 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 | ||
| 1869 | fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { | |
| 1870 | _ = inst; | |
| 1871 | return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); | |
| 1872 | } | |
| 1873 | ||
| 1858 | 1874 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 1859 | 1875 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1860 | 1876 | 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 |
| 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,16 @@ 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 | return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); | |
| 3455 | } | |
| 3456 | ||
| 3457 | fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 3458 | _ = inst; | |
| 3459 | return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{}); | |
| 3460 | } | |
| 3461 | ||
| 3450 | 3462 | fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3451 | 3463 | if (f.liveness.isUnused(inst)) |
| 3452 | 3464 | 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. |