remove formatted panics

implements #17969
This commit is contained in:
Andrew Kelley 2024-09-25 11:11:48 -07:00
parent 04e694ad11
commit 4f8d244e7e
17 changed files with 631 additions and 741 deletions

View file

@ -77,11 +77,9 @@ pub const want_sparc_abi = builtin.cpu.arch.isSPARC();
// Avoid dragging in the runtime safety mechanisms into this .o file,
// unless we're trying to test compiler-rt.
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = error_return_trace;
pub fn panic(cause: std.builtin.PanicCause, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
if (builtin.is_test) {
@branchHint(.cold);
std.debug.panic("{s}", .{msg});
std.debug.defaultPanic(cause, error_return_trace, ret_addr orelse @returnAddress());
} else {
unreachable;
}

View file

@ -763,181 +763,66 @@ pub const TestFn = struct {
/// This function type is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const PanicFn = fn ([]const u8, ?*StackTrace, ?usize) noreturn;
pub const PanicFn = fn (PanicCause, ?*StackTrace, ?usize) noreturn;
/// This function is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
/// The entry point for auto-generated calls by the compiler.
pub const panic: PanicFn = if (@hasDecl(root, "panic"))
root.panic
else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))
root.os.panic
else
default_panic;
std.debug.defaultPanic;
/// This function is used by the Zig language code generation and
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
@branchHint(.cold);
pub const PanicCause = union(enum) {
reached_unreachable,
unwrap_null,
cast_to_null,
incorrect_alignment,
invalid_error_code,
cast_truncated_data,
negative_to_unsigned,
integer_overflow,
shl_overflow,
shr_overflow,
divide_by_zero,
exact_division_remainder,
inactive_union_field: InactiveUnionField,
integer_part_out_of_bounds,
corrupt_switch,
shift_rhs_too_big,
invalid_enum_value,
sentinel_mismatch_usize: SentinelMismatchUsize,
sentinel_mismatch_other,
unwrap_error: anyerror,
index_out_of_bounds: IndexOutOfBounds,
start_index_greater_than_end: StartIndexGreaterThanEnd,
for_len_mismatch,
memcpy_len_mismatch,
memcpy_alias,
noreturn_returned,
explicit_call: []const u8,
// For backends that cannot handle the language features depended on by the
// default panic handler, we have a simpler panic handler:
if (builtin.zig_backend == .stage2_wasm or
builtin.zig_backend == .stage2_arm or
builtin.zig_backend == .stage2_aarch64 or
builtin.zig_backend == .stage2_x86 or
(builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
builtin.zig_backend == .stage2_sparc64 or
builtin.zig_backend == .stage2_spirv64)
{
while (true) {
@breakpoint();
}
}
pub const IndexOutOfBounds = struct {
index: usize,
len: usize,
};
if (builtin.zig_backend == .stage2_riscv64) {
std.debug.print("panic: {s}\n", .{msg});
@breakpoint();
std.posix.exit(127);
}
pub const StartIndexGreaterThanEnd = struct {
start: usize,
end: usize,
};
switch (builtin.os.tag) {
.freestanding => {
while (true) {
@breakpoint();
}
},
.wasi => {
std.debug.print("{s}", .{msg});
std.posix.abort();
},
.uefi => {
const uefi = std.os.uefi;
pub const SentinelMismatchUsize = struct {
expected: usize,
found: usize,
};
const Formatter = struct {
pub fn fmt(exit_msg: []const u8, out: []u16) ![:0]u16 {
var u8_buf: [256]u8 = undefined;
const slice = try std.fmt.bufPrint(&u8_buf, "err: {s}\r\n", .{exit_msg});
// We pass len - 1 because we need to add a null terminator after
const len = try std.unicode.utf8ToUtf16Le(out[0 .. out.len - 1], slice);
out[len] = 0;
return out[0..len :0];
}
};
const ExitData = struct {
pub fn create_exit_data(exit_msg: [:0]u16, exit_size: *usize) ![*:0]u16 {
// Need boot services for pool allocation
if (uefi.system_table.boot_services == null) {
return error.BootServicesUnavailable;
}
// ExitData buffer must be allocated using boot_services.allocatePool (spec: page 220)
const exit_data: []u16 = try uefi.raw_pool_allocator.alloc(u16, exit_msg.len + 1);
@memcpy(exit_data[0 .. exit_msg.len + 1], exit_msg[0 .. exit_msg.len + 1]);
exit_size.* = exit_msg.len + 1;
return @as([*:0]u16, @ptrCast(exit_data.ptr));
}
};
var buf: [256]u16 = undefined;
const utf16 = Formatter.fmt(msg, &buf) catch null;
var exit_size: usize = 0;
const exit_data = if (utf16) |u|
ExitData.create_exit_data(u, &exit_size) catch null
else
null;
if (utf16) |str| {
// Output to both std_err and con_out, as std_err is easier
// to read in stuff like QEMU at times, but, unlike con_out,
// isn't visible on actual hardware if directly booted into
inline for ([_]?*uefi.protocol.SimpleTextOutput{ uefi.system_table.std_err, uefi.system_table.con_out }) |o| {
if (o) |out| {
_ = out.setAttribute(uefi.protocol.SimpleTextOutput.red);
_ = out.outputString(str);
_ = out.setAttribute(uefi.protocol.SimpleTextOutput.white);
}
}
}
if (uefi.system_table.boot_services) |bs| {
_ = bs.exit(uefi.handle, .Aborted, exit_size, exit_data);
}
// Didn't have boot_services, just fallback to whatever.
std.posix.abort();
},
.cuda, .amdhsa => std.posix.abort(),
.plan9 => {
var status: [std.os.plan9.ERRMAX]u8 = undefined;
const len = @min(msg.len, status.len - 1);
@memcpy(status[0..len], msg[0..len]);
status[len] = 0;
std.os.plan9.exits(status[0..len :0]);
},
else => {
const first_trace_addr = ret_addr orelse @returnAddress();
std.debug.panicImpl(error_return_trace, first_trace_addr, msg);
},
}
}
pub fn panicSentinelMismatch(expected: anytype, actual: @TypeOf(expected)) noreturn {
@branchHint(.cold);
std.debug.panicExtra(null, @returnAddress(), "sentinel mismatch: expected {any}, found {any}", .{ expected, actual });
}
pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
@branchHint(.cold);
std.debug.panicExtra(st, @returnAddress(), "attempt to unwrap error: {s}", .{@errorName(err)});
}
pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
@branchHint(.cold);
std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
}
pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
@branchHint(.cold);
std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
}
pub fn panicInactiveUnionField(active: anytype, wanted: @TypeOf(active)) noreturn {
@branchHint(.cold);
std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{ @tagName(wanted), @tagName(active) });
}
pub const panic_messages = struct {
pub const unreach = "reached unreachable code";
pub const unwrap_null = "attempt to use null value";
pub const cast_to_null = "cast causes pointer to be null";
pub const incorrect_alignment = "incorrect alignment";
pub const invalid_error_code = "invalid error code";
pub const cast_truncated_data = "integer cast truncated bits";
pub const negative_to_unsigned = "attempt to cast negative value to unsigned integer";
pub const integer_overflow = "integer overflow";
pub const shl_overflow = "left shift overflowed bits";
pub const shr_overflow = "right shift overflowed bits";
pub const divide_by_zero = "division by zero";
pub const exact_division_remainder = "exact division produced remainder";
pub const inactive_union_field = "access of inactive union field";
pub const integer_part_out_of_bounds = "integer part of floating point value out of bounds";
pub const corrupt_switch = "switch on corrupt value";
pub const shift_rhs_too_big = "shift amount is greater than the type size";
pub const invalid_enum_value = "invalid enum value";
pub const sentinel_mismatch = "sentinel mismatch";
pub const unwrap_error = "attempt to unwrap error";
pub const index_out_of_bounds = "index out of bounds";
pub const start_index_greater_than_end = "start index is larger than end index";
pub const for_len_mismatch = "for loop over objects with non-equal lengths";
pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
pub const memcpy_alias = "@memcpy arguments alias";
pub const noreturn_returned = "'noreturn' function returned";
pub const InactiveUnionField = struct {
active: []const u8,
accessed: []const u8,
};
};
pub noinline fn returnError(st: *StackTrace) void {

View file

@ -408,14 +408,15 @@ pub fn assertReadable(slice: []const volatile u8) void {
for (slice) |*byte| _ = byte.*;
}
/// Equivalent to `@panic` but with a formatted message.
pub fn panic(comptime format: []const u8, args: anytype) noreturn {
@branchHint(.cold);
panicExtra(@errorReturnTrace(), @returnAddress(), format, args);
}
/// `panicExtra` is useful when you want to print out an `@errorReturnTrace`
/// and also print out some values.
/// Equivalent to `@panic` but with a formatted message, and with an explicitly
/// provided `@errorReturnTrace` and return address.
pub fn panicExtra(
trace: ?*std.builtin.StackTrace,
ret_addr: ?usize,
@ -447,11 +448,104 @@ var panicking = std.atomic.Value(u8).init(0);
/// This is used to catch and handle panics triggered by the panic handler.
threadlocal var panic_stage: usize = 0;
// `panicImpl` could be useful in implementing a custom panic handler which
// calls the default handler (on supported platforms)
pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize, msg: []const u8) noreturn {
// Dumps a stack trace to standard error, then aborts.
//
// This function avoids a dependency on formatted printing.
pub fn defaultPanic(
cause: std.builtin.PanicCause,
trace: ?*const std.builtin.StackTrace,
first_trace_addr: ?usize,
) noreturn {
@branchHint(.cold);
// For backends that cannot handle the language features depended on by the
// default panic handler, we have a simpler panic handler:
if (builtin.zig_backend == .stage2_wasm or
builtin.zig_backend == .stage2_arm or
builtin.zig_backend == .stage2_aarch64 or
builtin.zig_backend == .stage2_x86 or
(builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
builtin.zig_backend == .stage2_sparc64 or
builtin.zig_backend == .stage2_spirv64)
{
@trap();
}
if (builtin.zig_backend == .stage2_riscv64) {
var buffer: [1000]u8 = undefined;
var i: usize = 0;
i += fmtPanicCause(buffer[i..], cause);
buffer[i] = '\n';
i += 1;
const msg = buffer[0..i];
lockStdErr();
io.getStdErr().writeAll(msg) catch {};
@trap();
}
switch (builtin.os.tag) {
.freestanding => {
@trap();
},
.wasi => {
// TODO: before merging my branch, unify this logic with the main panic logic
var buffer: [1000]u8 = undefined;
var i: usize = 0;
i += fmtPanicCause(buffer[i..], cause);
buffer[i] = '\n';
i += 1;
const msg = buffer[0..i];
lockStdErr();
io.getStdErr().writeAll(msg) catch {};
@trap();
},
.uefi => {
const uefi = std.os.uefi;
var buffer: [1000]u8 = undefined;
var i: usize = 0;
i += fmtBuf(buffer[i..], "panic: ");
i += fmtPanicCause(buffer[i..], cause);
i += fmtBuf(buffer[i..], "\r\n\x00");
var utf16_buffer: [1000]u16 = undefined;
const len = std.unicode.utf8ToUtf16Le(&utf16_buffer, buffer[0..i]) catch 0;
const exit_msg = utf16_buffer[0 .. len - 1 :0];
// Output to both std_err and con_out, as std_err is easier
// to read in stuff like QEMU at times, but, unlike con_out,
// isn't visible on actual hardware if directly booted into
inline for ([_]?*uefi.protocol.SimpleTextOutput{ uefi.system_table.std_err, uefi.system_table.con_out }) |o| {
if (o) |out| {
_ = out.setAttribute(uefi.protocol.SimpleTextOutput.red);
_ = out.outputString(exit_msg);
_ = out.setAttribute(uefi.protocol.SimpleTextOutput.white);
}
}
if (uefi.system_table.boot_services) |bs| {
// ExitData buffer must be allocated using boot_services.allocatePool (spec: page 220)
const exit_data: []u16 = uefi.raw_pool_allocator.alloc(u16, exit_msg.len + 1) catch @trap();
@memcpy(exit_data, exit_msg[0..exit_data.len]); // Includes null terminator.
_ = bs.exit(uefi.handle, .Aborted, exit_msg.len + 1, exit_data);
}
@trap();
},
.cuda, .amdhsa => std.posix.abort(),
.plan9 => {
var buffer: [1000]u8 = undefined;
comptime assert(buffer.len > std.os.plan9.ERRMAX);
var i: usize = 0;
i += fmtPanicCause(buffer[i..], cause);
buffer[i] = '\n';
i += 1;
const len = @min(i, std.os.plan9.ERRMAX - 1);
buffer[len] = 0;
std.os.plan9.exits(buffer[0..len :0]);
},
else => {},
}
if (enable_segfault_handler) {
// If a segfault happens while panicking, we want it to actually segfault, not trigger
// the handler.
@ -465,23 +559,29 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
_ = panicking.fetchAdd(1, .seq_cst);
// Make sure to release the mutex when done
{
// This code avoids a dependency on formatted printing, the writer interface,
// and limits to only 1 syscall made to print the panic message to stderr.
var buffer: [0x1000]u8 = undefined;
var i: usize = 0;
if (builtin.single_threaded) {
i += fmtBuf(buffer[i..], "panic: ");
} else {
i += fmtBuf(buffer[i..], "thread ");
i += fmtInt10(buffer[i..], std.Thread.getCurrentId());
i += fmtBuf(buffer[i..], " panic: ");
}
i += fmtPanicCause(&buffer, cause);
buffer[i] = '\n';
i += 1;
const msg = buffer[0..i];
lockStdErr();
defer unlockStdErr();
const stderr = io.getStdErr().writer();
if (builtin.single_threaded) {
stderr.print("panic: ", .{}) catch posix.abort();
} else {
const current_thread_id = std.Thread.getCurrentId();
stderr.print("thread {} panic: ", .{current_thread_id}) catch posix.abort();
}
stderr.print("{s}\n", .{msg}) catch posix.abort();
if (trace) |t| {
dumpStackTrace(t.*);
}
dumpCurrentStackTrace(first_trace_addr);
io.getStdErr().writeAll(msg) catch posix.abort();
if (trace) |t| dumpStackTrace(t.*);
dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
}
waitForOtherThreadToFinishPanicking();
@ -489,20 +589,99 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
1 => {
panic_stage = 2;
// A panic happened while trying to print a previous panic message,
// we're still holding the mutex but that's fine as we're going to
// call abort()
const stderr = io.getStdErr().writer();
stderr.print("Panicked during a panic. Aborting.\n", .{}) catch posix.abort();
},
else => {
// Panicked while printing "Panicked during a panic."
// A panic happened while trying to print a previous panic message.
// We're still holding the mutex but that's fine as we're going to
// call abort().
io.getStdErr().writeAll("aborting due to recursive panic\n") catch {};
},
else => {}, // Panicked while printing the recursive panic message.
};
posix.abort();
}
pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
var i: usize = 0;
switch (cause) {
.reached_unreachable => i += fmtBuf(buffer[i..], "reached unreachable code"),
.unwrap_null => i += fmtBuf(buffer[i..], "attempt to use null value"),
.cast_to_null => i += fmtBuf(buffer[i..], "cast causes pointer to be null"),
.incorrect_alignment => i += fmtBuf(buffer[i..], "incorrect alignment"),
.invalid_error_code => i += fmtBuf(buffer[i..], "invalid error code"),
.cast_truncated_data => i += fmtBuf(buffer[i..], "integer cast truncated bits"),
.negative_to_unsigned => i += fmtBuf(buffer[i..], "attempt to cast negative value to unsigned integer"),
.integer_overflow => i += fmtBuf(buffer[i..], "integer overflow"),
.shl_overflow => i += fmtBuf(buffer[i..], "left shift overflowed bits"),
.shr_overflow => i += fmtBuf(buffer[i..], "right shift overflowed bits"),
.divide_by_zero => i += fmtBuf(buffer[i..], "division by zero"),
.exact_division_remainder => i += fmtBuf(buffer[i..], "exact division produced remainder"),
.inactive_union_field => |info| {
i += fmtBuf(buffer[i..], "access of union field '");
i += fmtBuf(buffer[i..], info.accessed);
i += fmtBuf(buffer[i..], "' while field '");
i += fmtBuf(buffer[i..], info.active);
i += fmtBuf(buffer[i..], "' is active");
},
.integer_part_out_of_bounds => i += fmtBuf(buffer[i..], "integer part of floating point value out of bounds"),
.corrupt_switch => i += fmtBuf(buffer[i..], "switch on corrupt value"),
.shift_rhs_too_big => i += fmtBuf(buffer[i..], "shift amount is greater than the type size"),
.invalid_enum_value => i += fmtBuf(buffer[i..], "invalid enum value"),
.sentinel_mismatch_usize => |mm| {
i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
i += fmtInt10(buffer[i..], mm.expected);
i += fmtBuf(buffer[i..], ", found ");
i += fmtInt10(buffer[i..], mm.found);
},
.sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
.unwrap_error => |err| {
i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
i += fmtBuf(buffer[i..], @errorName(err));
},
.index_out_of_bounds => |oob| {
i += fmtBuf(buffer[i..], "index ");
i += fmtInt10(buffer[i..], oob.index);
i += fmtBuf(buffer[i..], " exceeds length ");
i += fmtInt10(buffer[i..], oob.len);
},
.start_index_greater_than_end => |oob| {
i += fmtBuf(buffer[i..], "start index ");
i += fmtInt10(buffer[i..], oob.start);
i += fmtBuf(buffer[i..], " exceeds end index ");
i += fmtInt10(buffer[i..], oob.end);
},
.for_len_mismatch => i += fmtBuf(buffer[i..], "for loop over objects with non-equal lengths"),
.memcpy_len_mismatch => i += fmtBuf(buffer[i..], "@memcpy arguments have non-equal lengths"),
.memcpy_alias => i += fmtBuf(buffer[i..], "@memcpy arguments alias"),
.noreturn_returned => i += fmtBuf(buffer[i..], "'noreturn' function returned"),
.explicit_call => |msg| i += fmtBuf(buffer[i..], msg),
}
return i;
}
fn fmtBuf(out_buf: []u8, s: []const u8) usize {
@memcpy(out_buf[0..s.len], s);
return s.len;
}
fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
var tmp_buf: [50]u8 = undefined;
var i: usize = tmp_buf.len;
var a: usize = integer_value;
while (true) {
i -= 1;
tmp_buf[i] = '0' + (a % 10);
a /= 10;
if (a == 0) break;
}
const result = tmp_buf[i..];
@memcpy(out_buf[0..result.len], result);
return result.len;
}
/// Must be called only after adding 1 to `panicking`. There are three callsites.
fn waitForOtherThreadToFinishPanicking() void {
if (panicking.fetchSub(1, .seq_cst) != 1) {
@ -1157,7 +1336,7 @@ pub const default_enable_segfault_handler = runtime_safety and have_segfault_han
pub fn maybeEnableSegfaultHandler() void {
if (enable_segfault_handler) {
std.debug.attachSegfaultHandler();
attachSegfaultHandler();
}
}
@ -1289,46 +1468,29 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WIN
}
}
fn handleSegfaultWindowsExtra(
info: *windows.EXCEPTION_POINTERS,
msg: u8,
label: ?[]const u8,
) noreturn {
const exception_address = @intFromPtr(info.ExceptionRecord.ExceptionAddress);
if (windows.CONTEXT != void) {
nosuspend switch (panic_stage) {
0 => {
panic_stage = 1;
_ = panicking.fetchAdd(1, .seq_cst);
fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) noreturn {
comptime assert(windows.CONTEXT != void);
nosuspend switch (panic_stage) {
0 => {
panic_stage = 1;
_ = panicking.fetchAdd(1, .seq_cst);
{
lockStdErr();
defer unlockStdErr();
{
lockStdErr();
defer unlockStdErr();
dumpSegfaultInfoWindows(info, msg, label);
}
waitForOtherThreadToFinishPanicking();
},
else => {
// panic mutex already locked
dumpSegfaultInfoWindows(info, msg, label);
},
};
posix.abort();
} else {
switch (msg) {
0 => panicImpl(null, exception_address, "{s}", label.?),
1 => {
const format_item = "Segmentation fault at address 0x{x}";
var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large
const to_print = std.fmt.bufPrint(buf[0..buf.len], format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable;
panicImpl(null, exception_address, to_print);
},
2 => panicImpl(null, exception_address, "Illegal Instruction"),
else => unreachable,
}
}
}
waitForOtherThreadToFinishPanicking();
},
1 => {
panic_stage = 2;
io.getStdErr().writeAll("aborting due to recursive panic\n") catch {};
},
else => {},
};
posix.abort();
}
fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[]const u8) void {
@ -1347,7 +1509,7 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {
const sp = asm (""
: [argc] "={rsp}" (-> usize),
);
std.debug.print("{s} sp = 0x{x}\n", .{ prefix, sp });
print("{s} sp = 0x{x}\n", .{ prefix, sp });
}
test "manage resources correctly" {

View file

@ -1197,7 +1197,7 @@ pub fn formatInt(
if (base == 10) {
while (a >= 100) : (a = @divTrunc(a, 100)) {
index -= 2;
buf[index..][0..2].* = digits2(@as(usize, @intCast(a % 100)));
buf[index..][0..2].* = digits2(@intCast(a % 100));
}
if (a < 10) {
@ -1205,13 +1205,13 @@ pub fn formatInt(
buf[index] = '0' + @as(u8, @intCast(a));
} else {
index -= 2;
buf[index..][0..2].* = digits2(@as(usize, @intCast(a)));
buf[index..][0..2].* = digits2(@intCast(a));
}
} else {
while (true) {
const digit = a % base;
index -= 1;
buf[index] = digitToChar(@as(u8, @intCast(digit)), case);
buf[index] = digitToChar(@intCast(digit), case);
a /= base;
if (a == 0) break;
}
@ -1242,11 +1242,7 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, case: Case, options
// Converts values in the range [0, 100) to a string.
pub fn digits2(value: usize) [2]u8 {
return ("0001020304050607080910111213141516171819" ++
"2021222324252627282930313233343536373839" ++
"4041424344454647484950515253545556575859" ++
"6061626364656667686970717273747576777879" ++
"8081828384858687888990919293949596979899")[value * 2 ..][0..2].*;
return "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"[value * 2 ..][0..2].*;
}
const FormatDurationData = struct {

View file

@ -195,7 +195,6 @@ job_queued_compiler_rt_obj: bool = false,
job_queued_fuzzer_lib: bool = false,
job_queued_update_builtin_zig: bool,
alloc_failure_occurred: bool = false,
formatted_panics: bool = false,
last_update_was_cache_hit: bool = false,
c_source_files: []const CSourceFile,
@ -1088,7 +1087,6 @@ pub const CreateOptions = struct {
/// executable this field is ignored.
want_compiler_rt: ?bool = null,
want_lto: ?bool = null,
formatted_panics: ?bool = null,
function_sections: bool = false,
data_sections: bool = false,
no_builtin: bool = false,
@ -1357,9 +1355,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
}
}
// TODO: https://github.com/ziglang/zig/issues/17969
const formatted_panics = options.formatted_panics orelse (options.root_mod.optimize_mode == .Debug);
const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1);
// We put everything into the cache hash that *cannot be modified
@ -1520,7 +1515,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.verbose_link = options.verbose_link,
.disable_c_depfile = options.disable_c_depfile,
.reference_trace = options.reference_trace,
.formatted_panics = formatted_panics,
.time_report = options.time_report,
.stack_report = options.stack_report,
.test_filters = options.test_filters,
@ -1638,7 +1632,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
hash.addListOfBytes(options.test_filters);
hash.addOptionalBytes(options.test_name_prefix);
hash.add(options.skip_linker_dependencies);
hash.add(formatted_panics);
hash.add(options.emit_h != null);
hash.add(error_limit);
@ -2564,7 +2557,6 @@ fn addNonIncrementalStuffToCacheManifest(
man.hash.addListOfBytes(comp.test_filters);
man.hash.addOptionalBytes(comp.test_name_prefix);
man.hash.add(comp.skip_linker_dependencies);
man.hash.add(comp.formatted_panics);
//man.hash.add(mod.emit_h != null);
man.hash.add(mod.error_limit);
} else {

View file

@ -7353,6 +7353,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.func_type => unreachable, // use getFuncType() instead
.@"extern" => unreachable, // use getExtern() instead
.func => unreachable, // use getFuncInstance() or getFuncDecl() instead
.un => unreachable, // use getUnion instead
.variable => |variable| {
const has_init = variable.init != .none;
@ -7968,15 +7969,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)});
},
.un => |un| {
assert(un.ty != .none);
assert(un.val != .none);
items.appendAssumeCapacity(.{
.tag = .union_value,
.data = try addExtra(extra, un),
});
},
.memoized_call => |memoized_call| {
for (memoized_call.arg_values) |arg| assert(arg != .none);
try extra.ensureUnusedCapacity(@typeInfo(MemoizedCall).@"struct".fields.len +
@ -7996,6 +7988,30 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
return gop.put();
}
pub fn getUnion(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
un: Key.Union,
) Allocator.Error!Index {
var gop = try ip.getOrPutKey(gpa, tid, .{ .un = un });
defer gop.deinit();
if (gop == .existing) return gop.existing;
const local = ip.getLocal(tid);
const items = local.getMutableItems(gpa);
const extra = local.getMutableExtra(gpa);
try items.ensureUnusedCapacity(1);
assert(un.ty != .none);
assert(un.val != .none);
items.appendAssumeCapacity(.{
.tag = .union_value,
.data = try addExtra(extra, un),
});
return gop.put();
}
pub const UnionTypeInit = struct {
flags: packed struct {
runtime_tag: LoadedUnionType.RuntimeTag,

File diff suppressed because it is too large Load diff

View file

@ -613,11 +613,11 @@ const PackValueBits = struct {
pack.bit_offset = prev_bit_offset;
break :backing;
}
return Value.fromInterned(try pt.intern(.{ .un = .{
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = .none,
.val = backing_val.toIntern(),
} }));
}));
}
const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
@ -658,21 +658,21 @@ const PackValueBits = struct {
continue;
}
const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
return Value.fromInterned(try pt.intern(.{ .un = .{
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = tag_val.toIntern(),
.val = field_val.toIntern(),
} }));
}));
}
// No field could represent the value. Just do whatever happens when we try to read
// the backing type - either `undefined` or `error.ReinterpretDeclRef`.
const backing_val = try pack.get(backing_ty);
return Value.fromInterned(try pt.intern(.{ .un = .{
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = .none,
.val = backing_val.toIntern(),
} }));
}));
},
else => return pack.primitive(ty),
}

