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

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

stage2: error return traces

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

lib/compiler_rt.zig+11-13
...@@ -198,19 +198,17 @@ comptime {...@@ -198,19 +198,17 @@ comptime {
198 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;198 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
199 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });199 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
200200
201 if (builtin.zig_backend == .stage1) { // TODO201 switch (arch) {
202 switch (arch) {202 .i386,
203 .i386,203 .x86_64,
204 .x86_64,204 => {
205 => {205 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
206 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;206 @export(zig_probe_stack, .{
207 @export(zig_probe_stack, .{207 .name = "__zig_probe_stack",
208 .name = "__zig_probe_stack",208 .linkage = linkage,
209 .linkage = linkage,209 });
210 });210 },
211 },211 else => {},
212 else => {},
213 }
214 }212 }
215213
216 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;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,5 +846,26 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846 }846 }
847}847}
848848
849pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850 @setCold(true);
851 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
852}
853
854pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
855 @setCold(true);
856 std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len });
857}
858
859pub noinline fn returnError(maybe_st: ?*StackTrace) void {
860 @setCold(true);
861 const st = maybe_st orelse return;
862 addErrRetTraceAddr(st, @returnAddress());
863}
864
865pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
866 st.instruction_addresses[st.index & (st.instruction_addresses.len - 1)] = addr;
867 st.index +%= 1;
868}
869
849const std = @import("std.zig");870const std = @import("std.zig");
850const root = @import("root");871const root = @import("root");
lib/test_runner.zig+2-2
...@@ -92,9 +92,9 @@ pub fn main() void {...@@ -92,9 +92,9 @@ pub fn main() void {
92 fail_count += 1;92 fail_count += 1;
93 progress.log("FAIL ({s})\n", .{@errorName(err)});93 progress.log("FAIL ({s})\n", .{@errorName(err)});
94 if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)});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 std.debug.dumpStackTrace(trace.*);96 std.debug.dumpStackTrace(trace.*);
97 };97 }
98 test_node.end();98 test_node.end();
99 },99 },
100 }100 }
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/AstGen.zig+27
...@@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
856 catch_token + 2856 catch_token + 2
857 else857 else
858 null;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 switch (rl) {886 switch (rl) {
860 .ref => return orelseCatchExpr(887 .ref => return orelseCatchExpr(
861 gz,888 gz,
src/Compilation.zig+2-1
...@@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1457 errdefer if (module) |zm| zm.deinit();1457 errdefer if (module) |zm| zm.deinit();
14581458
1459 const error_return_tracing = !strip and switch (options.optimize_mode) {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 .ReleaseFast, .ReleaseSmall => false,1462 .ReleaseFast, .ReleaseSmall => false,
1462 };1463 };
14631464
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 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+193-31
...@@ -1411,6 +1411,45 @@ fn analyzeAsType(...@@ -1411,6 +1411,45 @@ 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 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/// May return Value Tags: `variable`, `undef`.1453/// May return Value Tags: `variable`, `undef`.
1415/// See `resolveConstValue` for an alternative.1454/// See `resolveConstValue` for an alternative.
1416/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.1455/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
...@@ -5236,6 +5275,9 @@ fn analyzeCall(...@@ -5236,6 +5275,9 @@ fn analyzeCall(
5236 }5275 }
52375276
5238 try sema.queueFullTypeResolution(func_ty_info.return_type);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 }
52395281
5240 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +5282 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
5241 args.len);5283 args.len);
...@@ -5645,6 +5687,11 @@ fn instantiateGenericCall(...@@ -5645,6 +5687,11 @@ fn instantiateGenericCall(
56455687
5646 try sema.queueFullTypeResolution(new_fn_info.return_type);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 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +5695 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
5649 runtime_args_len);5696 runtime_args_len);
5650 const func_inst = try block.addInst(.{5697 const func_inst = try block.addInst(.{
...@@ -6201,8 +6248,7 @@ fn zirErrUnionPayload(...@@ -6201,8 +6248,7 @@ fn zirErrUnionPayload(
6201 }6248 }
6202 try sema.requireRuntimeBlock(block, src);6249 try sema.requireRuntimeBlock(block, src);
6203 if (safety_check and block.wantSafety()) {6250 if (safety_check and block.wantSafety()) {
6204 const is_non_err = try block.addUnOp(.is_err, operand);6251 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
6205 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6206 }6252 }
6207 const result_ty = operand_ty.errorUnionPayload();6253 const result_ty = operand_ty.errorUnionPayload();
6208 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);6254 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
...@@ -6283,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(...@@ -6283,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(
62836329
6284 try sema.requireRuntimeBlock(block, src);6330 try sema.requireRuntimeBlock(block, src);
6285 if (safety_check and block.wantSafety()) {6331 if (safety_check and block.wantSafety()) {
6286 const is_non_err = try block.addUnOp(.is_err, operand);6332 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
6287 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6288 }6333 }
6289 const air_tag: Air.Inst.Tag = if (initializing)6334 const air_tag: Air.Inst.Tag = if (initializing)
6290 .errunion_payload_ptr_set6335 .errunion_payload_ptr_set
...@@ -12607,6 +12652,23 @@ fn analyzeRet(...@@ -12607,6 +12652,23 @@ fn analyzeRet(
12607 return always_noreturn;12652 return always_noreturn;
12608 }12653 }
1260912654
12655 // TODO implement this feature in all the backends and then delete this check.
12656 const backend_supports_error_return_tracing =
12657 sema.mod.comp.bin_file.options.use_llvm;
12658
12659 if ((sema.fn_ret_ty.zigTypeTag() == .ErrorSet or sema.typeOf(uncasted_operand).zigTypeTag() == .ErrorUnion) and
12660 sema.mod.comp.bin_file.options.error_return_tracing and
12661 backend_supports_error_return_tracing)
12662 {
12663 const return_err_fn = try sema.getBuiltin(block, src, "returnError");
12664 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
12665 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
12666 const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
12667 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
12668 const args: [1]Air.Inst.Ref = .{err_return_trace};
12669 _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args);
12670 }
12671
12610 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);12672 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
12611 _ = try block.addUnOp(.ret, operand);12673 _ = try block.addUnOp(.ret, operand);
12612 return always_noreturn;12674 return always_noreturn;
...@@ -13336,11 +13398,26 @@ fn zirErrorReturnTrace(...@@ -13336,11 +13398,26 @@ fn zirErrorReturnTrace(
13336 extended: Zir.Inst.Extended.InstData,13398 extended: Zir.Inst.Extended.InstData,
13337) CompileError!Air.Inst.Ref {13399) CompileError!Air.Inst.Ref {
13338 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };13400 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
13401 return sema.getErrorReturnTrace(block, src);
13402}
13403
13404fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref {
13339 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");13405 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);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);13407 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/1125913408
13343 return sema.addConstant(opt_stack_trace_ty, Value.@"null");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}
1334513422
13346fn zirFrame(13423fn zirFrame(
...@@ -16777,11 +16854,9 @@ fn explainWhyTypeIsComptime(...@@ -16777,11 +16854,9 @@ fn explainWhyTypeIsComptime(
16777pub const PanicId = enum {16854pub const PanicId = enum {
16778 unreach,16855 unreach,
16779 unwrap_null,16856 unwrap_null,
16780 unwrap_errunion,
16781 cast_to_null,16857 cast_to_null,
16782 incorrect_alignment,16858 incorrect_alignment,
16783 invalid_error_code,16859 invalid_error_code,
16784 index_out_of_bounds,
16785 cast_truncated_data,16860 cast_truncated_data,
16786 integer_overflow,16861 integer_overflow,
16787 shl_overflow,16862 shl_overflow,
...@@ -16810,6 +16885,17 @@ fn addSafetyCheck(...@@ -16810,6 +16885,17 @@ fn addSafetyCheck(
1681016885
16811 _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);16886 _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);
1681216887
16888 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
16889}
16890
16891fn addSafetyCheckExtra(
16892 sema: *Sema,
16893 parent_block: *Block,
16894 ok: Air.Inst.Ref,
16895 fail_block: *Block,
16896) !void {
16897 const gpa = sema.gpa;
16898
16813 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);16899 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
1681416900
16815 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +16901 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
...@@ -16887,12 +16973,95 @@ fn panicWithMsg(...@@ -16887,12 +16973,95 @@ fn panicWithMsg(
16887 try Type.optional(arena, ptr_stack_trace_ty),16973 try Type.optional(arena, ptr_stack_trace_ty),
16888 Value.@"null",16974 Value.@"null",
16889 );16975 );
16890 const args = try arena.create([2]Air.Inst.Ref);16976 const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace };
16891 args.* = .{ msg_inst, null_stack_trace };16977 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args);
16892 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
16893 return always_noreturn;16978 return always_noreturn;
16894}16979}
1689516980
16981fn panicUnwrapError(
16982 sema: *Sema,
16983 parent_block: *Block,
16984 src: LazySrcLoc,
16985 operand: Air.Inst.Ref,
16986 unwrap_err_tag: Air.Inst.Tag,
16987 is_non_err_tag: Air.Inst.Tag,
16988) !void {
16989 const ok = try parent_block.addUnOp(is_non_err_tag, operand);
16990 const gpa = sema.gpa;
16991
16992 var fail_block: Block = .{
16993 .parent = parent_block,
16994 .sema = sema,
16995 .src_decl = parent_block.src_decl,
16996 .namespace = parent_block.namespace,
16997 .wip_capture_scope = parent_block.wip_capture_scope,
16998 .instructions = .{},
16999 .inlining = parent_block.inlining,
17000 .is_comptime = parent_block.is_comptime,
17001 };
17002
17003 defer fail_block.instructions.deinit(gpa);
17004
17005 {
17006 const this_feature_is_implemented_in_the_backend =
17007 sema.mod.comp.bin_file.options.use_llvm;
17008
17009 if (!this_feature_is_implemented_in_the_backend) {
17010 // TODO implement this feature in all the backends and then delete this branch
17011 _ = try fail_block.addNoOp(.breakpoint);
17012 _ = try fail_block.addNoOp(.unreach);
17013 } else {
17014 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError");
17015 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
17016 const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src);
17017 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
17018 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
17019 }
17020 }
17021 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
17022}
17023
17024fn panicIndexOutOfBounds(
17025 sema: *Sema,
17026 parent_block: *Block,
17027 src: LazySrcLoc,
17028 index: Air.Inst.Ref,
17029 len: Air.Inst.Ref,
17030 cmp_op: Air.Inst.Tag,
17031) !void {
17032 const ok = try parent_block.addBinOp(cmp_op, index, len);
17033 const gpa = sema.gpa;
17034
17035 var fail_block: Block = .{
17036 .parent = parent_block,
17037 .sema = sema,
17038 .src_decl = parent_block.src_decl,
17039 .namespace = parent_block.namespace,
17040 .wip_capture_scope = parent_block.wip_capture_scope,
17041 .instructions = .{},
17042 .inlining = parent_block.inlining,
17043 .is_comptime = parent_block.is_comptime,
17044 };
17045
17046 defer fail_block.instructions.deinit(gpa);
17047
17048 {
17049 const this_feature_is_implemented_in_the_backend =
17050 sema.mod.comp.bin_file.options.use_llvm;
17051
17052 if (!this_feature_is_implemented_in_the_backend) {
17053 // TODO implement this feature in all the backends and then delete this branch
17054 _ = try fail_block.addNoOp(.breakpoint);
17055 _ = try fail_block.addNoOp(.unreach);
17056 } else {
17057 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicOutOfBounds");
17058 const args: [2]Air.Inst.Ref = .{ index, len };
17059 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
17060 }
17061 }
17062 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
17063}
17064
16896fn safetyPanic(17065fn safetyPanic(
16897 sema: *Sema,17066 sema: *Sema,
16898 block: *Block,17067 block: *Block,
...@@ -16902,11 +17071,9 @@ fn safetyPanic(...@@ -16902,11 +17071,9 @@ fn safetyPanic(
16902 const msg = switch (panic_id) {17071 const msg = switch (panic_id) {
16903 .unreach => "reached unreachable code",17072 .unreach => "reached unreachable code",
16904 .unwrap_null => "attempt to use null value",17073 .unwrap_null => "attempt to use null value",
16905 .unwrap_errunion => "unreachable error occurred",
16906 .cast_to_null => "cast causes pointer to be null",17074 .cast_to_null => "cast causes pointer to be null",
16907 .incorrect_alignment => "incorrect alignment",17075 .incorrect_alignment => "incorrect alignment",
16908 .invalid_error_code => "invalid error code",17076 .invalid_error_code => "invalid error code",
16909 .index_out_of_bounds => "attempt to index out of bounds",
16910 .cast_truncated_data => "integer cast truncated bits",17077 .cast_truncated_data => "integer cast truncated bits",
16911 .integer_overflow => "integer overflow",17078 .integer_overflow => "integer overflow",
16912 .shl_overflow => "left shift overflowed bits",17079 .shl_overflow => "left shift overflowed bits",
...@@ -18098,8 +18265,7 @@ fn elemValArray(...@@ -18098,8 +18265,7 @@ fn elemValArray(
18098 if (maybe_index_val == null) {18265 if (maybe_index_val == null) {
18099 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);18266 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
18100 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;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);18268 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18102 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18103 }18269 }
18104 }18270 }
18105 return block.addBinOp(.array_elem_val, array, elem_index);18271 return block.addBinOp(.array_elem_val, array, elem_index);
...@@ -18154,8 +18320,7 @@ fn elemPtrArray(...@@ -18154,8 +18320,7 @@ fn elemPtrArray(
18154 if (maybe_index_val == null) {18320 if (maybe_index_val == null) {
18155 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);18321 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
18156 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;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);18323 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18158 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18159 }18324 }
18160 }18325 }
18161 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);18326 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
...@@ -18208,8 +18373,7 @@ fn elemValSlice(...@@ -18208,8 +18373,7 @@ fn elemValSlice(
18208 else18373 else
18209 try block.addTyOp(.slice_len, Type.usize, slice);18374 try block.addTyOp(.slice_len, Type.usize, slice);
18210 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;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);18376 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18212 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18213 }18377 }
18214 try sema.queueFullTypeResolution(sema.typeOf(slice));18378 try sema.queueFullTypeResolution(sema.typeOf(slice));
18215 return block.addBinOp(.slice_elem_val, slice, elem_index);18379 return block.addBinOp(.slice_elem_val, slice, elem_index);
...@@ -18262,8 +18426,7 @@ fn elemPtrSlice(...@@ -18262,8 +18426,7 @@ fn elemPtrSlice(
18262 break :len try block.addTyOp(.slice_len, Type.usize, slice);18426 break :len try block.addTyOp(.slice_len, Type.usize, slice);
18263 };18427 };
18264 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;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);18429 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18266 try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
18267 }18430 }
18268 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);18431 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
18269}18432}
...@@ -20948,13 +21111,11 @@ fn analyzeSlice(...@@ -20948,13 +21111,11 @@ fn analyzeSlice(
20948 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);21111 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
20949 } else null;21112 } else null;
20950 if (opt_len_inst) |len_inst| {21113 if (opt_len_inst) |len_inst| {
20951 const end_is_in_bounds = try block.addBinOp(.cmp_lte, end, len_inst);21114 try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);
20952 try sema.addSafetyCheck(block, end_is_in_bounds, .index_out_of_bounds);
20953 }21115 }
2095421116
20955 // requirement: start <= end21117 // requirement: start <= end
20956 const start_is_in_bounds = try block.addBinOp(.cmp_lte, start, end);21118 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
20957 try sema.addSafetyCheck(block, start_is_in_bounds, .index_out_of_bounds);
20958 }21119 }
20959 return block.addInst(.{21120 return block.addInst(.{
20960 .tag = .slice,21121 .tag = .slice,
...@@ -21817,11 +21978,7 @@ fn resolvePeerTypes(...@@ -21817,11 +21978,7 @@ fn resolvePeerTypes(
21817 info.data.sentinel = chosen_child_ty.sentinel();21978 info.data.sentinel = chosen_child_ty.sentinel();
21818 info.data.size = .Slice;21979 info.data.size = .Slice;
21819 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());21980 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
21820 info.data.pointee_type = switch (chosen_child_ty.tag()) {21981 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 };
2182521982
21826 const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);21983 const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
21827 const opt_ptr_ty = if (any_are_null)21984 const opt_ptr_ty = if (any_are_null)
...@@ -21891,6 +22048,11 @@ pub fn resolveFnTypes(...@@ -21891,6 +22048,11 @@ pub fn resolveFnTypes(
21891) CompileError!void {22048) CompileError!void {
21892 try sema.resolveTypeFully(block, src, fn_info.return_type);22049 try sema.resolveTypeFully(block, src, fn_info.return_type);
2189322050
22051 if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) {
22052 // Ensure the type exists so that backends can assume that.
22053 _ = try sema.getBuiltinType(block, src, "StackTrace");
22054 }
22055
21894 for (fn_info.param_types) |param_ty| {22056 for (fn_info.param_types) |param_ty| {
21895 try sema.resolveTypeFully(block, src, param_ty);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,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,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -2330,6 +2332,20 @@ 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 _ = inst;
2337 const result: MCValue = if (self.liveness.isUnused(inst))
2338 .dead
2339 else
2340 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
2341 return self.finishAir(inst, result, .{ .none, .none, .none });
2342}
2343
2344fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
2345 _ = inst;
2346 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
2347}
2348
2333fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {2349fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
2334 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2350 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2335 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {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,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,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1843,6 +1845,20 @@ 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 _ = inst;
1850 const result: MCValue = if (self.liveness.isUnused(inst))
1851 .dead
1852 else
1853 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1854 return self.finishAir(inst, result, .{ .none, .none, .none });
1855}
1856
1857fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1858 _ = inst;
1859 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1860}
1861
1846/// T to E!T1862/// T to E!T
1847fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {1863fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1848 const ty_op = self.air.instructions.items(.data)[inst].ty_op;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,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,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1267,6 +1269,20 @@ 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 _ = inst;
1274 const result: MCValue = if (self.liveness.isUnused(inst))
1275 .dead
1276 else
1277 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1278 return self.finishAir(inst, result, .{ .none, .none, .none });
1279}
1280
1281fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1282 _ = inst;
1283 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1284}
1285
1270fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {1286fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1271 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1287 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1272 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {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,6 +655,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
655 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),655 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
656 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),656 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
657 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),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)"),
658660
659 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),661 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
660 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),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,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+16
...@@ -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,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {...@@ -1855,6 +1857,20 @@ 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 _ = inst;
1862 const result: MCValue = if (self.liveness.isUnused(inst))
1863 .dead
1864 else
1865 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
1866 return self.finishAir(inst, result, .{ .none, .none, .none });
1867}
1868
1869fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
1870 _ = inst;
1871 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
1872}
1873
1858fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {1874fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1859 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1875 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1860 if (self.liveness.isUnused(inst)) {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,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,16 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3447,6 +3449,16 @@ 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 return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
3455}
3456
3457fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
3458 _ = inst;
3459 return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
3460}
3461
3450fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {3462fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
3451 if (f.liveness.isUnused(inst))3463 if (f.liveness.isUnused(inst))
3452 return CValue.none;3464 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.