mirror of
https://github.com/ziglang/zig.git
synced 2026-01-25 00:35:20 +00:00
Merge pull request #21520 from ziglang/no-formatted-panics
formalize the panic interface closes #17969 closes #20240
This commit is contained in:
commit
0cdec976e4
@ -75,13 +75,14 @@ pub const gnu_f16_abi = switch (builtin.cpu.arch) {
|
||||
|
||||
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;
|
||||
// Avoid dragging in the runtime safety mechanisms into this .o file, unless
|
||||
// we're trying to test compiler-rt.
|
||||
pub const Panic = if (builtin.is_test) std.debug.FormattedPanic else struct {};
|
||||
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
|
||||
if (builtin.is_test) {
|
||||
@branchHint(.cold);
|
||||
std.debug.panic("{s}", .{msg});
|
||||
std.debug.defaultPanic(msg, error_return_trace, ret_addr orelse @returnAddress());
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
@ -22,6 +22,83 @@ pub const WaitGroup = @import("Thread/WaitGroup.zig");
|
||||
|
||||
pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc;
|
||||
|
||||
/// Spurious wakeups are possible and no precision of timing is guaranteed.
|
||||
pub fn sleep(nanoseconds: u64) void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const big_ms_from_ns = nanoseconds / std.time.ns_per_ms;
|
||||
const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD);
|
||||
windows.kernel32.Sleep(ms);
|
||||
return;
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .wasi) {
|
||||
const w = std.os.wasi;
|
||||
const userdata: w.userdata_t = 0x0123_45678;
|
||||
const clock: w.subscription_clock_t = .{
|
||||
.id = .MONOTONIC,
|
||||
.timeout = nanoseconds,
|
||||
.precision = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
const in: w.subscription_t = .{
|
||||
.userdata = userdata,
|
||||
.u = .{
|
||||
.tag = .CLOCK,
|
||||
.u = .{ .clock = clock },
|
||||
},
|
||||
};
|
||||
|
||||
var event: w.event_t = undefined;
|
||||
var nevents: usize = undefined;
|
||||
_ = w.poll_oneoff(&in, &event, 1, &nevents);
|
||||
return;
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .uefi) {
|
||||
const boot_services = std.os.uefi.system_table.boot_services.?;
|
||||
const us_from_ns = nanoseconds / std.time.ns_per_us;
|
||||
const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);
|
||||
_ = boot_services.stall(us);
|
||||
return;
|
||||
}
|
||||
|
||||
const s = nanoseconds / std.time.ns_per_s;
|
||||
const ns = nanoseconds % std.time.ns_per_s;
|
||||
|
||||
// Newer kernel ports don't have old `nanosleep()` and `clock_nanosleep()` has been around
|
||||
// since Linux 2.6 and glibc 2.1 anyway.
|
||||
if (builtin.os.tag == .linux) {
|
||||
const linux = std.os.linux;
|
||||
|
||||
var req: linux.timespec = .{
|
||||
.sec = std.math.cast(linux.time_t, s) orelse std.math.maxInt(linux.time_t),
|
||||
.nsec = std.math.cast(linux.time_t, ns) orelse std.math.maxInt(linux.time_t),
|
||||
};
|
||||
var rem: linux.timespec = undefined;
|
||||
|
||||
while (true) {
|
||||
switch (linux.E.init(linux.clock_nanosleep(.MONOTONIC, .{ .ABSTIME = false }, &req, &rem))) {
|
||||
.SUCCESS => return,
|
||||
.INTR => {
|
||||
req = rem;
|
||||
continue;
|
||||
},
|
||||
.FAULT,
|
||||
.INVAL,
|
||||
.OPNOTSUPP,
|
||||
=> unreachable,
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
posix.nanosleep(s, ns);
|
||||
}
|
||||
|
||||
test sleep {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
const Thread = @This();
|
||||
const Impl = if (native_os == .windows)
|
||||
WindowsThreadImpl
|
||||
|
||||
@ -761,195 +761,54 @@ pub const TestFn = struct {
|
||||
func: *const fn () anyerror!void,
|
||||
};
|
||||
|
||||
/// This function type is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
/// Deprecated, use the `Panic` namespace instead.
|
||||
/// To be deleted after 0.14.0 is released.
|
||||
pub const PanicFn = fn ([]const u8, ?*StackTrace, ?usize) noreturn;
|
||||
/// Deprecated, use the `Panic` namespace instead.
|
||||
/// To be deleted after 0.14.0 is released.
|
||||
pub const panic: PanicFn = Panic.call;
|
||||
|
||||
/// This function is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const panic: PanicFn = if (@hasDecl(root, "panic"))
|
||||
root.panic
|
||||
else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))
|
||||
root.os.panic
|
||||
/// This namespace is used by the Zig compiler to emit various kinds of safety
|
||||
/// panics. These can be overridden by making a public `Panic` namespace in the
|
||||
/// root source file.
|
||||
pub const Panic: type = if (@hasDecl(root, "Panic"))
|
||||
root.Panic
|
||||
else if (@hasDecl(root, "panic")) // Deprecated, use `Panic` instead.
|
||||
DeprecatedPanic
|
||||
else if (builtin.zig_backend == .stage2_riscv64)
|
||||
std.debug.SimplePanic // https://github.com/ziglang/zig/issues/21519
|
||||
else
|
||||
default_panic;
|
||||
std.debug.FormattedPanic;
|
||||
|
||||
/// This function 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);
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
if (builtin.zig_backend == .stage2_riscv64) {
|
||||
std.debug.print("panic: {s}\n", .{msg});
|
||||
@breakpoint();
|
||||
std.posix.exit(127);
|
||||
}
|
||||
|
||||
switch (builtin.os.tag) {
|
||||
.freestanding => {
|
||||
while (true) {
|
||||
@breakpoint();
|
||||
}
|
||||
},
|
||||
.wasi => {
|
||||
std.debug.print("{s}", .{msg});
|
||||
std.posix.abort();
|
||||
},
|
||||
.uefi => {
|
||||
const uefi = std.os.uefi;
|
||||
|
||||
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";
|
||||
/// To be deleted after 0.14.0 is released.
|
||||
const DeprecatedPanic = struct {
|
||||
pub const call = root.panic;
|
||||
pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch;
|
||||
pub const unwrapError = std.debug.FormattedPanic.unwrapError;
|
||||
pub const outOfBounds = std.debug.FormattedPanic.outOfBounds;
|
||||
pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd;
|
||||
pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField;
|
||||
pub const messages = std.debug.FormattedPanic.messages;
|
||||
};
|
||||
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panicSentinelMismatch = Panic.sentinelMismatch;
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panicUnwrapError = Panic.unwrapError;
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panicOutOfBounds = Panic.outOfBounds;
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panicStartGreaterThanEnd = Panic.startGreaterThanEnd;
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panicInactiveUnionField = Panic.inactiveUnionField;
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const panic_messages = Panic.messages;
|
||||
|
||||
pub noinline fn returnError(st: *StackTrace) void {
|
||||
@branchHint(.cold);
|
||||
@branchHint(.unlikely);
|
||||
@setRuntimeSafety(false);
|
||||
addErrRetTraceAddr(st, @returnAddress());
|
||||
}
|
||||
|
||||
pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
|
||||
if (st.index < st.instruction_addresses.len)
|
||||
st.instruction_addresses[st.index] = addr;
|
||||
|
||||
st.instruction_addresses[st.index] = @returnAddress();
|
||||
st.index += 1;
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,9 @@ pub const SelfInfo = @import("debug/SelfInfo.zig");
|
||||
pub const Info = @import("debug/Info.zig");
|
||||
pub const Coverage = @import("debug/Coverage.zig");
|
||||
|
||||
pub const FormattedPanic = @import("debug/FormattedPanic.zig");
|
||||
pub const SimplePanic = @import("debug/SimplePanic.zig");
|
||||
|
||||
/// Unresolved source locations can be represented with a single `usize` that
|
||||
/// corresponds to a virtual memory address of the program counter. Combined
|
||||
/// with debug information, those values can be converted into a resolved
|
||||
@ -408,14 +411,21 @@ pub fn assertReadable(slice: []const volatile u8) void {
|
||||
for (slice) |*byte| _ = byte.*;
|
||||
}
|
||||
|
||||
/// By including a call to this function, the caller gains an error return trace
|
||||
/// secret parameter, making `@errorReturnTrace()` more useful. This is not
|
||||
/// necessary if the function already contains a call to an errorable function
|
||||
/// elsewhere.
|
||||
pub fn errorReturnTraceHelper() anyerror!void {}
|
||||
|
||||
/// Equivalent to `@panic` but with a formatted message.
|
||||
pub fn panic(comptime format: []const u8, args: anytype) noreturn {
|
||||
@branchHint(.cold);
|
||||
|
||||
errorReturnTraceHelper() catch unreachable;
|
||||
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,
|
||||
@ -436,7 +446,7 @@ pub fn panicExtra(
|
||||
break :blk &buf;
|
||||
},
|
||||
};
|
||||
std.builtin.panic(msg, trace, ret_addr);
|
||||
std.builtin.Panic.call(msg, trace, ret_addr);
|
||||
}
|
||||
|
||||
/// Non-zero whenever the program triggered a panic.
|
||||
@ -447,11 +457,70 @@ 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.
|
||||
pub fn defaultPanic(
|
||||
msg: []const u8,
|
||||
error_return_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();
|
||||
}
|
||||
|
||||
switch (builtin.os.tag) {
|
||||
.freestanding => {
|
||||
@trap();
|
||||
},
|
||||
.uefi => {
|
||||
const uefi = std.os.uefi;
|
||||
|
||||
var utf16_buffer: [1000]u16 = undefined;
|
||||
const len_minus_3 = std.unicode.utf8ToUtf16Le(&utf16_buffer, msg) catch 0;
|
||||
utf16_buffer[len_minus_3][0..3].* = .{ '\r', '\n', 0 };
|
||||
const len = len_minus_3 + 3;
|
||||
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 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 => {},
|
||||
}
|
||||
|
||||
if (enable_segfault_handler) {
|
||||
// If a segfault happens while panicking, we want it to actually segfault, not trigger
|
||||
// the handler.
|
||||
@ -465,7 +534,6 @@ 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
|
||||
{
|
||||
lockStdErr();
|
||||
defer unlockStdErr();
|
||||
@ -478,10 +546,9 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
|
||||
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);
|
||||
|
||||
if (error_return_trace) |t| dumpStackTrace(t.*);
|
||||
dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
|
||||
}
|
||||
|
||||
waitForOtherThreadToFinishPanicking();
|
||||
@ -489,15 +556,12 @@ 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();
|
||||
@ -1157,7 +1221,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 +1353,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 +1394,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" {
|
||||
|
||||
45
lib/std/debug/FormattedPanic.zig
Normal file
45
lib/std/debug/FormattedPanic.zig
Normal file
@ -0,0 +1,45 @@
|
||||
//! This namespace is the default one used by the Zig compiler to emit various
|
||||
//! kinds of safety panics, due to the logic in `std.builtin.Panic`.
|
||||
//!
|
||||
//! Since Zig does not have interfaces, this file serves as an example template
|
||||
//! for users to provide their own alternative panic handling.
|
||||
//!
|
||||
//! As an alternative, see `std.debug.SimplePanic`.
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
||||
/// Dumps a stack trace to standard error, then aborts.
|
||||
///
|
||||
/// Explicit calls to `@panic` lower to calling this function.
|
||||
pub const call: fn ([]const u8, ?*std.builtin.StackTrace, ?usize) noreturn = std.debug.defaultPanic;
|
||||
|
||||
pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
|
||||
@branchHint(.cold);
|
||||
std.debug.panicExtra(null, @returnAddress(), "sentinel mismatch: expected {any}, found {any}", .{
|
||||
expected, found,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn unwrapError(ert: ?*std.builtin.StackTrace, err: anyerror) noreturn {
|
||||
@branchHint(.cold);
|
||||
std.debug.panicExtra(ert, @returnAddress(), "attempt to unwrap error: {s}", .{@errorName(err)});
|
||||
}
|
||||
|
||||
pub fn outOfBounds(index: usize, len: usize) noreturn {
|
||||
@branchHint(.cold);
|
||||
std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
|
||||
}
|
||||
|
||||
pub fn startGreaterThanEnd(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 inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
|
||||
@branchHint(.cold);
|
||||
std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{
|
||||
@tagName(accessed), @tagName(active),
|
||||
});
|
||||
}
|
||||
|
||||
pub const messages = std.debug.SimplePanic.messages;
|
||||
86
lib/std/debug/SimplePanic.zig
Normal file
86
lib/std/debug/SimplePanic.zig
Normal file
@ -0,0 +1,86 @@
|
||||
//! This namespace is the default one used by the Zig compiler to emit various
|
||||
//! kinds of safety panics, due to the logic in `std.builtin.Panic`.
|
||||
//!
|
||||
//! Since Zig does not have interfaces, this file serves as an example template
|
||||
//! for users to provide their own alternative panic handling.
|
||||
//!
|
||||
//! As an alternative, see `std.debug.FormattedPanic`.
|
||||
|
||||
const std = @import("../std.zig");
|
||||
|
||||
/// Prints the message to stderr without a newline and then traps.
|
||||
///
|
||||
/// Explicit calls to `@panic` lower to calling this function.
|
||||
pub fn call(msg: []const u8, ert: ?*std.builtin.StackTrace, ra: ?usize) noreturn {
|
||||
@branchHint(.cold);
|
||||
_ = ert;
|
||||
_ = ra;
|
||||
std.debug.lockStdErr();
|
||||
const stderr = std.io.getStdErr();
|
||||
stderr.writeAll(msg) catch {};
|
||||
@trap();
|
||||
}
|
||||
|
||||
pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
|
||||
_ = found;
|
||||
call("sentinel mismatch", null, null);
|
||||
}
|
||||
|
||||
pub fn unwrapError(ert: ?*std.builtin.StackTrace, err: anyerror) noreturn {
|
||||
_ = ert;
|
||||
_ = &err;
|
||||
call("attempt to unwrap error", null, null);
|
||||
}
|
||||
|
||||
pub fn outOfBounds(index: usize, len: usize) noreturn {
|
||||
_ = index;
|
||||
_ = len;
|
||||
call("index out of bounds", null, null);
|
||||
}
|
||||
|
||||
pub fn startGreaterThanEnd(start: usize, end: usize) noreturn {
|
||||
_ = start;
|
||||
_ = end;
|
||||
call("start index is larger than end index", null, null);
|
||||
}
|
||||
|
||||
pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
|
||||
_ = accessed;
|
||||
call("access of inactive union field", null, null);
|
||||
}
|
||||
|
||||
pub const messages = struct {
|
||||
pub const reached_unreachable = "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 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 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";
|
||||
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const inactive_union_field = "access of inactive union field";
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const sentinel_mismatch = "sentinel mismatch";
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const unwrap_error = "attempt to unwrap error";
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const index_out_of_bounds = "index out of bounds";
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const start_index_greater_than_end = "start index is larger than end index";
|
||||
/// To be deleted after zig1.wasm is updated.
|
||||
pub const unreach = reached_unreachable;
|
||||
};
|
||||
@ -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 {
|
||||
|
||||
@ -8,82 +8,8 @@ const posix = std.posix;
|
||||
|
||||
pub const epoch = @import("time/epoch.zig");
|
||||
|
||||
/// Spurious wakeups are possible and no precision of timing is guaranteed.
|
||||
pub fn sleep(nanoseconds: u64) void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const big_ms_from_ns = nanoseconds / ns_per_ms;
|
||||
const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD);
|
||||
windows.kernel32.Sleep(ms);
|
||||
return;
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .wasi) {
|
||||
const w = std.os.wasi;
|
||||
const userdata: w.userdata_t = 0x0123_45678;
|
||||
const clock: w.subscription_clock_t = .{
|
||||
.id = .MONOTONIC,
|
||||
.timeout = nanoseconds,
|
||||
.precision = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
const in: w.subscription_t = .{
|
||||
.userdata = userdata,
|
||||
.u = .{
|
||||
.tag = .CLOCK,
|
||||
.u = .{ .clock = clock },
|
||||
},
|
||||
};
|
||||
|
||||
var event: w.event_t = undefined;
|
||||
var nevents: usize = undefined;
|
||||
_ = w.poll_oneoff(&in, &event, 1, &nevents);
|
||||
return;
|
||||
}
|
||||
|
||||
if (builtin.os.tag == .uefi) {
|
||||
const boot_services = std.os.uefi.system_table.boot_services.?;
|
||||
const us_from_ns = nanoseconds / ns_per_us;
|
||||
const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);
|
||||
_ = boot_services.stall(us);
|
||||
return;
|
||||
}
|
||||
|
||||
const s = nanoseconds / ns_per_s;
|
||||
const ns = nanoseconds % ns_per_s;
|
||||
|
||||
// Newer kernel ports don't have old `nanosleep()` and `clock_nanosleep()` has been around
|
||||
// since Linux 2.6 and glibc 2.1 anyway.
|
||||
if (builtin.os.tag == .linux) {
|
||||
const linux = std.os.linux;
|
||||
|
||||
var req: linux.timespec = .{
|
||||
.sec = std.math.cast(linux.time_t, s) orelse std.math.maxInt(linux.time_t),
|
||||
.nsec = std.math.cast(linux.time_t, ns) orelse std.math.maxInt(linux.time_t),
|
||||
};
|
||||
var rem: linux.timespec = undefined;
|
||||
|
||||
while (true) {
|
||||
switch (linux.E.init(linux.clock_nanosleep(.MONOTONIC, .{ .ABSTIME = false }, &req, &rem))) {
|
||||
.SUCCESS => return,
|
||||
.INTR => {
|
||||
req = rem;
|
||||
continue;
|
||||
},
|
||||
.FAULT,
|
||||
.INVAL,
|
||||
.OPNOTSUPP,
|
||||
=> unreachable,
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
posix.nanosleep(s, ns);
|
||||
}
|
||||
|
||||
test sleep {
|
||||
sleep(1);
|
||||
}
|
||||
/// Deprecated: moved to std.Thread.sleep
|
||||
pub const sleep = std.Thread.sleep;
|
||||
|
||||
/// Get a calendar timestamp, in seconds, relative to UTC 1970-01-01.
|
||||
/// Precision of timing depends on the hardware and operating system.
|
||||
@ -155,7 +81,7 @@ test milliTimestamp {
|
||||
const margin = ns_per_ms * 50;
|
||||
|
||||
const time_0 = milliTimestamp();
|
||||
sleep(ns_per_ms);
|
||||
std.Thread.sleep(ns_per_ms);
|
||||
const time_1 = milliTimestamp();
|
||||
const interval = time_1 - time_0;
|
||||
try testing.expect(interval > 0);
|
||||
@ -359,7 +285,7 @@ test Timer {
|
||||
const margin = ns_per_ms * 150;
|
||||
|
||||
var timer = try Timer.start();
|
||||
sleep(10 * ns_per_ms);
|
||||
std.Thread.sleep(10 * ns_per_ms);
|
||||
const time_0 = timer.read();
|
||||
try testing.expect(time_0 > 0);
|
||||
// Tests should not depend on timings: skip test if outside margin.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
622
src/Sema.zig
622
src/Sema.zig
File diff suppressed because it is too large
Load Diff
@ -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),
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
20
src/Zcu.zig
20
src/Zcu.zig
@ -220,7 +220,7 @@ generation: u32 = 0,
|
||||
pub const PerThread = @import("Zcu/PerThread.zig");
|
||||
|
||||
pub const PanicId = enum {
|
||||
unreach,
|
||||
reached_unreachable,
|
||||
unwrap_null,
|
||||
cast_to_null,
|
||||
incorrect_alignment,
|
||||
@ -232,15 +232,10 @@ pub const PanicId = enum {
|
||||
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,
|
||||
@ -2923,17 +2918,10 @@ pub fn addGlobalAssembly(zcu: *Zcu, cau: InternPool.Cau.Index, source: []const u
|
||||
}
|
||||
|
||||
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.
|
||||
/// When this feature is enabled, Sema will emit calls to
|
||||
/// `std.builtin.Panic` functions 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,
|
||||
|
||||
@ -1,6 +1,32 @@
|
||||
//! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`.
|
||||
//! Any operation which mutates `InternPool` state lives here rather than on `Zcu`.
|
||||
|
||||
const Air = @import("../Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const Ast = std.zig.Ast;
|
||||
const AstGen = std.zig.AstGen;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const Cache = std.Build.Cache;
|
||||
const dev = @import("../dev.zig");
|
||||
const InternPool = @import("../InternPool.zig");
|
||||
const AnalUnit = InternPool.AnalUnit;
|
||||
const isUpDir = @import("../introspect.zig").isUpDir;
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const log = std.log.scoped(.zcu);
|
||||
const Module = @import("../Package.zig").Module;
|
||||
const Sema = @import("../Sema.zig");
|
||||
const std = @import("std");
|
||||
const target_util = @import("../target.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const Type = @import("../Type.zig");
|
||||
const Value = @import("../Value.zig");
|
||||
const Zcu = @import("../Zcu.zig");
|
||||
const Zir = std.zig.Zir;
|
||||
|
||||
zcu: *Zcu,
|
||||
|
||||
/// Dense, per-thread unique index.
|
||||
@ -2697,11 +2723,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 +2980,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
|
||||
@ -3069,14 +3101,6 @@ pub fn structPackedFieldBitOffset(
|
||||
unreachable; // index out of bounds
|
||||
}
|
||||
|
||||
pub fn getBuiltin(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Air.Inst.Ref {
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
const nav = try pt.getBuiltinNav(name);
|
||||
pt.ensureCauAnalyzed(ip.getNav(nav).analysis_owner.unwrap().?) catch @panic("std.builtin is corrupt");
|
||||
return Air.internedToRef(ip.getNav(nav).status.resolved.val);
|
||||
}
|
||||
|
||||
pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!InternPool.Nav.Index {
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.gpa;
|
||||
@ -3094,13 +3118,6 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern
|
||||
return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt");
|
||||
}
|
||||
|
||||
pub fn getBuiltinType(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Type {
|
||||
const ty_inst = try pt.getBuiltin(name);
|
||||
const ty = Type.fromInterned(ty_inst.toInterned() orelse @panic("std.builtin is corrupt"));
|
||||
ty.resolveFully(pt) catch @panic("std.builtin is corrupt");
|
||||
return ty;
|
||||
}
|
||||
|
||||
pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Type {
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
@ -3650,28 +3667,21 @@ pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace
|
||||
namespace.generation = zcu.generation;
|
||||
}
|
||||
|
||||
const Air = @import("../Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const Ast = std.zig.Ast;
|
||||
const AstGen = std.zig.AstGen;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const Cache = std.Build.Cache;
|
||||
const dev = @import("../dev.zig");
|
||||
const InternPool = @import("../InternPool.zig");
|
||||
const AnalUnit = InternPool.AnalUnit;
|
||||
const isUpDir = @import("../introspect.zig").isUpDir;
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const log = std.log.scoped(.zcu);
|
||||
const Module = @import("../Package.zig").Module;
|
||||
const Sema = @import("../Sema.zig");
|
||||
const std = @import("std");
|
||||
const target_util = @import("../target.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const Type = @import("../Type.zig");
|
||||
const Value = @import("../Value.zig");
|
||||
const Zcu = @import("../Zcu.zig");
|
||||
const Zir = std.zig.Zir;
|
||||
pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index {
|
||||
const ptr_ty = (try pt.ptrTypeSema(.{
|
||||
.child = pt.zcu.intern_pool.typeOf(val),
|
||||
.flags = .{
|
||||
.alignment = .none,
|
||||
.is_const = true,
|
||||
.address_space = .generic,
|
||||
},
|
||||
})).toIntern();
|
||||
return pt.intern(.{ .ptr = .{
|
||||
.ty = ptr_ty,
|
||||
.base_addr = .{ .uav = .{
|
||||
.val = val,
|
||||
.orig_ty = ptr_ty,
|
||||
} },
|
||||
.byte_offset = 0,
|
||||
} });
|
||||
}
|
||||
|
||||
@ -3848,13 +3848,13 @@ pub const Object = struct {
|
||||
|
||||
.undef => unreachable, // handled above
|
||||
.simple_value => |simple_value| switch (simple_value) {
|
||||
.undefined,
|
||||
.void,
|
||||
.null,
|
||||
.empty_struct,
|
||||
.@"unreachable",
|
||||
.generic_poison,
|
||||
=> unreachable, // non-runtime values
|
||||
.undefined => unreachable, // non-runtime value
|
||||
.void => unreachable, // non-runtime value
|
||||
.null => unreachable, // non-runtime value
|
||||
.empty_struct => unreachable, // non-runtime value
|
||||
.@"unreachable" => unreachable, // non-runtime value
|
||||
.generic_poison => unreachable, // non-runtime value
|
||||
|
||||
.false => .false,
|
||||
.true => .true,
|
||||
},
|
||||
|
||||
@ -13,11 +13,23 @@ const Sema = @import("Sema.zig");
|
||||
const InternPool = @import("InternPool.zig");
|
||||
const Zir = std.zig.Zir;
|
||||
const Decl = Zcu.Decl;
|
||||
const dev = @import("dev.zig");
|
||||
|
||||
/// 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) struct {
|
||||
pub const call = compilerPanic;
|
||||
pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch;
|
||||
pub const unwrapError = std.debug.FormattedPanic.unwrapError;
|
||||
pub const outOfBounds = std.debug.FormattedPanic.outOfBounds;
|
||||
pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd;
|
||||
pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField;
|
||||
pub const messages = std.debug.FormattedPanic.messages;
|
||||
} else if (dev.env == .bootstrap)
|
||||
std.debug.SimplePanic
|
||||
else
|
||||
std.debug.FormattedPanic;
|
||||
|
||||
/// Install signal handlers to identify crashes and report diagnostics.
|
||||
pub fn initialize() void {
|
||||
@ -317,9 +329,6 @@ const PanicSwitch = struct {
|
||||
/// until all panicking threads have dumped their traces.
|
||||
var panicking = std.atomic.Value(u8).init(0);
|
||||
|
||||
// Locked to avoid interleaving panic messages from multiple threads.
|
||||
var panic_mutex = std.Thread.Mutex{};
|
||||
|
||||
/// Tracks the state of the current panic. If the code within the
|
||||
/// panic triggers a secondary panic, this allows us to recover.
|
||||
threadlocal var panic_state_raw: PanicState = .{};
|
||||
@ -387,7 +396,7 @@ const PanicSwitch = struct {
|
||||
|
||||
state.recover_stage = .release_ref_count;
|
||||
|
||||
panic_mutex.lock();
|
||||
std.debug.lockStdErr();
|
||||
|
||||
state.recover_stage = .release_mutex;
|
||||
|
||||
@ -447,7 +456,7 @@ const PanicSwitch = struct {
|
||||
noinline fn releaseMutex(state: *volatile PanicState) noreturn {
|
||||
state.recover_stage = .abort;
|
||||
|
||||
panic_mutex.unlock();
|
||||
std.debug.unlockStdErr();
|
||||
|
||||
goTo(releaseRefCount, .{state});
|
||||
}
|
||||
|
||||
@ -3230,7 +3230,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
|
||||
self.zig_text_seg_index = try self.addSegment("__TEXT_ZIG", .{
|
||||
.fileoff = off,
|
||||
.filesize = filesize,
|
||||
.vmaddr = base_vmaddr + 0x8000000,
|
||||
.vmaddr = base_vmaddr + 0x4000000,
|
||||
.vmsize = filesize,
|
||||
.prot = macho.PROT.READ | macho.PROT.EXEC,
|
||||
});
|
||||
|
||||
11
src/main.zig
11
src/main.zig
@ -44,8 +44,7 @@ pub const std_options = .{
|
||||
},
|
||||
};
|
||||
|
||||
// Crash report needs to override the panic handler
|
||||
pub const panic = crash_report.panic;
|
||||
pub const Panic = crash_report.Panic;
|
||||
|
||||
var wasi_preopens: fs.wasi.Preopens = undefined;
|
||||
pub fn wasi_cwd() std.os.wasi.fd_t {
|
||||
@ -826,7 +825,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 +1535,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 +3405,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,
|
||||
|
||||
@ -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(),
|
||||
} }),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
pub fn main() void {}
|
||||
|
||||
// run
|
||||
// target=x86_64-linux,x86_64-macos,x86_64-windows,x86_64-plan9
|
||||
// target=x86_64-linux,x86_64-macos,x86_64-windows
|
||||
//
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user