View file

@ -2677,11 +2677,11 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
const only_field_ty = union_obj.field_types.get(ip)[0];
const val_val = (try Type.fromInterned(only_field_ty).onePossibleValue(pt)) orelse
return null;
const only = try pt.intern(.{ .un = .{
const only = try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = tag_val.toIntern(),
.val = val_val.toIntern(),
} });
});
return Value.fromInterned(only);
},
.opaque_type => return null,

View file

@ -713,11 +713,11 @@ pub fn readFromMemory(
const union_size = ty.abiSize(zcu);
const array_ty = try zcu.arrayType(.{ .len = union_size, .child = .u8_type });
const val = (try readFromMemory(array_ty, zcu, buffer, arena)).toIntern();
return Value.fromInterned(try pt.intern(.{ .un = .{
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = .none,
.val = val,
} }));
}));
},
.@"packed" => {
const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
@ -860,11 +860,11 @@ pub fn readFromPackedMemory(
.@"packed" => {
const backing_ty = try ty.unionBackingType(pt);
const val = (try readFromPackedMemory(backing_ty, pt, buffer, bit_offset, arena)).toIntern();
return Value.fromInterned(try pt.intern(.{ .un = .{
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
.tag = .none,
.val = val,
} }));
}));
},
},
.pointer => {
@ -4481,11 +4481,11 @@ pub fn resolveLazy(
return if (resolved_tag == un.tag and resolved_val == un.val)
val
else
Value.fromInterned(try pt.intern(.{ .un = .{
Value.fromInterned(try pt.internUnion(.{
.ty = un.ty,
.tag = resolved_tag,
.val = resolved_val,
} }));
}));
},
else => return val,
}

