mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
reintroduce the std.builtin safety panic helpers
motivated by performance
This commit is contained in:
parent
76f0b6e7d8
commit
f2c8940aa6
@ -803,6 +803,7 @@ pub const PanicCause = union(enum) {
|
||||
memcpy_alias,
|
||||
noreturn_returned,
|
||||
explicit_call: []const u8,
|
||||
sentinel_mismatch_isize: SentinelMismatchIsize,
|
||||
|
||||
pub const IndexOutOfBounds = struct {
|
||||
index: usize,
|
||||
@ -819,22 +820,82 @@ pub const PanicCause = union(enum) {
|
||||
found: usize,
|
||||
};
|
||||
|
||||
pub const SentinelMismatchIsize = struct {
|
||||
expected: isize,
|
||||
found: isize,
|
||||
};
|
||||
|
||||
pub const InactiveUnionField = struct {
|
||||
active: []const u8,
|
||||
accessed: []const u8,
|
||||
};
|
||||
};
|
||||
|
||||
pub noinline fn returnError(st: *StackTrace) void {
|
||||
pub fn panicSentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
|
||||
@branchHint(.cold);
|
||||
@setRuntimeSafety(false);
|
||||
addErrRetTraceAddr(st, @returnAddress());
|
||||
switch (@typeInfo(@TypeOf(expected))) {
|
||||
.int => |int| switch (int.signedness) {
|
||||
.unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
|
||||
.expected = expected,
|
||||
.found = found,
|
||||
} }, null, @returnAddress()),
|
||||
.signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
|
||||
.expected = expected,
|
||||
.found = found,
|
||||
} }, null, @returnAddress()),
|
||||
},
|
||||
.@"enum" => |info| switch (@typeInfo(info.tag_type)) {
|
||||
.int => |int| switch (int.signedness) {
|
||||
.unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
|
||||
.expected = @intFromEnum(expected),
|
||||
.found = @intFromEnum(found),
|
||||
} }, null, @returnAddress()),
|
||||
.signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
|
||||
.expected = @intFromEnum(expected),
|
||||
.found = @intFromEnum(found),
|
||||
} }, null, @returnAddress()),
|
||||
},
|
||||
else => comptime unreachable,
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
panic(.sentinel_mismatch_other, null, @returnAddress());
|
||||
}
|
||||
|
||||
pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
|
||||
if (st.index < st.instruction_addresses.len)
|
||||
st.instruction_addresses[st.index] = addr;
|
||||
pub fn panicUnwrapError(ert: ?*StackTrace, err: anyerror) noreturn {
|
||||
@branchHint(.cold);
|
||||
panic(.{ .unwrap_error = err }, ert, @returnAddress());
|
||||
}
|
||||
|
||||
pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
|
||||
@branchHint(.cold);
|
||||
panic(.{ .index_out_of_bounds = .{
|
||||
.index = index,
|
||||
.len = len,
|
||||
} }, null, @returnAddress());
|
||||
}
|
||||
|
||||
pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
|
||||
@branchHint(.cold);
|
||||
panic(.{ .start_index_greater_than_end = .{
|
||||
.start = start,
|
||||
.end = end,
|
||||
} }, null, @returnAddress());
|
||||
}
|
||||
|
||||
pub fn panicInactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
|
||||
@branchHint(.cold);
|
||||
panic(.{ .inactive_union_field = .{
|
||||
.active = @tagName(active),
|
||||
.accessed = @tagName(accessed),
|
||||
} }, null, @returnAddress());
|
||||
}
|
||||
|
||||
pub noinline fn returnError(st: *StackTrace) void {
|
||||
@branchHint(.unlikely);
|
||||
@setRuntimeSafety(false);
|
||||
if (st.index < st.instruction_addresses.len)
|
||||
st.instruction_addresses[st.index] = @returnAddress();
|
||||
st.index += 1;
|
||||
}
|
||||
|
||||
|
||||
@ -453,7 +453,7 @@ threadlocal var panic_stage: usize = 0;
|
||||
// This function avoids a dependency on formatted printing.
|
||||
pub fn defaultPanic(
|
||||
cause: std.builtin.PanicCause,
|
||||
trace: ?*const std.builtin.StackTrace,
|
||||
error_return_trace: ?*const std.builtin.StackTrace,
|
||||
first_trace_addr: ?usize,
|
||||
) noreturn {
|
||||
@branchHint(.cold);
|
||||
@ -568,7 +568,7 @@ pub fn defaultPanic(
|
||||
defer unlockStdErr();
|
||||
|
||||
io.getStdErr().writeAll(msg) catch posix.abort();
|
||||
if (trace) |t| dumpStackTrace(t.*);
|
||||
if (error_return_trace) |t| dumpStackTrace(t.*);
|
||||
dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
|
||||
}
|
||||
|
||||
@ -621,6 +621,12 @@ pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
|
||||
i += fmtBuf(buffer[i..], ", found ");
|
||||
i += fmtInt10(buffer[i..], mm.found);
|
||||
},
|
||||
.sentinel_mismatch_isize => |mm| {
|
||||
i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
|
||||
i += fmtInt10s(buffer[i..], mm.expected);
|
||||
i += fmtBuf(buffer[i..], ", found ");
|
||||
i += fmtInt10s(buffer[i..], mm.found);
|
||||
},
|
||||
.sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
|
||||
.unwrap_error => |err| {
|
||||
i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
|
||||
@ -653,6 +659,15 @@ fn fmtBuf(out_buf: []u8, s: []const u8) usize {
|
||||
return s.len;
|
||||
}
|
||||
|
||||
fn fmtInt10s(out_buf: []u8, integer_value: isize) usize {
|
||||
if (integer_value < 0) {
|
||||
out_buf[0] = '-';
|
||||
return 1 + fmtInt10(out_buf[1..], @abs(integer_value));
|
||||
} else {
|
||||
return fmtInt10(out_buf, @abs(integer_value));
|
||||
}
|
||||
}
|
||||
|
||||
fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
|
||||
var tmp_buf: [50]u8 = undefined;
|
||||
var i: usize = tmp_buf.len;
|
||||
|
||||
151
src/Sema.zig
151
src/Sema.zig
@ -14249,7 +14249,7 @@ fn maybeErrorUnwrap(
|
||||
.as_node => try sema.zirAsNode(block, inst),
|
||||
.field_val => try sema.zirFieldVal(block, inst),
|
||||
.@"unreachable" => {
|
||||
try callPanic(sema, block, operand_src, .unwrap_error, operand, .@"safety check");
|
||||
try safetyPanicUnwrapError(sema, block, operand_src, operand);
|
||||
return true;
|
||||
},
|
||||
.panic => {
|
||||
@ -27827,11 +27827,24 @@ fn addSafetyCheckUnwrapError(
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
|
||||
try callPanic(sema, &fail_block, src, .unwrap_error, err, .@"safety check");
|
||||
try safetyPanicUnwrapError(sema, &fail_block, src, err);
|
||||
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
if (!zcu.backendSupportsFeature(.panic_fn)) {
|
||||
_ = try block.addNoOp(.trap);
|
||||
} else {
|
||||
const panic_fn = try pt.getBuiltin("panicUnwrapError");
|
||||
const err_return_trace = try sema.getErrorReturnTrace(block);
|
||||
const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
|
||||
try sema.callBuiltin(block, src, panic_fn, .auto, &args, .@"safety check");
|
||||
}
|
||||
}
|
||||
|
||||
fn addSafetyCheckIndexOob(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
@ -27841,68 +27854,8 @@ fn addSafetyCheckIndexOob(
|
||||
cmp_op: Air.Inst.Tag,
|
||||
) !void {
|
||||
assert(!parent_block.is_comptime);
|
||||
const gpa = sema.gpa;
|
||||
const ok = try parent_block.addBinOp(cmp_op, index, len);
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
.namespace = parent_block.namespace,
|
||||
.instructions = .{},
|
||||
.inlining = parent_block.inlining,
|
||||
.is_comptime = false,
|
||||
.src_base_inst = parent_block.src_base_inst,
|
||||
.type_name_ctx = parent_block.type_name_ctx,
|
||||
};
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
const oob_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "IndexOutOfBounds");
|
||||
comptime {
|
||||
const fields = @typeInfo(std.builtin.PanicCause.IndexOutOfBounds).@"struct".fields;
|
||||
assert(std.mem.eql(u8, fields[0].name, "index"));
|
||||
assert(std.mem.eql(u8, fields[1].name, "len"));
|
||||
assert(fields.len == 2);
|
||||
}
|
||||
const panic_cause_payload = try fail_block.addAggregateInit(oob_ty, &.{ index, len });
|
||||
try callPanic(sema, &fail_block, src, .index_out_of_bounds, panic_cause_payload, .@"safety check");
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
fn addSafetyCheckStartGreaterThanEnd(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
src: LazySrcLoc,
|
||||
start: Air.Inst.Ref,
|
||||
end: Air.Inst.Ref,
|
||||
) !void {
|
||||
assert(!parent_block.is_comptime);
|
||||
const gpa = sema.gpa;
|
||||
const ok = try parent_block.addBinOp(.cmp_lte, start, end);
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
.namespace = parent_block.namespace,
|
||||
.instructions = .{},
|
||||
.inlining = parent_block.inlining,
|
||||
.is_comptime = false,
|
||||
.src_base_inst = parent_block.src_base_inst,
|
||||
.type_name_ctx = parent_block.type_name_ctx,
|
||||
};
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
const oob_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "StartIndexGreaterThanEnd");
|
||||
comptime {
|
||||
const fields = @typeInfo(std.builtin.PanicCause.StartIndexGreaterThanEnd).@"struct".fields;
|
||||
assert(std.mem.eql(u8, fields[0].name, "start"));
|
||||
assert(std.mem.eql(u8, fields[1].name, "end"));
|
||||
assert(fields.len == 2);
|
||||
}
|
||||
const panic_cause_payload = try fail_block.addAggregateInit(oob_ty, &.{ start, end });
|
||||
try callPanic(sema, &fail_block, src, .start_index_greater_than_end, panic_cause_payload, .@"safety check");
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
return addSafetyCheckCall(sema, parent_block, src, ok, "panicOutOfBounds", &.{ index, len });
|
||||
}
|
||||
|
||||
fn addSafetyCheckInactiveUnionField(
|
||||
@ -27913,36 +27866,8 @@ fn addSafetyCheckInactiveUnionField(
|
||||
wanted_tag: Air.Inst.Ref,
|
||||
) !void {
|
||||
assert(!parent_block.is_comptime);
|
||||
const gpa = sema.gpa;
|
||||
const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
.namespace = parent_block.namespace,
|
||||
.instructions = .{},
|
||||
.inlining = parent_block.inlining,
|
||||
.is_comptime = false,
|
||||
.src_base_inst = parent_block.src_base_inst,
|
||||
.type_name_ctx = parent_block.type_name_ctx,
|
||||
};
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
const payload_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "InactiveUnionField");
|
||||
comptime {
|
||||
const fields = @typeInfo(std.builtin.PanicCause.InactiveUnionField).@"struct".fields;
|
||||
assert(std.mem.eql(u8, fields[0].name, "active"));
|
||||
assert(std.mem.eql(u8, fields[1].name, "accessed"));
|
||||
assert(fields.len == 2);
|
||||
}
|
||||
// TODO: before merging the branch, check how many safety checks end up being emitted
|
||||
// for union field accesses and avoid extraneous ones.
|
||||
const active_str = try analyzeTagName(sema, &fail_block, src, src, active_tag);
|
||||
const accessed_str = try analyzeTagName(sema, &fail_block, src, src, wanted_tag);
|
||||
const panic_cause_payload = try fail_block.addAggregateInit(payload_ty, &.{ active_str, accessed_str });
|
||||
try callPanic(sema, &fail_block, src, .inactive_union_field, panic_cause_payload, .@"safety check");
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
return addSafetyCheckCall(sema, parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
|
||||
}
|
||||
|
||||
fn addSafetyCheckSentinelMismatch(
|
||||
@ -27955,7 +27880,6 @@ fn addSafetyCheckSentinelMismatch(
|
||||
sentinel_index: Air.Inst.Ref,
|
||||
) !void {
|
||||
assert(!parent_block.is_comptime);
|
||||
const gpa = sema.gpa;
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const expected_sentinel_val = maybe_sentinel orelse return;
|
||||
@ -27984,6 +27908,24 @@ fn addSafetyCheckSentinelMismatch(
|
||||
break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
|
||||
};
|
||||
|
||||
return addSafetyCheckCall(sema, parent_block, src, ok, "panicSentinelMismatch", &.{
|
||||
expected_sentinel, actual_sentinel,
|
||||
});
|
||||
}
|
||||
|
||||
fn addSafetyCheckCall(
|
||||
sema: *Sema,
|
||||
parent_block: *Block,
|
||||
src: LazySrcLoc,
|
||||
ok: Air.Inst.Ref,
|
||||
func_name: []const u8,
|
||||
args: []const Air.Inst.Ref,
|
||||
) !void {
|
||||
assert(!parent_block.is_comptime);
|
||||
const gpa = sema.gpa;
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
|
||||
var fail_block: Block = .{
|
||||
.parent = parent_block,
|
||||
.sema = sema,
|
||||
@ -27997,23 +27939,13 @@ fn addSafetyCheckSentinelMismatch(
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
// A different PanicCause tag must be used depending on what payload type it can be fit into.
|
||||
// If it cannot fit into any, the "other" tag can be used, which does not try to carry the
|
||||
// sentinel value data.
|
||||
|
||||
if (sentinel_ty.isUnsignedInt(zcu) and sentinel_ty.intInfo(zcu).bits <= Type.usize.intInfo(zcu).bits) {
|
||||
const mm_ty = try getBuiltinInnerType(sema, &fail_block, src, "PanicCause", "SentinelMismatchUsize");
|
||||
comptime {
|
||||
const fields = @typeInfo(std.builtin.PanicCause.SentinelMismatchUsize).@"struct".fields;
|
||||
assert(std.mem.eql(u8, fields[0].name, "expected"));
|
||||
assert(std.mem.eql(u8, fields[1].name, "found"));
|
||||
assert(fields.len == 2);
|
||||
}
|
||||
const panic_cause_payload = try fail_block.addAggregateInit(mm_ty, &.{ expected_sentinel, actual_sentinel });
|
||||
try callPanic(sema, &fail_block, src, .sentinel_mismatch_usize, panic_cause_payload, .@"safety check");
|
||||
if (!zcu.backendSupportsFeature(.panic_fn)) {
|
||||
_ = try fail_block.addNoOp(.trap);
|
||||
} else {
|
||||
try callPanic(sema, &fail_block, src, .sentinel_mismatch_other, .void_value, .@"safety check");
|
||||
const panic_fn = try pt.getBuiltin(func_name);
|
||||
try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");
|
||||
}
|
||||
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
|
||||
@ -33512,7 +33444,8 @@ fn analyzeSlice(
|
||||
// requirement: start <= end
|
||||
assert(!block.is_comptime);
|
||||
try sema.requireRuntimeBlock(block, src, runtime_src.?);
|
||||
try sema.addSafetyCheckStartGreaterThanEnd(block, src, start, end);
|
||||
const ok = try block.addBinOp(.cmp_lte, start, end);
|
||||
try sema.addSafetyCheckCall(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end });
|
||||
}
|
||||
const new_len = if (by_length)
|
||||
try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user