From 83991efe10d92c4b920d7b7fc75be98ed7854ad7 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 24 Jan 2025 02:19:28 +0000 Subject: [PATCH] compiler: yet more panic handler changes * `std.builtin.Panic` -> `std.builtin.panic`, because it is a namespace. * `root.Panic` -> `root.panic` for the same reason. There are type checks so that we still allow the legacy `pub fn panic` strategy in the 0.14.0 release. * `std.debug.SimplePanic` -> `std.debug.simple_panic`, same reason. * `std.debug.NoPanic` -> `std.debug.no_panic`, same reason. * `std.debug.FormattedPanic` is now a function `std.debug.FullPanic` which takes as input a `panicFn` and returns a namespace with all the panic functions. This handles the incredibly common case of just wanting to override how the message is printed, whilst keeping nice formatted panics. * Remove `std.builtin.panic.messages`; now, every safety panic has its own function. This reduces binary bloat, as calls to these functions no longer need to prepare any arguments (aside from the error return trace). * Remove some legacy declarations, since a zig1.wasm update has happened. Most of these were related to the panic handler, but a quick grep for "zig1" brought up a couple more results too. Also, add some missing type checks to Sema. Resolves: #22584 formatted -> full --- lib/std/Target.zig | 2 +- lib/std/builtin.zig | 49 ++---- lib/std/debug.zig | 123 +++++++++++++- lib/std/debug/FormattedPanic.zig | 45 ----- lib/std/debug/NoPanic.zig | 59 ------- lib/std/debug/no_panic.zig | 160 ++++++++++++++++++ .../{SimplePanic.zig => simple_panic.zig} | 98 +++++++++-- lib/std/meta.zig | 3 +- src/Sema.zig | 155 +++++++++-------- src/Zcu.zig | 153 +++++++++-------- src/Zcu/PerThread.zig | 2 +- src/codegen/llvm.zig | 49 +----- src/crash_report.zig | 16 +- src/main.zig | 2 +- .../bad_panic_call_signature.zig | 46 +++++ .../bad_panic_generic_signature.zig | 41 +++++ .../compile_errors/bad_panic_signature.zig | 28 --- test/incremental/change_panic_handler | 30 +--- .../incremental/change_panic_handler_explicit | 141 +++++++++++++++ 19 files changed, 792 insertions(+), 410 deletions(-) delete mode 100644 lib/std/debug/FormattedPanic.zig delete mode 100644 lib/std/debug/NoPanic.zig create mode 100644 lib/std/debug/no_panic.zig rename lib/std/debug/{SimplePanic.zig => simple_panic.zig} (57%) create mode 100644 test/cases/compile_errors/bad_panic_call_signature.zig create mode 100644 test/cases/compile_errors/bad_panic_generic_signature.zig delete mode 100644 test/cases/compile_errors/bad_panic_signature.zig create mode 100644 test/incremental/change_panic_handler_explicit diff --git a/lib/std/Target.zig b/lib/std/Target.zig index ce6eaa9f2f..9eea755189 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -370,7 +370,7 @@ pub const Os = struct { range: std.SemanticVersion.Range, glibc: std.SemanticVersion, /// Android API level. - android: u32 = 14, // This default value is to be deleted after zig1.wasm is updated. + android: u32, pub inline fn includesVersion(range: LinuxVersionRange, ver: std.SemanticVersion) bool { return range.range.includesVersion(ver); diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 73a47fa3af..b2e973aea8 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -1110,45 +1110,28 @@ pub const TestFn = struct { /// 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 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 +/// 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 - std.debug.FormattedPanic; - -/// 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; +pub const panic: type = p: { + if (@hasDecl(root, "panic")) { + if (@TypeOf(root.panic) != type) { + break :p std.debug.FullPanic(root.panic); // Deprecated; make `panic` a namespace instead. + } + break :p root.panic; + } + if (@hasDecl(root, "Panic")) { + break :p root.Panic; // Deprecated; use `panic` instead. + } + if (builtin.zig_backend == .stage2_riscv64) { + break :p std.debug.simple_panic; + } + break :p std.debug.FullPanic(std.debug.defaultPanic); }; /// 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 const Panic = panic; pub noinline fn returnError() void { @branchHint(.unlikely); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3624d6d3e7..0ff51a7339 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -21,9 +21,124 @@ 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"); -pub const NoPanic = @import("debug/NoPanic.zig"); +pub const simple_panic = @import("debug/simple_panic.zig"); +pub const no_panic = @import("debug/no_panic.zig"); + +/// A fully-featured panic handler namespace which lowers all panics to calls to `panicFn`. +/// Safety panics will use formatted printing to provide a meaningful error message. +/// The signature of `panicFn` should match that of `defaultPanic`. +pub fn FullPanic(comptime panicFn: fn ([]const u8, ?*std.builtin.StackTrace, ?usize) noreturn) type { + return struct { + pub const call = panicFn; + 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 fn reachedUnreachable() noreturn { + @branchHint(.cold); + call("reached unreachable code", null, @returnAddress()); + } + pub fn unwrapNull() noreturn { + @branchHint(.cold); + call("attempt to use null value", null, @returnAddress()); + } + pub fn castToNull() noreturn { + @branchHint(.cold); + call("cast causes pointer to be null", null, @returnAddress()); + } + pub fn incorrectAlignment() noreturn { + @branchHint(.cold); + call("incorrect alignment", null, @returnAddress()); + } + pub fn invalidErrorCode() noreturn { + @branchHint(.cold); + call("invalid error code", null, @returnAddress()); + } + pub fn castTruncatedData() noreturn { + @branchHint(.cold); + call("integer cast truncated bits", null, @returnAddress()); + } + pub fn negativeToUnsigned() noreturn { + @branchHint(.cold); + call("attempt to cast negative value to unsigned integer", null, @returnAddress()); + } + pub fn integerOverflow() noreturn { + @branchHint(.cold); + call("integer overflow", null, @returnAddress()); + } + pub fn shlOverflow() noreturn { + @branchHint(.cold); + call("left shift overflowed bits", null, @returnAddress()); + } + pub fn shrOverflow() noreturn { + @branchHint(.cold); + call("right shift overflowed bits", null, @returnAddress()); + } + pub fn divideByZero() noreturn { + @branchHint(.cold); + call("division by zero", null, @returnAddress()); + } + pub fn exactDivisionRemainder() noreturn { + @branchHint(.cold); + call("exact division produced remainder", null, @returnAddress()); + } + pub fn integerPartOutOfBounds() noreturn { + @branchHint(.cold); + call("integer part of floating point value out of bounds", null, @returnAddress()); + } + pub fn corruptSwitch() noreturn { + @branchHint(.cold); + call("switch on corrupt value", null, @returnAddress()); + } + pub fn shiftRhsTooBig() noreturn { + @branchHint(.cold); + call("shift amount is greater than the type size", null, @returnAddress()); + } + pub fn invalidEnumValue() noreturn { + @branchHint(.cold); + call("invalid enum value", null, @returnAddress()); + } + pub fn forLenMismatch() noreturn { + @branchHint(.cold); + call("for loop over objects with non-equal lengths", null, @returnAddress()); + } + pub fn memcpyLenMismatch() noreturn { + @branchHint(.cold); + call("@memcpy arguments have non-equal lengths", null, @returnAddress()); + } + pub fn memcpyAlias() noreturn { + @branchHint(.cold); + call("@memcpy arguments alias", null, @returnAddress()); + } + pub fn noreturnReturned() noreturn { + @branchHint(.cold); + call("'noreturn' function returned", null, @returnAddress()); + } + + /// To be deleted after zig1.wasm update. + pub const messages = simple_panic.messages; + }; +} /// Unresolved source locations can be represented with a single `usize` that /// corresponds to a virtual memory address of the program counter. Combined @@ -441,7 +556,7 @@ pub fn panicExtra( break :blk &buf; }, }; - std.builtin.Panic.call(msg, trace, ret_addr); + std.builtin.panic.call(msg, trace, ret_addr); } /// Non-zero whenever the program triggered a panic. diff --git a/lib/std/debug/FormattedPanic.zig b/lib/std/debug/FormattedPanic.zig deleted file mode 100644 index 0ad6ea696c..0000000000 --- a/lib/std/debug/FormattedPanic.zig +++ /dev/null @@ -1,45 +0,0 @@ -//! 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; diff --git a/lib/std/debug/NoPanic.zig b/lib/std/debug/NoPanic.zig deleted file mode 100644 index fb26de53f1..0000000000 --- a/lib/std/debug/NoPanic.zig +++ /dev/null @@ -1,59 +0,0 @@ -//! This namespace can be used with `pub const Panic = std.debug.NoPanic;` in the root file. -//! It emits as little code as possible, for testing purposes. -//! -//! For a functional alternative, see `std.debug.FormattedPanic`. - -const std = @import("../std.zig"); - -pub fn call(_: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn sentinelMismatch(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn unwrapError(_: ?*std.builtin.StackTrace, _: anyerror) noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn outOfBounds(_: usize, _: usize) noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn startGreaterThanEnd(_: usize, _: usize) noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn inactiveUnionField(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - @trap(); -} - -pub const messages = struct { - pub const reached_unreachable = ""; - pub const unwrap_null = ""; - pub const cast_to_null = ""; - pub const incorrect_alignment = ""; - pub const invalid_error_code = ""; - pub const cast_truncated_data = ""; - pub const negative_to_unsigned = ""; - pub const integer_overflow = ""; - pub const shl_overflow = ""; - pub const shr_overflow = ""; - pub const divide_by_zero = ""; - pub const exact_division_remainder = ""; - pub const integer_part_out_of_bounds = ""; - pub const corrupt_switch = ""; - pub const shift_rhs_too_big = ""; - pub const invalid_enum_value = ""; - pub const for_len_mismatch = ""; - pub const memcpy_len_mismatch = ""; - pub const memcpy_alias = ""; - pub const noreturn_returned = ""; -}; diff --git a/lib/std/debug/no_panic.zig b/lib/std/debug/no_panic.zig new file mode 100644 index 0000000000..edd61ab974 --- /dev/null +++ b/lib/std/debug/no_panic.zig @@ -0,0 +1,160 @@ +//! This namespace can be used with `pub const panic = std.debug.no_panic;` in the root file. +//! It emits as little code as possible, for testing purposes. +//! +//! For a functional alternative, see `std.debug.FullPanic`. + +const std = @import("../std.zig"); + +pub fn call(_: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn sentinelMismatch(_: anytype, _: anytype) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn unwrapError(_: ?*std.builtin.StackTrace, _: anyerror) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn outOfBounds(_: usize, _: usize) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn startGreaterThanEnd(_: usize, _: usize) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn inactiveUnionField(_: anytype, _: anytype) noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn reachedUnreachable() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn unwrapNull() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn castToNull() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn incorrectAlignment() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn invalidErrorCode() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn castTruncatedData() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn negativeToUnsigned() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn integerOverflow() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn shlOverflow() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn shrOverflow() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn divideByZero() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn exactDivisionRemainder() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn integerPartOutOfBounds() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn corruptSwitch() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn shiftRhsTooBig() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn invalidEnumValue() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn forLenMismatch() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn memcpyLenMismatch() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn memcpyAlias() noreturn { + @branchHint(.cold); + @trap(); +} + +pub fn noreturnReturned() noreturn { + @branchHint(.cold); + @trap(); +} + +/// To be deleted after zig1.wasm update. +pub const messages = struct { + pub const reached_unreachable = ""; + pub const unwrap_null = ""; + pub const cast_to_null = ""; + pub const incorrect_alignment = ""; + pub const invalid_error_code = ""; + pub const cast_truncated_data = ""; + pub const negative_to_unsigned = ""; + pub const integer_overflow = ""; + pub const shl_overflow = ""; + pub const shr_overflow = ""; + pub const divide_by_zero = ""; + pub const exact_division_remainder = ""; + pub const integer_part_out_of_bounds = ""; + pub const corrupt_switch = ""; + pub const shift_rhs_too_big = ""; + pub const invalid_enum_value = ""; + pub const for_len_mismatch = ""; + pub const memcpy_len_mismatch = ""; + pub const memcpy_alias = ""; + pub const noreturn_returned = ""; +}; diff --git a/lib/std/debug/SimplePanic.zig b/lib/std/debug/simple_panic.zig similarity index 57% rename from lib/std/debug/SimplePanic.zig rename to lib/std/debug/simple_panic.zig index 9685642a5c..6a6c12aa2d 100644 --- a/lib/std/debug/SimplePanic.zig +++ b/lib/std/debug/simple_panic.zig @@ -1,10 +1,10 @@ //! 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`. +//! 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`. +//! As an alternative, see `std.debug.FullPanic`. const std = @import("../std.zig"); @@ -49,6 +49,87 @@ pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn { call("access of inactive union field", null, null); } +pub fn reachedUnreachable() noreturn { + call("reached unreachable code", null, null); +} + +pub fn unwrapNull() noreturn { + call("attempt to use null value", null, null); +} + +pub fn castToNull() noreturn { + call("cast causes pointer to be null", null, null); +} + +pub fn incorrectAlignment() noreturn { + call("incorrect alignment", null, null); +} + +pub fn invalidErrorCode() noreturn { + call("invalid error code", null, null); +} + +pub fn castTruncatedData() noreturn { + call("integer cast truncated bits", null, null); +} + +pub fn negativeToUnsigned() noreturn { + call("attempt to cast negative value to unsigned integer", null, null); +} + +pub fn integerOverflow() noreturn { + call("integer overflow", null, null); +} + +pub fn shlOverflow() noreturn { + call("left shift overflowed bits", null, null); +} + +pub fn shrOverflow() noreturn { + call("right shift overflowed bits", null, null); +} + +pub fn divideByZero() noreturn { + call("division by zero", null, null); +} + +pub fn exactDivisionRemainder() noreturn { + call("exact division produced remainder", null, null); +} + +pub fn integerPartOutOfBounds() noreturn { + call("integer part of floating point value out of bounds", null, null); +} + +pub fn corruptSwitch() noreturn { + call("switch on corrupt value", null, null); +} + +pub fn shiftRhsTooBig() noreturn { + call("shift amount is greater than the type size", null, null); +} + +pub fn invalidEnumValue() noreturn { + call("invalid enum value", null, null); +} + +pub fn forLenMismatch() noreturn { + call("for loop over objects with non-equal lengths", null, null); +} + +pub fn memcpyLenMismatch() noreturn { + call("@memcpy arguments have non-equal lengths", null, null); +} + +pub fn memcpyAlias() noreturn { + call("@memcpy arguments alias", null, null); +} + +pub fn noreturnReturned() noreturn { + call("'noreturn' function returned", null, null); +} + +/// To be deleted after zig1.wasm update. pub const messages = struct { pub const reached_unreachable = "reached unreachable code"; pub const unwrap_null = "attempt to use null value"; @@ -70,17 +151,4 @@ pub const messages = struct { 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; }; diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 08c8589682..bf43432463 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -448,8 +448,7 @@ pub fn fieldNames(comptime T: type) *const [fields(T).len][:0]const u8 { return comptime blk: { const fieldInfos = fields(T); var names: [fieldInfos.len][:0]const u8 = undefined; - // This concat can be removed with the next zig1 update. - for (&names, fieldInfos) |*name, field| name.* = field.name ++ ""; + for (&names, fieldInfos) |*name, field| name.* = field.name; const final = names; break :blk &final; }; diff --git a/src/Sema.zig b/src/Sema.zig index 833b05413f..b8fe4636f8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5918,13 +5918,14 @@ fn zirCompileLog( } fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const msg_inst = try sema.resolveInst(inst_data.operand); - // `panicWithMsg` would perform this coercion for us, but we can get a better - // source location if we do it here. - const coerced_msg = try sema.coerce(block, Type.slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0)); + const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0)); if (block.isComptime()) { return sema.fail(block, src, "encountered @panic at comptime", .{}); @@ -5936,7 +5937,23 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void sema.branch_hint = .cold; } - try sema.panicWithMsg(block, src, coerced_msg, .@"@panic"); + if (!zcu.backendSupportsFeature(.panic_fn)) { + _ = try block.addNoOp(.trap); + return; + } + + try sema.ensureMemoizedStateResolved(src, .panic); + try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"panic.call")); + + const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"panic.call")); + const null_stack_trace = Air.internedToRef(zcu.null_stack_trace); + + const opt_usize_ty = try pt.optionalType(.usize_type); + const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ + .ty = opt_usize_ty.toIntern(), + .val = .none, + } }))); + try sema.callBuiltin(block, src, panic_fn, .auto, &.{ coerced_msg, null_stack_trace, null_ret_addr }, .@"@panic"); } fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -13787,7 +13804,7 @@ fn maybeErrorUnwrap( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const msg_inst = try sema.resolveInst(inst_data.operand); - const panic_fn = try getBuiltin(sema, operand_src, .@"Panic.call"); + const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value }; try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); @@ -27083,15 +27100,16 @@ fn explainWhyTypeIsNotPacked( /// Backends depend on panic decls being available when lowering safety-checked /// instructions. This function ensures the panic function will be available to /// be called during that time. -fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.PanicId) !InternPool.Index { +fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index { const zcu = sema.pt.zcu; try sema.ensureMemoizedStateResolved(src, .panic); - try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"Panic.call")); + const panic_func = zcu.builtin_decl_values.get(panic_id.toBuiltin()); + try zcu.ensureFuncBodyAnalysisQueued(panic_func); switch (sema.owner.unwrap()) { .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true), } - return zcu.builtin_decl_values.get(panic_id.toBuiltin()); + return panic_func; } fn addSafetyCheck( @@ -27099,7 +27117,7 @@ fn addSafetyCheck( parent_block: *Block, src: LazySrcLoc, ok: Air.Inst.Ref, - panic_id: Zcu.PanicId, + panic_id: Zcu.SimplePanicId, ) !void { const gpa = sema.gpa; assert(!parent_block.isComptime()); @@ -27186,29 +27204,6 @@ fn addSafetyCheckExtra( parent_block.instructions.appendAssumeCapacity(block_inst); } -fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, operation: CallOperation) !void { - const pt = sema.pt; - const zcu = pt.zcu; - - if (!zcu.backendSupportsFeature(.panic_fn)) { - _ = try block.addNoOp(.trap); - return; - } - - try sema.ensureMemoizedStateResolved(src, .panic); - try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"Panic.call")); - - const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"Panic.call")); - const null_stack_trace = Air.internedToRef(zcu.null_stack_trace); - - const opt_usize_ty = try pt.optionalType(.usize_type); - const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ - .ty = opt_usize_ty.toIntern(), - .val = .none, - } }))); - try sema.callBuiltin(block, src, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }, operation); -} - fn addSafetyCheckUnwrapError( sema: *Sema, parent_block: *Block, @@ -27246,7 +27241,7 @@ fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air. if (!zcu.backendSupportsFeature(.panic_fn)) { _ = try block.addNoOp(.trap); } else { - const panic_fn = try getBuiltin(sema, src, .@"Panic.unwrapError"); + const panic_fn = try getBuiltin(sema, src, .@"panic.unwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); @@ -27263,7 +27258,7 @@ fn addSafetyCheckIndexOob( ) !void { assert(!parent_block.isComptime()); const ok = try parent_block.addBinOp(cmp_op, index, len); - return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.outOfBounds", &.{ index, len }); + return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len }); } fn addSafetyCheckInactiveUnionField( @@ -27275,7 +27270,7 @@ fn addSafetyCheckInactiveUnionField( ) !void { assert(!parent_block.isComptime()); const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); - return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.inactiveUnionField", &.{ active_tag, wanted_tag }); + return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag }); } fn addSafetyCheckSentinelMismatch( @@ -27316,7 +27311,7 @@ fn addSafetyCheckSentinelMismatch( break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); }; - return addSafetyCheckCall(sema, parent_block, src, ok, .@"Panic.sentinelMismatch", &.{ + return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{ expected_sentinel, actual_sentinel, }); } @@ -27358,9 +27353,13 @@ fn addSafetyCheckCall( } /// This does not set `sema.branch_hint`. -fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) CompileError!void { - const msg_val = try sema.preparePanicId(src, panic_id); - try sema.panicWithMsg(block, src, Air.internedToRef(msg_val), .@"safety check"); +fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void { + if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) { + _ = try block.addNoOp(.trap); + } else { + const panic_fn = try sema.preparePanicId(src, panic_id); + try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check"); + } } fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { @@ -32818,7 +32817,7 @@ fn analyzeSlice( assert(!block.isComptime()); try sema.requireRuntimeBlock(block, src, runtime_src.?); const ok = try block.addBinOp(.cmp_lte, start, end); - try sema.addSafetyCheckCall(block, src, ok, .@"Panic.startGreaterThanEnd", &.{ start, end }); + try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end }); } const new_len = if (by_length) try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) @@ -38525,14 +38524,9 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, break :val uncoerced_val; }, .func => val: { - if (try sema.getExpectedBuiltinFnType(src, builtin_decl)) |func_ty| { - const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src); - break :val .fromInterned(coerced.toInterned().?); - } - if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") { - return sema.fail(block, src, "{s}.{s} is not a function", .{ parent_name, name }); - } - break :val uncoerced_val; + const func_ty = try sema.getExpectedBuiltinFnType(src, builtin_decl); + const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src); + break :val .fromInterned(coerced.toInterned().?); }, .string => val: { const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src); @@ -38567,16 +38561,19 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, return any_changed; } -/// Given that `decl.kind() == .func`, get the type expected of the function if necessary. -/// If this will be type checked by `Sema` anyway, this function may return `null`. In -/// particular, generic functions should return `null`, as `Sema` will necessarily check -/// them at instantiation time. Returning non-null is necessary only when backends can emit -/// calls to the function, as is the case with the panic handler. -fn getExpectedBuiltinFnType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) CompileError!?Type { +/// Given that `decl.kind() == .func`, get the type expected of the function. +fn getExpectedBuiltinFnType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) CompileError!Type { const pt = sema.pt; return switch (decl) { + // `noinline fn () void` + .returnError => try pt.funcType(.{ + .param_types = &.{}, + .return_type = .void_type, + .is_noinline = true, + }), + // `fn ([]const u8, ?*StackTrace, ?usize) noreturn` - .@"Panic.call" => try pt.funcType(.{ + .@"panic.call" => try pt.funcType(.{ .param_types = &.{ .slice_const_u8_type, (try pt.optionalType( @@ -38589,8 +38586,17 @@ fn getExpectedBuiltinFnType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) .return_type = .noreturn_type, }), + // `fn (anytype, anytype) noreturn` + .@"panic.sentinelMismatch", + .@"panic.inactiveUnionField", + => try pt.funcType(.{ + .param_types = &.{ .generic_poison_type, .generic_poison_type }, + .return_type = .noreturn_type, + .is_generic = true, + }), + // `fn (?*StackTrace, anyerror) noreturn` - .@"Panic.unwrapError" => try pt.funcType(.{ + .@"panic.unwrapError" => try pt.funcType(.{ .param_types = &.{ (try pt.optionalType( (try pt.singleMutPtrType( @@ -38603,21 +38609,38 @@ fn getExpectedBuiltinFnType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) }), // `fn (usize, usize) noreturn` - .@"Panic.outOfBounds", - .@"Panic.startGreaterThanEnd", + .@"panic.outOfBounds", + .@"panic.startGreaterThanEnd", => try pt.funcType(.{ .param_types = &.{ .usize_type, .usize_type }, .return_type = .noreturn_type, }), - // Generic functions, so calls are necessarily validated by Sema - .@"Panic.sentinelMismatch", - .@"Panic.inactiveUnionField", - => null, - - // Other functions called exclusively by Sema - .returnError, - => null, + // `fn () noreturn` + .@"panic.reachedUnreachable", + .@"panic.unwrapNull", + .@"panic.castToNull", + .@"panic.incorrectAlignment", + .@"panic.invalidErrorCode", + .@"panic.castTruncatedData", + .@"panic.negativeToUnsigned", + .@"panic.integerOverflow", + .@"panic.shlOverflow", + .@"panic.shrOverflow", + .@"panic.divideByZero", + .@"panic.exactDivisionRemainder", + .@"panic.integerPartOutOfBounds", + .@"panic.corruptSwitch", + .@"panic.shiftRhsTooBig", + .@"panic.invalidEnumValue", + .@"panic.forLenMismatch", + .@"panic.memcpyLenMismatch", + .@"panic.memcpyAlias", + .@"panic.noreturnReturned", + => try pt.funcType(.{ + .param_types = &.{}, + .return_type = .noreturn_type, + }), else => unreachable, }; diff --git a/src/Zcu.zig b/src/Zcu.zig index c75cd5d40c..3582e8af3a 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -269,34 +269,33 @@ pub const BuiltinDecl = enum { @"Type.Opaque", @"Type.Declaration", - Panic, - @"Panic.call", - @"Panic.sentinelMismatch", - @"Panic.unwrapError", - @"Panic.outOfBounds", - @"Panic.startGreaterThanEnd", - @"Panic.inactiveUnionField", - @"Panic.messages", - @"Panic.messages.reached_unreachable", - @"Panic.messages.unwrap_null", - @"Panic.messages.cast_to_null", - @"Panic.messages.incorrect_alignment", - @"Panic.messages.invalid_error_code", - @"Panic.messages.cast_truncated_data", - @"Panic.messages.negative_to_unsigned", - @"Panic.messages.integer_overflow", - @"Panic.messages.shl_overflow", - @"Panic.messages.shr_overflow", - @"Panic.messages.divide_by_zero", - @"Panic.messages.exact_division_remainder", - @"Panic.messages.integer_part_out_of_bounds", - @"Panic.messages.corrupt_switch", - @"Panic.messages.shift_rhs_too_big", - @"Panic.messages.invalid_enum_value", - @"Panic.messages.for_len_mismatch", - @"Panic.messages.memcpy_len_mismatch", - @"Panic.messages.memcpy_alias", - @"Panic.messages.noreturn_returned", + panic, + @"panic.call", + @"panic.sentinelMismatch", + @"panic.unwrapError", + @"panic.outOfBounds", + @"panic.startGreaterThanEnd", + @"panic.inactiveUnionField", + @"panic.reachedUnreachable", + @"panic.unwrapNull", + @"panic.castToNull", + @"panic.incorrectAlignment", + @"panic.invalidErrorCode", + @"panic.castTruncatedData", + @"panic.negativeToUnsigned", + @"panic.integerOverflow", + @"panic.shlOverflow", + @"panic.shrOverflow", + @"panic.divideByZero", + @"panic.exactDivisionRemainder", + @"panic.integerPartOutOfBounds", + @"panic.corruptSwitch", + @"panic.shiftRhsTooBig", + @"panic.invalidEnumValue", + @"panic.forLenMismatch", + @"panic.memcpyLenMismatch", + @"panic.memcpyAlias", + @"panic.noreturnReturned", VaList, @@ -345,39 +344,35 @@ pub const BuiltinDecl = enum { .@"Type.Declaration", => .type, - .Panic => .type, + .panic => .type, - .@"Panic.call", - .@"Panic.sentinelMismatch", - .@"Panic.unwrapError", - .@"Panic.outOfBounds", - .@"Panic.startGreaterThanEnd", - .@"Panic.inactiveUnionField", + .@"panic.call", + .@"panic.sentinelMismatch", + .@"panic.unwrapError", + .@"panic.outOfBounds", + .@"panic.startGreaterThanEnd", + .@"panic.inactiveUnionField", + .@"panic.reachedUnreachable", + .@"panic.unwrapNull", + .@"panic.castToNull", + .@"panic.incorrectAlignment", + .@"panic.invalidErrorCode", + .@"panic.castTruncatedData", + .@"panic.negativeToUnsigned", + .@"panic.integerOverflow", + .@"panic.shlOverflow", + .@"panic.shrOverflow", + .@"panic.divideByZero", + .@"panic.exactDivisionRemainder", + .@"panic.integerPartOutOfBounds", + .@"panic.corruptSwitch", + .@"panic.shiftRhsTooBig", + .@"panic.invalidEnumValue", + .@"panic.forLenMismatch", + .@"panic.memcpyLenMismatch", + .@"panic.memcpyAlias", + .@"panic.noreturnReturned", => .func, - - .@"Panic.messages" => .type, - - .@"Panic.messages.reached_unreachable", - .@"Panic.messages.unwrap_null", - .@"Panic.messages.cast_to_null", - .@"Panic.messages.incorrect_alignment", - .@"Panic.messages.invalid_error_code", - .@"Panic.messages.cast_truncated_data", - .@"Panic.messages.negative_to_unsigned", - .@"Panic.messages.integer_overflow", - .@"Panic.messages.shl_overflow", - .@"Panic.messages.shr_overflow", - .@"Panic.messages.divide_by_zero", - .@"Panic.messages.exact_division_remainder", - .@"Panic.messages.integer_part_out_of_bounds", - .@"Panic.messages.corrupt_switch", - .@"Panic.messages.shift_rhs_too_big", - .@"Panic.messages.invalid_enum_value", - .@"Panic.messages.for_len_mismatch", - .@"Panic.messages.memcpy_len_mismatch", - .@"Panic.messages.memcpy_alias", - .@"Panic.messages.noreturn_returned", - => .string, }; } @@ -423,7 +418,7 @@ pub const BuiltinDecl = enum { const Memoized = std.enums.EnumArray(BuiltinDecl, InternPool.Index); }; -pub const PanicId = enum { +pub const SimplePanicId = enum { reached_unreachable, unwrap_null, cast_to_null, @@ -445,19 +440,31 @@ pub const PanicId = enum { memcpy_alias, noreturn_returned, - pub fn toBuiltin(id: PanicId) BuiltinDecl { - const first_msg: PanicId = @enumFromInt(0); - const first_decl = @field(BuiltinDecl, "Panic.messages." ++ @tagName(first_msg)); - comptime { - // Ensure that the messages are ordered the same in `BuiltinDecl` as they are here. - for (@typeInfo(PanicId).@"enum".fields) |panic_field| { - const expect_name = "Panic.messages." ++ panic_field.name; - const expect_idx = @intFromEnum(first_decl) + panic_field.value; - const actual_idx = @intFromEnum(@field(BuiltinDecl, expect_name)); - assert(expect_idx == actual_idx); - } - } - return @enumFromInt(@intFromEnum(first_decl) + @intFromEnum(id)); + pub fn toBuiltin(id: SimplePanicId) BuiltinDecl { + return switch (id) { + // zig fmt: off + .reached_unreachable => .@"panic.reachedUnreachable", + .unwrap_null => .@"panic.unwrapNull", + .cast_to_null => .@"panic.castToNull", + .incorrect_alignment => .@"panic.incorrectAlignment", + .invalid_error_code => .@"panic.invalidErrorCode", + .cast_truncated_data => .@"panic.castTruncatedData", + .negative_to_unsigned => .@"panic.negativeToUnsigned", + .integer_overflow => .@"panic.integerOverflow", + .shl_overflow => .@"panic.shlOverflow", + .shr_overflow => .@"panic.shrOverflow", + .divide_by_zero => .@"panic.divideByZero", + .exact_division_remainder => .@"panic.exactDivisionRemainder", + .integer_part_out_of_bounds => .@"panic.integerPartOutOfBounds", + .corrupt_switch => .@"panic.corruptSwitch", + .shift_rhs_too_big => .@"panic.shiftRhsTooBig", + .invalid_enum_value => .@"panic.invalidEnumValue", + .for_len_mismatch => .@"panic.forLenMismatch", + .memcpy_len_mismatch => .@"panic.memcpyLenMismatch", + .memcpy_alias => .@"panic.memcpyAlias", + .noreturn_returned => .@"panic.noreturnReturned", + // zig fmt: on + }; } }; diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index c6a16ee00c..0529a7fb2a 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -605,7 +605,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized // We use an arbitrary element to check if the state has been resolved yet. const to_check: Zcu.BuiltinDecl = switch (stage) { .main => .Type, - .panic => .Panic, + .panic => .panic, .va_list => .VaList, }; if (zcu.builtin_decl_values.get(to_check) != .none) return; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cf5f75b880..17d93ebcbf 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5019,18 +5019,6 @@ pub const FuncGen = struct { ); } - fn resolveNullOptUsize(self: *FuncGen) Error!Builder.Constant { - const o = self.ng.object; - const pt = o.pt; - if (o.null_opt_usize == .no_init) { - o.null_opt_usize = try self.resolveValue(Value.fromInterned(try pt.intern(.{ .opt = .{ - .ty = try pt.intern(.{ .opt_type = .usize_type }), - .val = .none, - } }))); - } - return o.null_opt_usize; - } - fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.CoveragePoint) Error!void { const o = self.ng.object; const zcu = o.pt.zcu; @@ -5732,30 +5720,14 @@ pub const FuncGen = struct { } } - fn buildSimplePanic(fg: *FuncGen, panic_id: Zcu.PanicId) !void { + fn buildSimplePanic(fg: *FuncGen, panic_id: Zcu.SimplePanicId) !void { const o = fg.ng.object; const zcu = o.pt.zcu; - const ip = &zcu.intern_pool; - const msg_len: u64, const msg_ptr: Builder.Constant = msg: { - const str_val = zcu.builtin_decl_values.get(panic_id.toBuiltin()); - assert(str_val != .none); - const slice = ip.indexToKey(str_val).slice; - break :msg .{ Value.fromInterned(slice.len).toUnsignedInt(zcu), try o.lowerValue(slice.ptr) }; - }; - const null_opt_addr_global = try fg.resolveNullOptUsize(); const target = zcu.getTarget(); - const llvm_usize = try o.lowerType(Type.usize); - // example: - // call fastcc void @test2.panic( - // ptr @builtin.panic_messages.integer_overflow__anon_987, ; msg.ptr - // i64 16, ; msg.len - // ptr null, ; stack trace - // ptr @2, ; addr (null ?usize) - // ) - const panic_func = zcu.funcInfo(zcu.builtin_decl_values.get(.@"Panic.call")); - const panic_nav = ip.getNav(panic_func.owner_nav); - const fn_info = zcu.typeToFunc(Type.fromInterned(panic_nav.typeOf(ip))).?; + const panic_func = zcu.funcInfo(zcu.builtin_decl_values.get(panic_id.toBuiltin())); + const fn_info = zcu.typeToFunc(.fromInterned(panic_func.ty)).?; const panic_global = try o.resolveLlvmFunction(panic_func.owner_nav); + const has_err_trace = zcu.comp.config.any_error_tracing and fn_info.cc == .auto; if (has_err_trace) assert(fg.err_ret_trace != .none); _ = try fg.wip.callIntrinsicAssumeCold(); @@ -5765,18 +5737,7 @@ pub const FuncGen = struct { .none, panic_global.typeOf(&o.builder), panic_global.toValue(&o.builder), - if (has_err_trace) &.{ - fg.err_ret_trace, - msg_ptr.toValue(), - try o.builder.intValue(llvm_usize, msg_len), - try o.builder.nullValue(.ptr), - null_opt_addr_global.toValue(), - } else &.{ - msg_ptr.toValue(), - try o.builder.intValue(llvm_usize, msg_len), - try o.builder.nullValue(.ptr), - null_opt_addr_global.toValue(), - }, + if (has_err_trace) &.{fg.err_ret_trace} else &.{}, "", ); _ = try fg.wip.@"unreachable"(); diff --git a/src/crash_report.zig b/src/crash_report.zig index 48a7fd5c39..26096574d3 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -18,18 +18,12 @@ 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) 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 +pub const panic = if (build_options.enable_debug_extensions) + std.debug.FullPanic(compilerPanic) +else if (dev.env == .bootstrap) + std.debug.simple_panic else - std.debug.FormattedPanic; + std.debug.FullPanic(std.debug.defaultPanic); /// Install signal handlers to identify crashes and report diagnostics. pub fn initialize() void { diff --git a/src/main.zig b/src/main.zig index ae7c2229aa..5d691060c6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -56,7 +56,7 @@ pub const std_options: std.Options = .{ }, }; -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 { diff --git a/test/cases/compile_errors/bad_panic_call_signature.zig b/test/cases/compile_errors/bad_panic_call_signature.zig new file mode 100644 index 0000000000..3536397241 --- /dev/null +++ b/test/cases/compile_errors/bad_panic_call_signature.zig @@ -0,0 +1,46 @@ +const simple_panic = std.debug.simple_panic; +pub const panic = struct { + pub fn call(msg: []const u8, bad1: usize, bad2: void) noreturn { + _ = msg; + _ = bad1; + _ = bad2; + @trap(); + } + pub const sentinelMismatch = simple_panic.sentinelMismatch; + pub const unwrapError = simple_panic.unwrapError; + pub const outOfBounds = simple_panic.outOfBounds; + pub const startGreaterThanEnd = simple_panic.startGreaterThanEnd; + pub const inactiveUnionField = simple_panic.inactiveUnionField; + pub const reachedUnreachable = simple_panic.reachedUnreachable; + pub const unwrapNull = simple_panic.unwrapNull; + pub const castToNull = simple_panic.castToNull; + pub const incorrectAlignment = simple_panic.incorrectAlignment; + pub const invalidErrorCode = simple_panic.invalidErrorCode; + pub const castTruncatedData = simple_panic.castTruncatedData; + pub const negativeToUnsigned = simple_panic.negativeToUnsigned; + pub const integerOverflow = simple_panic.integerOverflow; + pub const shlOverflow = simple_panic.shlOverflow; + pub const shrOverflow = simple_panic.shrOverflow; + pub const divideByZero = simple_panic.divideByZero; + pub const exactDivisionRemainder = simple_panic.exactDivisionRemainder; + pub const integerPartOutOfBounds = simple_panic.integerPartOutOfBounds; + pub const corruptSwitch = simple_panic.corruptSwitch; + pub const shiftRhsTooBig = simple_panic.shiftRhsTooBig; + pub const invalidEnumValue = simple_panic.invalidEnumValue; + pub const forLenMismatch = simple_panic.forLenMismatch; + pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; + pub const memcpyAlias = simple_panic.memcpyAlias; + pub const noreturnReturned = simple_panic.noreturnReturned; +}; + +export fn foo(a: u8) void { + @setRuntimeSafety(true); + _ = a + 1; // safety check to reference the panic handler +} + +const std = @import("std"); + +// error +// +// :3:9: error: expected type 'fn ([]const u8, ?*builtin.StackTrace, ?usize) noreturn', found 'fn ([]const u8, usize, void) noreturn' +// :3:9: note: parameter 1 'usize' cannot cast into '?*builtin.StackTrace' diff --git a/test/cases/compile_errors/bad_panic_generic_signature.zig b/test/cases/compile_errors/bad_panic_generic_signature.zig new file mode 100644 index 0000000000..92fa49c7f3 --- /dev/null +++ b/test/cases/compile_errors/bad_panic_generic_signature.zig @@ -0,0 +1,41 @@ +const simple_panic = std.debug.simple_panic; +pub const panic = struct { + pub fn sentinelMismatch() void {} // invalid + pub const call = simple_panic.call; + pub const unwrapError = simple_panic.unwrapError; + pub const outOfBounds = simple_panic.outOfBounds; + pub const startGreaterThanEnd = simple_panic.startGreaterThanEnd; + pub const inactiveUnionField = simple_panic.inactiveUnionField; + pub const reachedUnreachable = simple_panic.reachedUnreachable; + pub const unwrapNull = simple_panic.unwrapNull; + pub const castToNull = simple_panic.castToNull; + pub const incorrectAlignment = simple_panic.incorrectAlignment; + pub const invalidErrorCode = simple_panic.invalidErrorCode; + pub const castTruncatedData = simple_panic.castTruncatedData; + pub const negativeToUnsigned = simple_panic.negativeToUnsigned; + pub const integerOverflow = simple_panic.integerOverflow; + pub const shlOverflow = simple_panic.shlOverflow; + pub const shrOverflow = simple_panic.shrOverflow; + pub const divideByZero = simple_panic.divideByZero; + pub const exactDivisionRemainder = simple_panic.exactDivisionRemainder; + pub const integerPartOutOfBounds = simple_panic.integerPartOutOfBounds; + pub const corruptSwitch = simple_panic.corruptSwitch; + pub const shiftRhsTooBig = simple_panic.shiftRhsTooBig; + pub const invalidEnumValue = simple_panic.invalidEnumValue; + pub const forLenMismatch = simple_panic.forLenMismatch; + pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; + pub const memcpyAlias = simple_panic.memcpyAlias; + pub const noreturnReturned = simple_panic.noreturnReturned; +}; + +export fn foo(arr: *const [2]u8) void { + @setRuntimeSafety(true); + _ = arr[0..1 :0]; +} + +const std = @import("std"); + +// error +// +// :3:9: error: expected type 'fn (anytype, anytype) noreturn', found 'fn () void' +// :3:9: note: non-generic function cannot cast into a generic function diff --git a/test/cases/compile_errors/bad_panic_signature.zig b/test/cases/compile_errors/bad_panic_signature.zig deleted file mode 100644 index f9a30ef1d7..0000000000 --- a/test/cases/compile_errors/bad_panic_signature.zig +++ /dev/null @@ -1,28 +0,0 @@ -pub const Panic = struct { - pub const call = badPanicSignature; - 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; -}; - -fn badPanicSignature(msg: []const u8, bad1: usize, bad2: void) noreturn { - _ = msg; - _ = bad1; - _ = bad2; - @trap(); -} - -export fn foo(a: u8) void { - @setRuntimeSafety(true); - _ = a + 1; // safety check to reference the panic handler -} - -const std = @import("std"); - -// error -// -// :2:9: error: expected type 'fn ([]const u8, ?*builtin.StackTrace, ?usize) noreturn', found 'fn ([]const u8, usize, void) noreturn' -// :2:9: note: parameter 1 'usize' cannot cast into '?*builtin.StackTrace' diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler index bab6eab793..3e0674a4d9 100644 --- a/test/incremental/change_panic_handler +++ b/test/incremental/change_panic_handler @@ -9,15 +9,7 @@ pub fn main() !u8 { _ = a + 1; return 1; } -pub const Panic = struct { - pub const call = myPanic; - 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; -}; +pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); @@ -33,15 +25,7 @@ pub fn main() !u8 { _ = a + 1; return 1; } -pub const Panic = struct { - pub const call = myPanic; - 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; -}; +pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); @@ -57,15 +41,7 @@ pub fn main() !u8 { _ = a + 1; return 1; } -pub const Panic = struct { - pub const call = myPanicNew; - 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; -}; +pub const panic = std.debug.FullPanic(myPanicNew); fn myPanicNew(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit new file mode 100644 index 0000000000..12258d7ce2 --- /dev/null +++ b/test/incremental/change_panic_handler_explicit @@ -0,0 +1,141 @@ +#target=x86_64-linux-selfhosted +#target=x86_64-linux-cbe +#target=x86_64-windows-cbe +#update=initial version +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +const no_panic = std.debug.no_panic; +pub const panic = struct { + pub const call = myPanic; + pub fn integerOverflow() noreturn { + @panic("integer overflow"); + } + pub const sentinelMismatch = no_panic.sentinelMismatch; + pub const unwrapError = no_panic.unwrapError; + pub const outOfBounds = no_panic.outOfBounds; + pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; + pub const inactiveUnionField = no_panic.inactiveUnionField; + pub const reachedUnreachable = no_panic.reachedUnreachable; + pub const unwrapNull = no_panic.unwrapNull; + pub const castToNull = no_panic.castToNull; + pub const incorrectAlignment = no_panic.incorrectAlignment; + pub const invalidErrorCode = no_panic.invalidErrorCode; + pub const castTruncatedData = no_panic.castTruncatedData; + pub const negativeToUnsigned = no_panic.negativeToUnsigned; + pub const shlOverflow = no_panic.shlOverflow; + pub const shrOverflow = no_panic.shrOverflow; + pub const divideByZero = no_panic.divideByZero; + pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; + pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; + pub const corruptSwitch = no_panic.corruptSwitch; + pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; + pub const invalidEnumValue = no_panic.invalidEnumValue; + pub const forLenMismatch = no_panic.forLenMismatch; + pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; + pub const memcpyAlias = no_panic.memcpyAlias; + pub const noreturnReturned = no_panic.noreturnReturned; +}; +fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="panic message: integer overflow\n" + +#update=change the panic handler body +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +const no_panic = std.debug.no_panic; +pub const panic = struct { + pub const call = myPanic; + pub fn integerOverflow() noreturn { + @panic("integer overflow"); + } + pub const sentinelMismatch = no_panic.sentinelMismatch; + pub const unwrapError = no_panic.unwrapError; + pub const outOfBounds = no_panic.outOfBounds; + pub const startGreaterThanEnd = no_panic.startGreaterThanEnd; + pub const inactiveUnionField = no_panic.inactiveUnionField; + pub const reachedUnreachable = no_panic.reachedUnreachable; + pub const unwrapNull = no_panic.unwrapNull; + pub const castToNull = no_panic.castToNull; + pub const incorrectAlignment = no_panic.incorrectAlignment; + pub const invalidErrorCode = no_panic.invalidErrorCode; + pub const castTruncatedData = no_panic.castTruncatedData; + pub const negativeToUnsigned = no_panic.negativeToUnsigned; + pub const shlOverflow = no_panic.shlOverflow; + pub const shrOverflow = no_panic.shrOverflow; + pub const divideByZero = no_panic.divideByZero; + pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; + pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; + pub const corruptSwitch = no_panic.corruptSwitch; + pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; + pub const invalidEnumValue = no_panic.invalidEnumValue; + pub const forLenMismatch = no_panic.forLenMismatch; + pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; + pub const memcpyAlias = no_panic.memcpyAlias; + pub const noreturnReturned = no_panic.noreturnReturned; +}; +fn myPanic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("new panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="new panic message: integer overflow\n" + +#update=change the panic handler function value +#file=main.zig +pub fn main() !u8 { + var a: u8 = undefined; + a = 255; + _ = a + 1; + return 1; +} +const no_panic = std.debug.no_panic; +pub const panic = struct { + pub const call = myPanicNew; + pub fn integerOverflow() noreturn { + @panic("integer overflow"); + } + pub const sentinelMismatch = std.debug.no_panic.sentinelMismatch; + pub const unwrapError = std.debug.no_panic.unwrapError; + pub const outOfBounds = std.debug.no_panic.outOfBounds; + pub const startGreaterThanEnd = std.debug.no_panic.startGreaterThanEnd; + pub const inactiveUnionField = std.debug.no_panic.inactiveUnionField; + pub const messages = std.debug.no_panic.messages; + pub const reachedUnreachable = no_panic.reachedUnreachable; + pub const unwrapNull = no_panic.unwrapNull; + pub const castToNull = no_panic.castToNull; + pub const incorrectAlignment = no_panic.incorrectAlignment; + pub const invalidErrorCode = no_panic.invalidErrorCode; + pub const castTruncatedData = no_panic.castTruncatedData; + pub const negativeToUnsigned = no_panic.negativeToUnsigned; + pub const shlOverflow = no_panic.shlOverflow; + pub const shrOverflow = no_panic.shrOverflow; + pub const divideByZero = no_panic.divideByZero; + pub const exactDivisionRemainder = no_panic.exactDivisionRemainder; + pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds; + pub const corruptSwitch = no_panic.corruptSwitch; + pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; + pub const invalidEnumValue = no_panic.invalidEnumValue; + pub const forLenMismatch = no_panic.forLenMismatch; + pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; + pub const memcpyAlias = no_panic.memcpyAlias; + pub const noreturnReturned = no_panic.noreturnReturned; +}; +fn myPanicNew(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.io.getStdOut().writer().print("third panic message: {s}\n", .{msg}) catch {}; + std.process.exit(0); +} +const std = @import("std"); +#expect_stdout="third panic message: integer overflow\n"