View file

@ -210,45 +210,15 @@ all_type_references: std.ArrayListUnmanaged(TypeReference) = .empty,
/// Freelist of indices in `all_type_references`.
free_type_references: std.ArrayListUnmanaged(u32) = .empty,
panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,
/// The panic function body.
panic_func_index: InternPool.Index = .none,
null_stack_trace: InternPool.Index = .none,
panic_cause_type: InternPool.Index = .none,
generation: u32 = 0,
pub const PerThread = @import("Zcu/PerThread.zig");
pub const PanicId = enum {
unreach,
unwrap_null,
cast_to_null,
incorrect_alignment,
invalid_error_code,
cast_truncated_data,
negative_to_unsigned,
integer_overflow,
shl_overflow,
shr_overflow,
divide_by_zero,
exact_division_remainder,
inactive_union_field,
integer_part_out_of_bounds,
corrupt_switch,
shift_rhs_too_big,
invalid_enum_value,
sentinel_mismatch,
unwrap_error,
index_out_of_bounds,
start_index_greater_than_end,
for_len_mismatch,
memcpy_len_mismatch,
memcpy_alias,
noreturn_returned,
pub const len = @typeInfo(PanicId).@"enum".fields.len;
};
pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
pub const CImportError = struct {
@ -2926,14 +2896,6 @@ pub const Feature = enum {
/// When this feature is enabled, Sema will emit calls to `std.builtin.panic`
/// for things like safety checks and unreachables. Otherwise traps will be emitted.
panic_fn,
/// When this feature is enabled, Sema will emit calls to `std.builtin.panicUnwrapError`.
/// This error message requires more advanced formatting, hence it being seperate from `panic_fn`.
/// Otherwise traps will be emitted.
panic_unwrap_error,
/// When this feature is enabled, Sema will emit calls to the more complex panic functions
/// that use formatting to add detail to error messages. Similar to `panic_unwrap_error`.
/// Otherwise traps will be emitted.
safety_check_formatted,
/// When this feature is enabled, Sema will insert tracer functions for gathering a stack
/// trace for error returns.
error_return_trace,

View file

@ -2697,11 +2697,16 @@ pub fn reportRetryableFileError(
gop.value_ptr.* = err_msg;
}
///Shortcut for calling `intern_pool.get`.
/// Shortcut for calling `intern_pool.get`.
pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {
return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key);
}
/// Shortcut for calling `intern_pool.getUnion`.
pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!InternPool.Index {
return pt.zcu.intern_pool.getUnion(pt.zcu.gpa, pt.tid, un);
}
/// Essentially a shortcut for calling `intern_pool.getCoerced`.
/// However, this function also allows coercing `extern`s. The `InternPool` function can't do
/// this because it requires potentially pushing to the job queue.
@ -2949,11 +2954,12 @@ pub fn intValue_i64(pt: Zcu.PerThread, ty: Type, x: i64) Allocator.Error!Value {
}
pub fn unionValue(pt: Zcu.PerThread, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
return Value.fromInterned(try pt.intern(.{ .un = .{
const zcu = pt.zcu;
return Value.fromInterned(try zcu.intern_pool.getUnion(zcu.gpa, pt.tid, .{
.ty = union_ty.toIntern(),
.tag = tag.toIntern(),
.val = val.toIntern(),
} }));
}));
}
/// This function casts the float representation down to the representation of the type, potentially

View file

@ -17,7 +17,7 @@ const Decl = Zcu.Decl;
/// To use these crash report diagnostics, publish this panic in your main file
/// and add `pub const enable_segfault_handler = false;` to your `std_options`.
/// You will also need to call initialize() on startup, preferably as the very first operation in your program.
pub const panic = if (build_options.enable_debug_extensions) compilerPanic else std.builtin.default_panic;
pub const panic = if (build_options.enable_debug_extensions) compilerPanic else std.debug.defaultPanic;
/// Install signal handlers to identify crashes and report diagnostics.
pub fn initialize() void {
@ -152,12 +152,16 @@ fn writeFilePath(file: *Zcu.File, writer: anytype) !void {
try writer.writeAll(file.sub_file_path);
}
pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, maybe_ret_addr: ?usize) noreturn {
pub fn compilerPanic(
cause: std.builtin.PanicCause,
error_return_trace: ?*std.builtin.StackTrace,
maybe_ret_addr: ?usize,
) noreturn {
@branchHint(.cold);
PanicSwitch.preDispatch();
const ret_addr = maybe_ret_addr orelse @returnAddress();
const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } };
PanicSwitch.dispatch(error_return_trace, stack_ctx, msg);
PanicSwitch.dispatch(error_return_trace, stack_ctx, cause);
}
/// Attaches a global SIGSEGV handler
@ -354,17 +358,17 @@ const PanicSwitch = struct {
pub fn dispatch(
trace: ?*const std.builtin.StackTrace,
stack_ctx: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
var panic_state: *volatile PanicState = &panic_state_raw;
debug.assert(panic_state.awaiting_dispatch);
panic_state.awaiting_dispatch = false;
nosuspend switch (panic_state.recover_stage) {
.initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, msg }),
.report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, msg }),
.release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, msg }),
.release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, msg }),
.abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, msg }),
.initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, panic_cause }),
.report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, panic_cause }),
.release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, panic_cause }),
.release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, panic_cause }),
.abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, panic_cause }),
.silent_abort => goTo(abort, .{}),
};
}
@ -373,7 +377,7 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
// use a temporary so there's only one volatile store
const new_state = PanicState{
@ -398,6 +402,8 @@ const PanicSwitch = struct {
const current_thread_id = std.Thread.getCurrentId();
stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state});
}
var buffer: [1000]u8 = undefined;
const msg = buffer[0..std.debug.fmtPanicCause(&buffer, panic_cause)];
stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state});
state.recover_stage = .report_stack;
@ -413,9 +419,9 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
recover(state, trace, stack, msg);
recover(state, trace, stack, panic_cause);
state.recover_stage = .release_mutex;
const stderr = io.getStdErr().writer();
@ -438,9 +444,9 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
recover(state, trace, stack, msg);
recover(state, trace, stack, panic_cause);
goTo(releaseMutex, .{state});
}
@ -456,9 +462,9 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
recover(state, trace, stack, msg);
recover(state, trace, stack, panic_cause);
goTo(releaseRefCount, .{state});
}
@ -484,9 +490,9 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) noreturn {
recover(state, trace, stack, msg);
recover(state, trace, stack, panic_cause);
state.recover_stage = .silent_abort;
const stderr = io.getStdErr().writer();
@ -510,7 +516,7 @@ const PanicSwitch = struct {
state: *volatile PanicState,
trace: ?*const std.builtin.StackTrace,
stack: StackContext,
msg: []const u8,
panic_cause: std.builtin.PanicCause,
) void {
switch (state.recover_verbosity) {
.message_and_stack => {
@ -519,7 +525,7 @@ const PanicSwitch = struct {
const stderr = io.getStdErr().writer();
stderr.writeAll("\nPanicked during a panic: ") catch {};
stderr.writeAll(msg) catch {};
stderr.writeAll(panic_cause) catch {};
stderr.writeAll("\nInner panic stack:\n") catch {};
if (trace) |t| {
debug.dumpStackTrace(t.*);
@ -533,7 +539,7 @@ const PanicSwitch = struct {
const stderr = io.getStdErr().writer();
stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {};
stderr.writeAll(msg) catch {};
stderr.writeAll(panic_cause) catch {};
stderr.writeAll("\n") catch {};
// If we succeed, restore all the way to dumping the stack.

View file

@ -826,7 +826,6 @@ fn buildOutputType(
var version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 };
var have_version = false;
var compatibility_version: ?std.SemanticVersion = null;
var formatted_panics: ?bool = null;
var function_sections = false;
var data_sections = false;
var no_builtin = false;
@ -1537,9 +1536,11 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-gdwarf64")) {
create_module.opts.debug_format = .{ .dwarf = .@"64" };
} else if (mem.eql(u8, arg, "-fformatted-panics")) {
formatted_panics = true;
// Remove this after 0.15.0 is tagged.
warn("-fformatted-panics is deprecated and does nothing", .{});
} else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
formatted_panics = false;
// Remove this after 0.15.0 is tagged.
warn("-fno-formatted-panics is deprecated and does nothing", .{});
} else if (mem.eql(u8, arg, "-fsingle-threaded")) {
mod_opts.single_threaded = true;
} else if (mem.eql(u8, arg, "-fno-single-threaded")) {
@ -3405,7 +3406,6 @@ fn buildOutputType(
.force_undefined_symbols = force_undefined_symbols,
.stack_size = stack_size,
.image_base = image_base,
.formatted_panics = formatted_panics,
.function_sections = function_sections,
.data_sections = data_sections,
.no_builtin = no_builtin,

View file

@ -88,11 +88,11 @@ pub const MutableValue = union(enum) {
.ptr = (try s.ptr.intern(pt, arena)).toIntern(),
.len = (try s.len.intern(pt, arena)).toIntern(),
} }),
.un => |u| try pt.intern(.{ .un = .{
.un => |u| try pt.internUnion(.{
.ty = u.ty,
.tag = u.tag,
.val = (try u.payload.intern(pt, arena)).toIntern(),
} }),
}),
});
}

View file

@ -586,14 +586,6 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
=> true,
else => false,
},
.panic_unwrap_error => switch (backend) {
.stage2_c, .stage2_llvm => true,
else => false,
},
.safety_check_formatted => switch (backend) {
.stage2_c, .stage2_llvm => true,
else => false,
},
.error_return_trace => switch (backend) {
.stage2_llvm => true,
else => false,

View file

@ -1,7 +1,8 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = stack_trace;
_ = ret_addr;
if (std.mem.eql(u8, message, "reached unreachable code")) {
std.process.exit(0);
}