mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #11501 from Vexu/stage2-err-return-trace
stage2: error return traces
This commit is contained in:
commit
df74c45fa7
@ -198,19 +198,17 @@ comptime {
|
||||
const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
|
||||
@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
|
||||
|
||||
if (builtin.zig_backend == .stage1) { // TODO
|
||||
switch (arch) {
|
||||
.i386,
|
||||
.x86_64,
|
||||
=> {
|
||||
const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
|
||||
@export(zig_probe_stack, .{
|
||||
.name = "__zig_probe_stack",
|
||||
.linkage = linkage,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (arch) {
|
||||
.i386,
|
||||
.x86_64,
|
||||
=> {
|
||||
const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
|
||||
@export(zig_probe_stack, .{
|
||||
.name = "__zig_probe_stack",
|
||||
.linkage = linkage,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
|
||||
|
||||
@ -846,5 +846,26 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
|
||||
}
|
||||
}
|
||||
|
||||
pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
|
||||
@setCold(true);
|
||||
std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
|
||||
}
|
||||
|
||||
pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
|
||||
@setCold(true);
|
||||
std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len });
|
||||
}
|
||||
|
||||
pub noinline fn returnError(maybe_st: ?*StackTrace) void {
|
||||
@setCold(true);
|
||||
const st = maybe_st orelse return;
|
||||
addErrRetTraceAddr(st, @returnAddress());
|
||||
}
|
||||
|
||||
pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
|
||||
st.instruction_addresses[st.index & (st.instruction_addresses.len - 1)] = addr;
|
||||
st.index +%= 1;
|
||||
}
|
||||
|
||||
const std = @import("std.zig");
|
||||
const root = @import("root");
|
||||
|
||||
@ -92,9 +92,9 @@ pub fn main() void {
|
||||
fail_count += 1;
|
||||
progress.log("FAIL ({s})\n", .{@errorName(err)});
|
||||
if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)});
|
||||
if (builtin.zig_backend != .stage2_llvm) if (@errorReturnTrace()) |trace| {
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
std.debug.dumpStackTrace(trace.*);
|
||||
};
|
||||
}
|
||||
test_node.end();
|
||||
},
|
||||
}
|
||||
|
||||
@ -649,6 +649,12 @@ pub const Inst = struct {
|
||||
/// flush().
|
||||
cmp_lt_errors_len,
|
||||
|
||||
/// Returns pointer to current error return trace.
|
||||
err_return_trace,
|
||||
|
||||
/// Sets the operand as the current error return trace,
|
||||
set_err_return_trace,
|
||||
|
||||
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
|
||||
return switch (op) {
|
||||
.lt => .cmp_lt,
|
||||
@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.alloc,
|
||||
.ret_ptr,
|
||||
.arg,
|
||||
.err_return_trace,
|
||||
=> return datas[inst].ty,
|
||||
|
||||
.assembly,
|
||||
@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.memcpy,
|
||||
.set_union_tag,
|
||||
.prefetch,
|
||||
.set_err_return_trace,
|
||||
=> return Type.void,
|
||||
|
||||
.ptrtoint,
|
||||
|
||||
@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
|
||||
catch_token + 2
|
||||
else
|
||||
null;
|
||||
|
||||
var rhs = node_datas[node].rhs;
|
||||
while (true) switch (node_tags[rhs]) {
|
||||
.grouped_expression => rhs = node_datas[rhs].lhs,
|
||||
.unreachable_literal => {
|
||||
if (payload_token != null and mem.eql(u8, tree.tokenSlice(payload_token.?), "_")) {
|
||||
return astgen.failTok(payload_token.?, "discard of error capture; omit it instead", .{});
|
||||
} else if (payload_token != null) {
|
||||
return astgen.failTok(payload_token.?, "unused capture", .{});
|
||||
}
|
||||
const lhs = node_datas[node].lhs;
|
||||
|
||||
const operand = try reachableExpr(gz, scope, switch (rl) {
|
||||
.ref => .ref,
|
||||
else => .none,
|
||||
}, lhs, lhs);
|
||||
const result = try gz.addUnNode(switch (rl) {
|
||||
.ref => .err_union_payload_safe_ptr,
|
||||
else => .err_union_payload_safe,
|
||||
}, operand, node);
|
||||
switch (rl) {
|
||||
.none, .coerced_ty, .discard, .ref => return result,
|
||||
else => return rvalue(gz, rl, result, lhs),
|
||||
}
|
||||
},
|
||||
else => break,
|
||||
};
|
||||
switch (rl) {
|
||||
.ref => return orelseCatchExpr(
|
||||
gz,
|
||||
|
||||
@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
||||
errdefer if (module) |zm| zm.deinit();
|
||||
|
||||
const error_return_tracing = !strip and switch (options.optimize_mode) {
|
||||
.Debug, .ReleaseSafe => true,
|
||||
.Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and
|
||||
!options.target.cpu.arch.isBpf(),
|
||||
.ReleaseFast, .ReleaseSmall => false,
|
||||
};
|
||||
|
||||
|
||||
@ -362,6 +362,7 @@ fn analyzeInst(
|
||||
.ret_addr,
|
||||
.frame_addr,
|
||||
.wasm_memory_size,
|
||||
.err_return_trace,
|
||||
=> return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
|
||||
|
||||
.not,
|
||||
@ -434,6 +435,7 @@ fn analyzeInst(
|
||||
.round,
|
||||
.trunc_float,
|
||||
.cmp_lt_errors_len,
|
||||
.set_err_return_trace,
|
||||
=> {
|
||||
const operand = inst_datas[inst].un_op;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
|
||||
|
||||
@ -1427,6 +1427,7 @@ pub const Fn = struct {
|
||||
state: Analysis,
|
||||
is_cold: bool = false,
|
||||
is_noinline: bool = false,
|
||||
calls_or_awaits_errorable_fn: bool = false,
|
||||
|
||||
/// Any inferred error sets that this function owns, both its own inferred error set and
|
||||
/// 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 {
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
// reset in case calls to errorable functions are removed.
|
||||
func.calls_or_awaits_errorable_fn = false;
|
||||
|
||||
// First few indexes of extra are reserved and set at the end.
|
||||
const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
|
||||
try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
|
||||
@ -4936,6 +4940,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
|
||||
func.state = .in_progress;
|
||||
log.debug("set {s} to in_progress", .{decl.name});
|
||||
|
||||
const last_arg_index = inner_block.instructions.items.len;
|
||||
|
||||
sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
|
||||
// TODO make these unreachable instead of @panic
|
||||
error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
|
||||
@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
// If we don't get an error return trace from a caller, create our own.
|
||||
if (func.calls_or_awaits_errorable_fn and
|
||||
mod.comp.bin_file.options.error_return_tracing and
|
||||
!sema.fn_ret_ty.isError())
|
||||
{
|
||||
sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
|
||||
// TODO make these unreachable instead of @panic
|
||||
error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"),
|
||||
error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
|
||||
error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
|
||||
error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"),
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
// Copy the block into place and mark that as the main block.
|
||||
|
||||
224
src/Sema.zig
224
src/Sema.zig
@ -1411,6 +1411,45 @@ fn analyzeAsType(
|
||||
return ty.copy(sema.arena);
|
||||
}
|
||||
|
||||
pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
|
||||
const backend_supports_error_return_tracing =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
if (!backend_supports_error_return_tracing) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
return;
|
||||
}
|
||||
|
||||
var err_trace_block = block.makeSubBlock();
|
||||
err_trace_block.is_comptime = false;
|
||||
defer err_trace_block.instructions.deinit(sema.gpa);
|
||||
|
||||
const src: LazySrcLoc = .unneeded;
|
||||
|
||||
// var addrs: [err_return_trace_addr_count]usize = undefined;
|
||||
const err_return_trace_addr_count = 32;
|
||||
const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod);
|
||||
const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty));
|
||||
|
||||
// var st: StackTrace = undefined;
|
||||
const unresolved_stack_trace_ty = try sema.getBuiltinType(&err_trace_block, src, "StackTrace");
|
||||
const stack_trace_ty = try sema.resolveTypeFields(&err_trace_block, src, unresolved_stack_trace_ty);
|
||||
const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
|
||||
|
||||
// st.instruction_addresses = &addrs;
|
||||
const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src);
|
||||
try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
|
||||
|
||||
// st.index = 0;
|
||||
const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src);
|
||||
const zero = try sema.addConstant(Type.usize, Value.zero);
|
||||
try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
|
||||
|
||||
// @errorReturnTrace() = &st;
|
||||
_ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
|
||||
|
||||
try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items);
|
||||
}
|
||||
|
||||
/// May return Value Tags: `variable`, `undef`.
|
||||
/// See `resolveConstValue` for an alternative.
|
||||
/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
|
||||
@ -5236,6 +5275,9 @@ fn analyzeCall(
|
||||
}
|
||||
|
||||
try sema.queueFullTypeResolution(func_ty_info.return_type);
|
||||
if (sema.owner_func != null and func_ty_info.return_type.isError()) {
|
||||
sema.owner_func.?.calls_or_awaits_errorable_fn = true;
|
||||
}
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
|
||||
args.len);
|
||||
@ -5645,6 +5687,11 @@ fn instantiateGenericCall(
|
||||
|
||||
try sema.queueFullTypeResolution(new_fn_info.return_type);
|
||||
}
|
||||
|
||||
if (sema.owner_func != null and new_fn_info.return_type.isError()) {
|
||||
sema.owner_func.?.calls_or_awaits_errorable_fn = true;
|
||||
}
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
|
||||
runtime_args_len);
|
||||
const func_inst = try block.addInst(.{
|
||||
@ -6201,8 +6248,7 @@ fn zirErrUnionPayload(
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
if (safety_check and block.wantSafety()) {
|
||||
const is_non_err = try block.addUnOp(.is_err, operand);
|
||||
try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
|
||||
try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
|
||||
}
|
||||
const result_ty = operand_ty.errorUnionPayload();
|
||||
return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
|
||||
@ -6283,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
if (safety_check and block.wantSafety()) {
|
||||
const is_non_err = try block.addUnOp(.is_err, operand);
|
||||
try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
|
||||
try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
|
||||
}
|
||||
const air_tag: Air.Inst.Tag = if (initializing)
|
||||
.errunion_payload_ptr_set
|
||||
@ -12607,6 +12652,23 @@ fn analyzeRet(
|
||||
return always_noreturn;
|
||||
}
|
||||
|
||||
// TODO implement this feature in all the backends and then delete this check.
|
||||
const backend_supports_error_return_tracing =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if ((sema.fn_ret_ty.zigTypeTag() == .ErrorSet or sema.typeOf(uncasted_operand).zigTypeTag() == .ErrorUnion) and
|
||||
sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
backend_supports_error_return_tracing)
|
||||
{
|
||||
const return_err_fn = try sema.getBuiltin(block, src, "returnError");
|
||||
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
|
||||
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
|
||||
const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
|
||||
const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
|
||||
const args: [1]Air.Inst.Ref = .{err_return_trace};
|
||||
_ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args);
|
||||
}
|
||||
|
||||
try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
|
||||
_ = try block.addUnOp(.ret, operand);
|
||||
return always_noreturn;
|
||||
@ -13336,11 +13398,26 @@ fn zirErrorReturnTrace(
|
||||
extended: Zir.Inst.Extended.InstData,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
|
||||
return sema.getErrorReturnTrace(block, src);
|
||||
}
|
||||
|
||||
fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref {
|
||||
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
|
||||
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
|
||||
const opt_stack_trace_ty = try Type.optional(sema.arena, stack_trace_ty);
|
||||
// https://github.com/ziglang/zig/issues/11259
|
||||
return sema.addConstant(opt_stack_trace_ty, Value.@"null");
|
||||
const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
|
||||
|
||||
// TODO implement this feature in all the backends and then delete this check.
|
||||
const backend_supports_error_return_tracing =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (sema.owner_func != null and
|
||||
sema.owner_func.?.calls_or_awaits_errorable_fn and
|
||||
sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
backend_supports_error_return_tracing)
|
||||
{
|
||||
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
|
||||
}
|
||||
return sema.addConstant(opt_ptr_stack_trace_ty, Value.@"null");
|
||||
}
|
||||
|
||||
fn zirFrame(
|
||||
@ -16777,11 +16854,9 @@ fn explainWhyTypeIsComptime(
|
||||
pub const PanicId = enum {
|
||||
unreach,
|
||||
unwrap_null,
|
||||
unwrap_errunion,
|
||||
cast_to_null,
|
||||
incorrect_alignment,
|
||||
invalid_error_code,
|
||||
index_out_of_bounds,
|
||||
cast_truncated_data,
|
||||
integer_overflow,
|
||||
shl_overflow,
|
||||
@ -16810,6 +16885,17 @@ fn addSafetyCheck(
|
||||
|
||||
_ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);
|
||||
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
fn addSafetyCheckExtra(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
ok: Air.Inst.Ref,
|
||||
fail_block: *Block,
|
||||
) !void {
|
||||
const gpa = sema.gpa;
|
||||
|
||||
try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
|
||||
@ -16887,12 +16973,95 @@ fn panicWithMsg(
|
||||
try Type.optional(arena, ptr_stack_trace_ty),
|
||||
Value.@"null",
|
||||
);
|
||||
const args = try arena.create([2]Air.Inst.Ref);
|
||||
args.* = .{ msg_inst, null_stack_trace };
|
||||
_ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
|
||||
const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace };
|
||||
_ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args);
|
||||
return always_noreturn;
|
||||
}
|
||||
|
||||
fn panicUnwrapError(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
src: LazySrcLoc,
|
||||
operand: Air.Inst.Ref,
|
||||
unwrap_err_tag: Air.Inst.Tag,
|
||||
is_non_err_tag: Air.Inst.Tag,
|
||||
) !void {
|
||||
const ok = try parent_block.addUnOp(is_non_err_tag, operand);
|
||||
const gpa = sema.gpa;
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
.src_decl = parent_block.src_decl,
|
||||
.namespace = parent_block.namespace,
|
||||
.wip_capture_scope = parent_block.wip_capture_scope,
|
||||
.instructions = .{},
|
||||
.inlining = parent_block.inlining,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
{
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
_ = try fail_block.addNoOp(.breakpoint);
|
||||
_ = try fail_block.addNoOp(.unreach);
|
||||
} else {
|
||||
const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError");
|
||||
const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
|
||||
const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src);
|
||||
const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
|
||||
_ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
|
||||
}
|
||||
}
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
fn panicIndexOutOfBounds(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
src: LazySrcLoc,
|
||||
index: Air.Inst.Ref,
|
||||
len: Air.Inst.Ref,
|
||||
cmp_op: Air.Inst.Tag,
|
||||
) !void {
|
||||
const ok = try parent_block.addBinOp(cmp_op, index, len);
|
||||
const gpa = sema.gpa;
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
.src_decl = parent_block.src_decl,
|
||||
.namespace = parent_block.namespace,
|
||||
.wip_capture_scope = parent_block.wip_capture_scope,
|
||||
.instructions = .{},
|
||||
.inlining = parent_block.inlining,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
{
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
_ = try fail_block.addNoOp(.breakpoint);
|
||||
_ = try fail_block.addNoOp(.unreach);
|
||||
} else {
|
||||
const panic_fn = try sema.getBuiltin(&fail_block, src, "panicOutOfBounds");
|
||||
const args: [2]Air.Inst.Ref = .{ index, len };
|
||||
_ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
|
||||
}
|
||||
}
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
fn safetyPanic(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -16902,11 +17071,9 @@ fn safetyPanic(
|
||||
const msg = switch (panic_id) {
|
||||
.unreach => "reached unreachable code",
|
||||
.unwrap_null => "attempt to use null value",
|
||||
.unwrap_errunion => "unreachable error occurred",
|
||||
.cast_to_null => "cast causes pointer to be null",
|
||||
.incorrect_alignment => "incorrect alignment",
|
||||
.invalid_error_code => "invalid error code",
|
||||
.index_out_of_bounds => "attempt to index out of bounds",
|
||||
.cast_truncated_data => "integer cast truncated bits",
|
||||
.integer_overflow => "integer overflow",
|
||||
.shl_overflow => "left shift overflowed bits",
|
||||
@ -18098,8 +18265,7 @@ fn elemValArray(
|
||||
if (maybe_index_val == null) {
|
||||
const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
|
||||
const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
|
||||
const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
|
||||
try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
|
||||
}
|
||||
}
|
||||
return block.addBinOp(.array_elem_val, array, elem_index);
|
||||
@ -18154,8 +18320,7 @@ fn elemPtrArray(
|
||||
if (maybe_index_val == null) {
|
||||
const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
|
||||
const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
|
||||
const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
|
||||
try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
|
||||
}
|
||||
}
|
||||
return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
|
||||
@ -18208,8 +18373,7 @@ fn elemValSlice(
|
||||
else
|
||||
try block.addTyOp(.slice_len, Type.usize, slice);
|
||||
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
|
||||
const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
|
||||
try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
|
||||
}
|
||||
try sema.queueFullTypeResolution(sema.typeOf(slice));
|
||||
return block.addBinOp(.slice_elem_val, slice, elem_index);
|
||||
@ -18262,8 +18426,7 @@ fn elemPtrSlice(
|
||||
break :len try block.addTyOp(.slice_len, Type.usize, slice);
|
||||
};
|
||||
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
|
||||
const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst);
|
||||
try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
|
||||
}
|
||||
return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
|
||||
}
|
||||
@ -20948,13 +21111,11 @@ fn analyzeSlice(
|
||||
break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
|
||||
} else null;
|
||||
if (opt_len_inst) |len_inst| {
|
||||
const end_is_in_bounds = try block.addBinOp(.cmp_lte, end, len_inst);
|
||||
try sema.addSafetyCheck(block, end_is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);
|
||||
}
|
||||
|
||||
// requirement: start <= end
|
||||
const start_is_in_bounds = try block.addBinOp(.cmp_lte, start, end);
|
||||
try sema.addSafetyCheck(block, start_is_in_bounds, .index_out_of_bounds);
|
||||
try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
|
||||
}
|
||||
return block.addInst(.{
|
||||
.tag = .slice,
|
||||
@ -21817,11 +21978,7 @@ fn resolvePeerTypes(
|
||||
info.data.sentinel = chosen_child_ty.sentinel();
|
||||
info.data.size = .Slice;
|
||||
info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
|
||||
info.data.pointee_type = switch (chosen_child_ty.tag()) {
|
||||
.array => chosen_child_ty.elemType2(),
|
||||
.array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),
|
||||
else => unreachable,
|
||||
};
|
||||
info.data.pointee_type = chosen_child_ty.elemType2();
|
||||
|
||||
const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
|
||||
const opt_ptr_ty = if (any_are_null)
|
||||
@ -21891,6 +22048,11 @@ pub fn resolveFnTypes(
|
||||
) CompileError!void {
|
||||
try sema.resolveTypeFully(block, src, fn_info.return_type);
|
||||
|
||||
if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) {
|
||||
// Ensure the type exists so that backends can assume that.
|
||||
_ = try sema.getBuiltinType(block, src, "StackTrace");
|
||||
}
|
||||
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
try sema.resolveTypeFully(block, src, param_ty);
|
||||
}
|
||||
|
||||
@ -718,6 +718,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
|
||||
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
|
||||
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
|
||||
.err_return_trace => try self.airErrReturnTrace(inst),
|
||||
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
|
||||
|
||||
.wrap_optional => try self.airWrapOptional(inst),
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
@ -2330,6 +2332,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst))
|
||||
.dead
|
||||
else
|
||||
return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
return self.finishAir(inst, result, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
|
||||
@ -725,6 +725,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
|
||||
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
|
||||
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
|
||||
.err_return_trace => try self.airErrReturnTrace(inst),
|
||||
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
|
||||
|
||||
.wrap_optional => try self.airWrapOptional(inst),
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
@ -1843,6 +1845,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst))
|
||||
.dead
|
||||
else
|
||||
return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
return self.finishAir(inst, result, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
/// T to E!T
|
||||
fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
|
||||
@ -654,6 +654,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
|
||||
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
|
||||
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
|
||||
.err_return_trace => try self.airErrReturnTrace(inst),
|
||||
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
|
||||
|
||||
.wrap_optional => try self.airWrapOptional(inst),
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
@ -1267,6 +1269,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst))
|
||||
.dead
|
||||
else
|
||||
return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
return self.finishAir(inst, result, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
|
||||
@ -655,6 +655,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
|
||||
.unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
|
||||
.errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
|
||||
.err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"),
|
||||
.set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"),
|
||||
|
||||
.wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
|
||||
.wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
|
||||
|
||||
@ -1612,6 +1612,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
.atomic_store_seq_cst,
|
||||
.atomic_rmw,
|
||||
.tag_name,
|
||||
.err_return_trace,
|
||||
.set_err_return_trace,
|
||||
=> |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -749,6 +749,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
|
||||
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
|
||||
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
|
||||
.err_return_trace => try self.airErrReturnTrace(inst),
|
||||
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
|
||||
|
||||
.wrap_optional => try self.airWrapOptional(inst),
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
@ -1855,6 +1857,20 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst))
|
||||
.dead
|
||||
else
|
||||
return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
return self.finishAir(inst, result, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
|
||||
}
|
||||
|
||||
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
if (self.liveness.isUnused(inst)) {
|
||||
|
||||
@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
|
||||
.wrap_errunion_err => try airWrapErrUnionErr(f, inst),
|
||||
.errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
|
||||
.err_return_trace => try airErrReturnTrace(f, inst),
|
||||
.set_err_return_trace => try airSetErrReturnTrace(f, inst),
|
||||
|
||||
.wasm_memory_size => try airWasmMemorySize(f, inst),
|
||||
.wasm_memory_grow => try airWasmMemoryGrow(f, inst),
|
||||
@ -3447,6 +3449,16 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||
return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
_ = inst;
|
||||
return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
|
||||
}
|
||||
|
||||
fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
if (f.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
@ -636,10 +636,18 @@ pub const Object = struct {
|
||||
const ret_ptr = if (sret) llvm_func.getParam(0) else null;
|
||||
const gpa = dg.gpa;
|
||||
|
||||
const err_return_tracing = fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing;
|
||||
|
||||
const err_ret_trace = if (err_return_tracing)
|
||||
llvm_func.getParam(@boolToInt(ret_ptr != null))
|
||||
else
|
||||
null;
|
||||
|
||||
var args = std.ArrayList(*const llvm.Value).init(gpa);
|
||||
defer args.deinit();
|
||||
|
||||
const param_offset: c_uint = @boolToInt(ret_ptr != null);
|
||||
const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
@ -711,6 +719,7 @@ pub const Object = struct {
|
||||
.base_line = dg.decl.src_line,
|
||||
.prev_dbg_line = 0,
|
||||
.prev_dbg_column = 0,
|
||||
.err_ret_trace = err_ret_trace,
|
||||
};
|
||||
defer fg.deinit();
|
||||
|
||||
@ -1755,6 +1764,17 @@ pub const Object = struct {
|
||||
try param_di_types.append(try o.lowerDebugType(Type.void, .full));
|
||||
}
|
||||
|
||||
if (fn_info.return_type.isError() and
|
||||
o.module.comp.bin_file.options.error_return_tracing)
|
||||
{
|
||||
var ptr_ty_payload: Type.Payload.ElemType = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
.data = o.getStackTraceType(),
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
|
||||
}
|
||||
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
@ -1824,6 +1844,27 @@ pub const Object = struct {
|
||||
"", // unique id
|
||||
);
|
||||
}
|
||||
|
||||
fn getStackTraceType(o: *Object) Type {
|
||||
const mod = o.module;
|
||||
|
||||
const std_pkg = mod.main_pkg.table.get("std").?;
|
||||
const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
|
||||
|
||||
const builtin_str: []const u8 = "builtin";
|
||||
const std_namespace = mod.declPtr(std_file.root_decl.unwrap().?).src_namespace;
|
||||
const builtin_decl = std_namespace.decls
|
||||
.getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?;
|
||||
|
||||
const stack_trace_str: []const u8 = "StackTrace";
|
||||
// buffer is only used for int_type, `builtin` is a struct.
|
||||
const builtin_ty = mod.declPtr(builtin_decl).val.toType(undefined);
|
||||
const builtin_namespace = builtin_ty.getNamespace().?;
|
||||
const stack_trace_decl = builtin_namespace.decls
|
||||
.getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?;
|
||||
|
||||
return mod.declPtr(stack_trace_decl).val.toType(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclGen = struct {
|
||||
@ -1976,8 +2017,15 @@ pub const DeclGen = struct {
|
||||
llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
|
||||
}
|
||||
|
||||
const err_return_tracing = fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing;
|
||||
|
||||
if (err_return_tracing) {
|
||||
dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull");
|
||||
}
|
||||
|
||||
// Set parameter attributes.
|
||||
var llvm_param_i: c_uint = @boolToInt(sret);
|
||||
var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
@ -2435,6 +2483,17 @@ pub const DeclGen = struct {
|
||||
try llvm_params.append(llvm_sret_ty.pointerType(0));
|
||||
}
|
||||
|
||||
if (fn_info.return_type.isError() and
|
||||
dg.module.comp.bin_file.options.error_return_tracing)
|
||||
{
|
||||
var ptr_ty_payload: Type.Payload.ElemType = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
.data = dg.object.getStackTraceType(),
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty));
|
||||
}
|
||||
|
||||
for (fn_info.param_types) |param_ty| {
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
|
||||
@ -3449,6 +3508,8 @@ pub const FuncGen = struct {
|
||||
|
||||
llvm_func: *const llvm.Value,
|
||||
|
||||
err_ret_trace: ?*const llvm.Value = null,
|
||||
|
||||
/// This data structure is used to implement breaking to blocks.
|
||||
blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
|
||||
parent_bb: *const llvm.BasicBlock,
|
||||
@ -3678,6 +3739,8 @@ pub const FuncGen = struct {
|
||||
.unwrap_errunion_err => try self.airErrUnionErr(inst, false),
|
||||
.unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
|
||||
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
|
||||
.err_return_trace => try self.airErrReturnTrace(inst),
|
||||
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
|
||||
|
||||
.wrap_optional => try self.airWrapOptional(inst),
|
||||
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
|
||||
@ -3732,6 +3795,12 @@ pub const FuncGen = struct {
|
||||
break :blk ret_ptr;
|
||||
};
|
||||
|
||||
if (fn_info.return_type.isError() and
|
||||
self.dg.module.comp.bin_file.options.error_return_tracing)
|
||||
{
|
||||
try llvm_args.append(self.err_ret_trace.?);
|
||||
}
|
||||
|
||||
for (args) |arg| {
|
||||
const param_ty = self.air.typeOf(arg);
|
||||
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
|
||||
@ -5149,6 +5218,17 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
|
||||
}
|
||||
|
||||
fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !?*const llvm.Value {
|
||||
return self.err_ret_trace.?;
|
||||
}
|
||||
|
||||
fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
self.err_ret_trace = operand;
|
||||
return null;
|
||||
}
|
||||
|
||||
fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
|
||||
@ -170,6 +170,7 @@ const Writer = struct {
|
||||
.round,
|
||||
.trunc_float,
|
||||
.cmp_lt_errors_len,
|
||||
.set_err_return_trace,
|
||||
=> try w.writeUnOp(s, inst),
|
||||
|
||||
.breakpoint,
|
||||
@ -182,6 +183,7 @@ const Writer = struct {
|
||||
.alloc,
|
||||
.ret_ptr,
|
||||
.arg,
|
||||
.err_return_trace,
|
||||
=> try w.writeTy(s, inst),
|
||||
|
||||
.not,
|
||||
|
||||
@ -4093,6 +4093,13 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isError(ty: Type) bool {
|
||||
return switch (ty.zigTypeTag()) {
|
||||
.ErrorUnion, .ErrorSet => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns whether ty, which must be an error set, includes an error `name`.
|
||||
/// Might return a false negative if `ty` is an inferred error set and not fully
|
||||
/// resolved yet.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user