authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-09-26 14:24:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-09-26 16:06:05-07:00
logc9c080a187ae1839a5531d3d95c1080f38721229
tree7d3ded29e2afa8244f34a31b70a5a1f79c0caf7d
parentfcfbedc2f06ba5700092a2cb444261133944be01

embrace panic helpers

Introduces `std.builtin.Panic` which is a complete interface for panicking. Provide `std.debug.FormattedPanic` and `std.debug.SimplePanic` and let the user choose, or make their own.

8 files changed, 309 insertions(+), 401 deletions(-)

lib/compiler_rt/common.zig+7-4
...@@ -75,11 +75,14 @@ pub const gnu_f16_abi = switch (builtin.cpu.arch) {...@@ -75,11 +75,14 @@ pub const gnu_f16_abi = switch (builtin.cpu.arch) {
7575
76pub const want_sparc_abi = builtin.cpu.arch.isSPARC();76pub const want_sparc_abi = builtin.cpu.arch.isSPARC();
7777
78// Avoid dragging in the runtime safety mechanisms into this .o file,78// Avoid dragging in the runtime safety mechanisms into this .o file, unless
79// unless we're trying to test compiler-rt.79// we're trying to test compiler-rt.
80pub fn panic(cause: std.builtin.PanicCause, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {80pub const Panic = if (builtin.is_test) std.debug.FormattedPanic else struct {};
81
82/// To be deleted after zig1.wasm is updated.
83pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
81 if (builtin.is_test) {84 if (builtin.is_test) {
82 std.debug.defaultPanic(cause, error_return_trace, ret_addr orelse @returnAddress());85 std.debug.defaultPanic(msg, error_return_trace, ret_addr orelse @returnAddress());
83 } else {86 } else {
84 unreachable;87 unreachable;
85 }88 }
lib/std/builtin.zig+25-141
...@@ -761,11 +761,10 @@ pub const TestFn = struct {...@@ -761,11 +761,10 @@ pub const TestFn = struct {
761 func: *const fn () anyerror!void,761 func: *const fn () anyerror!void,
762};762};
763763
764/// This function type is used by the Zig language code generation and764/// Deprecated, use the `Panic` namespace instead.
765/// therefore must be kept in sync with the compiler implementation.765pub const PanicFn = fn ([]const u8, ?*StackTrace, ?usize) noreturn;
766pub const PanicFn = fn (PanicCause, ?*StackTrace, ?usize) noreturn;
767766
768/// The entry point for auto-generated calls by the compiler.767/// Deprecated, use the `Panic` namespace instead.
769pub const panic: PanicFn = if (@hasDecl(root, "panic"))768pub const panic: PanicFn = if (@hasDecl(root, "panic"))
770 root.panic769 root.panic
771else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))770else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))
...@@ -773,143 +772,28 @@ else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))...@@ -773,143 +772,28 @@ else if (@hasDecl(root, "os") and @hasDecl(root.os, "panic"))
773else772else
774 std.debug.defaultPanic;773 std.debug.defaultPanic;
775774
776/// This data structure is used by the Zig language code generation and775/// This namespace is used by the Zig compiler to emit various kinds of safety
777/// therefore must be kept in sync with the compiler implementation.776/// panics. These can be overridden by making a public `Panic` namespace in the
778pub const PanicCause = union(enum) {777/// root source file.
779 reached_unreachable,778pub const Panic: type = if (@hasDecl(root, "Panic"))
780 unwrap_null,779 root.Panic
781 cast_to_null,780else if (std.builtin.zig_backend == .stage2_riscv64)
782 incorrect_alignment,781 std.debug.SimplePanic // https://github.com/ziglang/zig/issues/21519
783 invalid_error_code,782else
784 cast_truncated_data,783 std.debug.FormattedPanic;
785 negative_to_unsigned,784
786 integer_overflow,785/// To be deleted after zig1.wasm is updated.
787 shl_overflow,786pub const panicSentinelMismatch = Panic.sentinelMismatch;
788 shr_overflow,787/// To be deleted after zig1.wasm is updated.
789 divide_by_zero,788pub const panicUnwrapError = Panic.unwrapError;
790 exact_division_remainder,789/// To be deleted after zig1.wasm is updated.
791 inactive_union_field: InactiveUnionField,790pub const panicOutOfBounds = Panic.outOfBounds;
792 integer_part_out_of_bounds,791/// To be deleted after zig1.wasm is updated.
793 corrupt_switch,792pub const panicStartGreaterThanEnd = Panic.startGreaterThanEnd;
794 shift_rhs_too_big,793/// To be deleted after zig1.wasm is updated.
795 invalid_enum_value,794pub const panicInactiveUnionField = Panic.inactiveUnionField;
796 sentinel_mismatch_usize: SentinelMismatchUsize,795/// To be deleted after zig1.wasm is updated.
797 sentinel_mismatch_other,796pub const panic_messages = Panic.messages;
798 unwrap_error: anyerror,
799 index_out_of_bounds: IndexOutOfBounds,
800 start_index_greater_than_end: StartIndexGreaterThanEnd,
801 for_len_mismatch,
802 memcpy_len_mismatch,
803 memcpy_alias,
804 noreturn_returned,
805 explicit_call: []const u8,
806 sentinel_mismatch_isize: SentinelMismatchIsize,
807
808 pub const IndexOutOfBounds = struct {
809 index: usize,
810 len: usize,
811 };
812
813 pub const StartIndexGreaterThanEnd = struct {
814 start: usize,
815 end: usize,
816 };
817
818 pub const SentinelMismatchUsize = struct {
819 expected: usize,
820 found: usize,
821 };
822
823 pub const SentinelMismatchIsize = struct {
824 expected: isize,
825 found: isize,
826 };
827
828 pub const InactiveUnionField = struct {
829 active: []const u8,
830 accessed: []const u8,
831 };
832};
833
834pub fn panicSentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
835 @branchHint(.cold);
836 if (builtin.zig_backend == .stage2_riscv64) {
837 // https://github.com/ziglang/zig/issues/21519
838 @trap();
839 }
840 switch (@typeInfo(@TypeOf(expected))) {
841 .int => |int| switch (int.signedness) {
842 .unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
843 .expected = expected,
844 .found = found,
845 } }, null, @returnAddress()),
846 .signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
847 .expected = expected,
848 .found = found,
849 } }, null, @returnAddress()),
850 },
851 .@"enum" => |info| switch (@typeInfo(info.tag_type)) {
852 .int => |int| switch (int.signedness) {
853 .unsigned => if (int.bits <= @bitSizeOf(usize)) panic(.{ .sentinel_mismatch_usize = .{
854 .expected = @intFromEnum(expected),
855 .found = @intFromEnum(found),
856 } }, null, @returnAddress()),
857 .signed => if (int.bits <= @bitSizeOf(isize)) panic(.{ .sentinel_mismatch_isize = .{
858 .expected = @intFromEnum(expected),
859 .found = @intFromEnum(found),
860 } }, null, @returnAddress()),
861 },
862 else => comptime unreachable,
863 },
864 else => {},
865 }
866 panic(.sentinel_mismatch_other, null, @returnAddress());
867}
868
869pub fn panicUnwrapError(ert: ?*StackTrace, err: anyerror) noreturn {
870 @branchHint(.cold);
871 if (builtin.zig_backend == .stage2_riscv64) {
872 // https://github.com/ziglang/zig/issues/21519
873 @trap();
874 }
875 panic(.{ .unwrap_error = err }, ert, @returnAddress());
876}
877
878pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
879 @branchHint(.cold);
880 if (builtin.zig_backend == .stage2_riscv64) {
881 // https://github.com/ziglang/zig/issues/21519
882 @trap();
883 }
884 panic(.{ .index_out_of_bounds = .{
885 .index = index,
886 .len = len,
887 } }, null, @returnAddress());
888}
889
890pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
891 @branchHint(.cold);
892 if (builtin.zig_backend == .stage2_riscv64) {
893 // https://github.com/ziglang/zig/issues/21519
894 @trap();
895 }
896 panic(.{ .start_index_greater_than_end = .{
897 .start = start,
898 .end = end,
899 } }, null, @returnAddress());
900}
901
902pub fn panicInactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
903 @branchHint(.cold);
904 if (builtin.zig_backend == .stage2_riscv64) {
905 // https://github.com/ziglang/zig/issues/21519
906 @trap();
907 }
908 panic(.{ .inactive_union_field = .{
909 .active = @tagName(active),
910 .accessed = @tagName(accessed),
911 } }, null, @returnAddress());
912}
913797
914pub noinline fn returnError(st: *StackTrace) void {798pub noinline fn returnError(st: *StackTrace) void {
915 @branchHint(.unlikely);799 @branchHint(.unlikely);
lib/std/debug.zig+29-152
...@@ -21,6 +21,9 @@ pub const SelfInfo = @import("debug/SelfInfo.zig");...@@ -21,6 +21,9 @@ pub const SelfInfo = @import("debug/SelfInfo.zig");
21pub const Info = @import("debug/Info.zig");21pub const Info = @import("debug/Info.zig");
22pub const Coverage = @import("debug/Coverage.zig");22pub const Coverage = @import("debug/Coverage.zig");
2323
24pub const FormattedPanic = @import("debug/FormattedPanic.zig");
25pub const SimplePanic = @import("debug/SimplePanic.zig");
26
24/// Unresolved source locations can be represented with a single `usize` that27/// Unresolved source locations can be represented with a single `usize` that
25/// corresponds to a virtual memory address of the program counter. Combined28/// corresponds to a virtual memory address of the program counter. Combined
26/// with debug information, those values can be converted into a resolved29/// with debug information, those values can be converted into a resolved
...@@ -408,10 +411,16 @@ pub fn assertReadable(slice: []const volatile u8) void {...@@ -408,10 +411,16 @@ pub fn assertReadable(slice: []const volatile u8) void {
408 for (slice) |*byte| _ = byte.*;411 for (slice) |*byte| _ = byte.*;
409}412}
410413
414/// By including a call to this function, the caller gains an error return trace
415/// secret parameter, making `@errorReturnTrace()` more useful. This is not
416/// necessary if the function already contains a call to an errorable function
417/// elsewhere.
418pub fn errorReturnTraceHelper() anyerror!void {}
419
411/// Equivalent to `@panic` but with a formatted message.420/// Equivalent to `@panic` but with a formatted message.
412pub fn panic(comptime format: []const u8, args: anytype) noreturn {421pub fn panic(comptime format: []const u8, args: anytype) noreturn {
413 @branchHint(.cold);422 @branchHint(.cold);
414423 errorReturnTraceHelper() catch unreachable;
415 panicExtra(@errorReturnTrace(), @returnAddress(), format, args);424 panicExtra(@errorReturnTrace(), @returnAddress(), format, args);
416}425}
417426
...@@ -437,7 +446,7 @@ pub fn panicExtra(...@@ -437,7 +446,7 @@ pub fn panicExtra(
437 break :blk &buf;446 break :blk &buf;
438 },447 },
439 };448 };
440 std.builtin.panic(.{ .explicit_call = msg }, trace, ret_addr);449 std.builtin.Panic.call(msg, trace, ret_addr);
441}450}
442451
443/// Non-zero whenever the program triggered a panic.452/// Non-zero whenever the program triggered a panic.
...@@ -448,11 +457,9 @@ var panicking = std.atomic.Value(u8).init(0);...@@ -448,11 +457,9 @@ var panicking = std.atomic.Value(u8).init(0);
448/// This is used to catch and handle panics triggered by the panic handler.457/// This is used to catch and handle panics triggered by the panic handler.
449threadlocal var panic_stage: usize = 0;458threadlocal var panic_stage: usize = 0;
450459
451// Dumps a stack trace to standard error, then aborts.460/// Dumps a stack trace to standard error, then aborts.
452//
453// This function avoids a dependency on formatted printing.
454pub fn defaultPanic(461pub fn defaultPanic(
455 cause: std.builtin.PanicCause,462 msg: []const u8,
456 error_return_trace: ?*const std.builtin.StackTrace,463 error_return_trace: ?*const std.builtin.StackTrace,
457 first_trace_addr: ?usize,464 first_trace_addr: ?usize,
458) noreturn {465) noreturn {
...@@ -471,18 +478,6 @@ pub fn defaultPanic(...@@ -471,18 +478,6 @@ pub fn defaultPanic(
471 @trap();478 @trap();
472 }479 }
473480
474 if (builtin.zig_backend == .stage2_riscv64) {
475 var buffer: [1000]u8 = undefined;
476 var i: usize = 0;
477 i += fmtPanicCause(buffer[i..], cause);
478 buffer[i] = '\n';
479 i += 1;
480 const msg = buffer[0..i];
481 lockStdErr();
482 io.getStdErr().writeAll(msg) catch {};
483 @trap();
484 }
485
486 switch (builtin.os.tag) {481 switch (builtin.os.tag) {
487 .freestanding => {482 .freestanding => {
488 @trap();483 @trap();
...@@ -490,14 +485,10 @@ pub fn defaultPanic(...@@ -490,14 +485,10 @@ pub fn defaultPanic(
490 .uefi => {485 .uefi => {
491 const uefi = std.os.uefi;486 const uefi = std.os.uefi;
492487
493 var buffer: [1000]u8 = undefined;
494 var i: usize = 0;
495 i += fmtBuf(buffer[i..], "panic: ");
496 i += fmtPanicCause(buffer[i..], cause);
497 i += fmtBuf(buffer[i..], "\r\n\x00");
498
499 var utf16_buffer: [1000]u16 = undefined;488 var utf16_buffer: [1000]u16 = undefined;
500 const len = std.unicode.utf8ToUtf16Le(&utf16_buffer, buffer[0..i]) catch 0;489 const len_minus_3 = std.unicode.utf8ToUtf16Le(&utf16_buffer, msg) catch 0;
490 utf16_buffer[len_minus_3][0..3].* = .{ '\r', '\n', 0 };
491 const len = len_minus_3 + 3;
501 const exit_msg = utf16_buffer[0 .. len - 1 :0];492 const exit_msg = utf16_buffer[0 .. len - 1 :0];
502493
503 // Output to both std_err and con_out, as std_err is easier494 // Output to both std_err and con_out, as std_err is easier
...@@ -521,15 +512,11 @@ pub fn defaultPanic(...@@ -521,15 +512,11 @@ pub fn defaultPanic(
521 },512 },
522 .cuda, .amdhsa => std.posix.abort(),513 .cuda, .amdhsa => std.posix.abort(),
523 .plan9 => {514 .plan9 => {
524 var buffer: [1000]u8 = undefined;515 var status: [std.os.plan9.ERRMAX]u8 = undefined;
525 comptime assert(buffer.len > std.os.plan9.ERRMAX);516 const len = @min(msg.len, status.len - 1);
526 var i: usize = 0;517 @memcpy(status[0..len], msg[0..len]);
527 i += fmtPanicCause(buffer[i..], cause);518 status[len] = 0;
528 buffer[i] = '\n';519 std.os.plan9.exits(status[0..len :0]);
529 i += 1;
530 const len = @min(i, std.os.plan9.ERRMAX - 1);
531 buffer[len] = 0;
532 std.os.plan9.exits(buffer[0..len :0]);
533 },520 },
534 else => {},521 else => {},
535 }522 }
...@@ -548,26 +535,18 @@ pub fn defaultPanic(...@@ -548,26 +535,18 @@ pub fn defaultPanic(
548 _ = panicking.fetchAdd(1, .seq_cst);535 _ = panicking.fetchAdd(1, .seq_cst);
549536
550 {537 {
551 // This code avoids a dependency on formatted printing, the writer interface,538 lockStdErr();
552 // and limits to only 1 syscall made to print the panic message to stderr.539 defer unlockStdErr();
553 var buffer: [0x1000]u8 = undefined;540
554 var i: usize = 0;541 const stderr = io.getStdErr().writer();
555 if (builtin.single_threaded) {542 if (builtin.single_threaded) {
556 i += fmtBuf(buffer[i..], "panic: ");543 stderr.print("panic: ", .{}) catch posix.abort();
557 } else {544 } else {
558 i += fmtBuf(buffer[i..], "thread ");545 const current_thread_id = std.Thread.getCurrentId();
559 i += fmtInt10(buffer[i..], std.Thread.getCurrentId());546 stderr.print("thread {} panic: ", .{current_thread_id}) catch posix.abort();
560 i += fmtBuf(buffer[i..], " panic: ");
561 }547 }
562 i += fmtPanicCause(buffer[i..], cause);548 stderr.print("{s}\n", .{msg}) catch posix.abort();
563 buffer[i] = '\n';
564 i += 1;
565 const msg = buffer[0..i];
566549
567 lockStdErr();
568 defer unlockStdErr();
569
570 io.getStdErr().writeAll(msg) catch posix.abort();
571 if (error_return_trace) |t| dumpStackTrace(t.*);550 if (error_return_trace) |t| dumpStackTrace(t.*);
572 dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());551 dumpCurrentStackTrace(first_trace_addr orelse @returnAddress());
573 }552 }
...@@ -588,108 +567,6 @@ pub fn defaultPanic(...@@ -588,108 +567,6 @@ pub fn defaultPanic(
588 posix.abort();567 posix.abort();
589}568}
590569
591pub fn fmtPanicCause(buffer: []u8, cause: std.builtin.PanicCause) usize {
592 var i: usize = 0;
593
594 switch (cause) {
595 .reached_unreachable => i += fmtBuf(buffer[i..], "reached unreachable code"),
596 .unwrap_null => i += fmtBuf(buffer[i..], "attempt to use null value"),
597 .cast_to_null => i += fmtBuf(buffer[i..], "cast causes pointer to be null"),
598 .incorrect_alignment => i += fmtBuf(buffer[i..], "incorrect alignment"),
599 .invalid_error_code => i += fmtBuf(buffer[i..], "invalid error code"),
600 .cast_truncated_data => i += fmtBuf(buffer[i..], "integer cast truncated bits"),
601 .negative_to_unsigned => i += fmtBuf(buffer[i..], "attempt to cast negative value to unsigned integer"),
602 .integer_overflow => i += fmtBuf(buffer[i..], "integer overflow"),
603 .shl_overflow => i += fmtBuf(buffer[i..], "left shift overflowed bits"),
604 .shr_overflow => i += fmtBuf(buffer[i..], "right shift overflowed bits"),
605 .divide_by_zero => i += fmtBuf(buffer[i..], "division by zero"),
606 .exact_division_remainder => i += fmtBuf(buffer[i..], "exact division produced remainder"),
607 .inactive_union_field => |info| {
608 i += fmtBuf(buffer[i..], "access of union field '");
609 i += fmtBuf(buffer[i..], info.accessed);
610 i += fmtBuf(buffer[i..], "' while field '");
611 i += fmtBuf(buffer[i..], info.active);
612 i += fmtBuf(buffer[i..], "' is active");
613 },
614 .integer_part_out_of_bounds => i += fmtBuf(buffer[i..], "integer part of floating point value out of bounds"),
615 .corrupt_switch => i += fmtBuf(buffer[i..], "switch on corrupt value"),
616 .shift_rhs_too_big => i += fmtBuf(buffer[i..], "shift amount is greater than the type size"),
617 .invalid_enum_value => i += fmtBuf(buffer[i..], "invalid enum value"),
618 .sentinel_mismatch_usize => |mm| {
619 i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
620 i += fmtInt10(buffer[i..], mm.expected);
621 i += fmtBuf(buffer[i..], ", found ");
622 i += fmtInt10(buffer[i..], mm.found);
623 },
624 .sentinel_mismatch_isize => |mm| {
625 i += fmtBuf(buffer[i..], "sentinel mismatch: expected ");
626 i += fmtInt10s(buffer[i..], mm.expected);
627 i += fmtBuf(buffer[i..], ", found ");
628 i += fmtInt10s(buffer[i..], mm.found);
629 },
630 .sentinel_mismatch_other => i += fmtBuf(buffer[i..], "sentinel mismatch"),
631 .unwrap_error => |err| {
632 if (builtin.zig_backend == .stage2_riscv64) {
633 // https://github.com/ziglang/zig/issues/21519
634 i += fmtBuf(buffer[i..], "attempt to unwrap error");
635 return i;
636 }
637 i += fmtBuf(buffer[i..], "attempt to unwrap error: ");
638 i += fmtBuf(buffer[i..], @errorName(err));
639 },
640 .index_out_of_bounds => |oob| {
641 i += fmtBuf(buffer[i..], "index ");
642 i += fmtInt10(buffer[i..], oob.index);
643 i += fmtBuf(buffer[i..], " exceeds length ");
644 i += fmtInt10(buffer[i..], oob.len);
645 },
646 .start_index_greater_than_end => |oob| {
647 i += fmtBuf(buffer[i..], "start index ");
648 i += fmtInt10(buffer[i..], oob.start);
649 i += fmtBuf(buffer[i..], " exceeds end index ");
650 i += fmtInt10(buffer[i..], oob.end);
651 },
652 .for_len_mismatch => i += fmtBuf(buffer[i..], "for loop over objects with non-equal lengths"),
653 .memcpy_len_mismatch => i += fmtBuf(buffer[i..], "@memcpy arguments have non-equal lengths"),
654 .memcpy_alias => i += fmtBuf(buffer[i..], "@memcpy arguments alias"),
655 .noreturn_returned => i += fmtBuf(buffer[i..], "'noreturn' function returned"),
656 .explicit_call => |msg| i += fmtBuf(buffer[i..], msg),
657 }
658
659 return i;
660}
661
662fn fmtBuf(out_buf: []u8, s: []const u8) usize {
663 @memcpy(out_buf[0..s.len], s);
664 return s.len;
665}
666
667fn fmtInt10s(out_buf: []u8, integer_value: isize) usize {
668 if (integer_value < 0) {
669 out_buf[0] = '-';
670 return 1 + fmtInt10(out_buf[1..], @abs(integer_value));
671 } else {
672 return fmtInt10(out_buf, @abs(integer_value));
673 }
674}
675
676fn fmtInt10(out_buf: []u8, integer_value: usize) usize {
677 var tmp_buf: [50]u8 = undefined;
678 var i: usize = tmp_buf.len;
679 var a: usize = integer_value;
680
681 while (true) {
682 i -= 1;
683 tmp_buf[i] = '0' + @as(u8, @intCast(a % 10));
684 a /= 10;
685 if (a == 0) break;
686 }
687
688 const result = tmp_buf[i..];
689 @memcpy(out_buf[0..result.len], result);
690 return result.len;
691}
692
693/// Must be called only after adding 1 to `panicking`. There are three callsites.570/// Must be called only after adding 1 to `panicking`. There are three callsites.
694fn waitForOtherThreadToFinishPanicking() void {571fn waitForOtherThreadToFinishPanicking() void {
695 if (panicking.fetchSub(1, .seq_cst) != 1) {572 if (panicking.fetchSub(1, .seq_cst) != 1) {
lib/std/debug/FormattedPanic.zig created+45
...@@ -0,0 +1,45 @@
1//! This namespace is the default one used by the Zig compiler to emit various
2//! kinds of safety panics, due to the logic in `std.builtin.Panic`.
3//!
4//! Since Zig does not have interfaces, this file serves as an example template
5//! for users to provide their own alternative panic handling.
6//!
7//! As an alternative, see `std.debug.SimplePanic`.
8
9const std = @import("../std.zig");
10
11/// Dumps a stack trace to standard error, then aborts.
12///
13/// Explicit calls to `@panic` lower to calling this function.
14pub const call: fn ([]const u8, ?*std.builtin.StackTrace, ?usize) noreturn = std.debug.defaultPanic;
15
16pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
17 @branchHint(.cold);
18 std.debug.panicExtra(null, @returnAddress(), "sentinel mismatch: expected {any}, found {any}", .{
19 expected, found,
20 });
21}
22
23pub fn unwrapError(ert: ?*std.builtin.StackTrace, err: anyerror) noreturn {
24 @branchHint(.cold);
25 std.debug.panicExtra(ert, @returnAddress(), "attempt to unwrap error: {s}", .{@errorName(err)});
26}
27
28pub fn outOfBounds(index: usize, len: usize) noreturn {
29 @branchHint(.cold);
30 std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
31}
32
33pub fn startGreaterThanEnd(start: usize, end: usize) noreturn {
34 @branchHint(.cold);
35 std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
36}
37
38pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
39 @branchHint(.cold);
40 std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{
41 @tagName(accessed), @tagName(active),
42 });
43}
44
45pub const messages = std.debug.SimplePanic.messages;
lib/std/debug/SimplePanic.zig created+86
...@@ -0,0 +1,86 @@
1//! This namespace is the default one used by the Zig compiler to emit various
2//! kinds of safety panics, due to the logic in `std.builtin.Panic`.
3//!
4//! Since Zig does not have interfaces, this file serves as an example template
5//! for users to provide their own alternative panic handling.
6//!
7//! As an alternative, see `std.debug.FormattedPanic`.
8
9const std = @import("../std.zig");
10
11/// Prints the message to stderr without a newline and then traps.
12///
13/// Explicit calls to `@panic` lower to calling this function.
14pub fn call(msg: []const u8, ert: ?*std.builtin.StackTrace, ra: ?usize) noreturn {
15 @branchHint(.cold);
16 _ = ert;
17 _ = ra;
18 std.debug.lockStdErr();
19 const stderr = std.io.getStdErr();
20 stderr.writeAll(msg) catch {};
21 @trap();
22}
23
24pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
25 _ = found;
26 call("sentinel mismatch", null, null);
27}
28
29pub fn unwrapError(ert: ?*std.builtin.StackTrace, err: anyerror) noreturn {
30 _ = ert;
31 _ = err;
32 call("attempt to unwrap error", null, null);
33}
34
35pub fn outOfBounds(index: usize, len: usize) noreturn {
36 _ = index;
37 _ = len;
38 call("index out of bounds", null, null);
39}
40
41pub fn startGreaterThanEnd(start: usize, end: usize) noreturn {
42 _ = start;
43 _ = end;
44 call("start index is larger than end index", null, null);
45}
46
47pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
48 _ = accessed;
49 call("access of inactive union field", null, null);
50}
51
52pub const messages = struct {
53 pub const reached_unreachable = "reached unreachable code";
54 pub const unwrap_null = "attempt to use null value";
55 pub const cast_to_null = "cast causes pointer to be null";
56 pub const incorrect_alignment = "incorrect alignment";
57 pub const invalid_error_code = "invalid error code";
58 pub const cast_truncated_data = "integer cast truncated bits";
59 pub const negative_to_unsigned = "attempt to cast negative value to unsigned integer";
60 pub const integer_overflow = "integer overflow";
61 pub const shl_overflow = "left shift overflowed bits";
62 pub const shr_overflow = "right shift overflowed bits";
63 pub const divide_by_zero = "division by zero";
64 pub const exact_division_remainder = "exact division produced remainder";
65 pub const integer_part_out_of_bounds = "integer part of floating point value out of bounds";
66 pub const corrupt_switch = "switch on corrupt value";
67 pub const shift_rhs_too_big = "shift amount is greater than the type size";
68 pub const invalid_enum_value = "invalid enum value";
69 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
70 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
71 pub const memcpy_alias = "@memcpy arguments alias";
72 pub const noreturn_returned = "'noreturn' function returned";
73
74 /// To be deleted after zig1.wasm is updated.
75 pub const inactive_union_field = "access of inactive union field";
76 /// To be deleted after zig1.wasm is updated.
77 pub const sentinel_mismatch = "sentinel mismatch";
78 /// To be deleted after zig1.wasm is updated.
79 pub const unwrap_error = "attempt to unwrap error";
80 /// To be deleted after zig1.wasm is updated.
81 pub const index_out_of_bounds = "index out of bounds";
82 /// To be deleted after zig1.wasm is updated.
83 pub const start_index_greater_than_end = "start index is larger than end index";
84 /// To be deleted after zig1.wasm is updated.
85 pub const unreach = reached_unreachable;
86};
src/Sema.zig+71-72
...@@ -2566,7 +2566,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg...@@ -2566,7 +2566,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg
2566 std.debug.print("compile error during Sema:\n", .{});2566 std.debug.print("compile error during Sema:\n", .{});
2567 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");2567 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
2568 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });2568 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
2569 crash_report.compilerPanic(.{ .explicit_call = "unexpected compile error occurred" }, null, null);2569 crash_report.compilerPanic("unexpected compile error occurred", null, null);
2570 }2570 }
25712571
2572 if (block) |start_block| {2572 if (block) |start_block| {
...@@ -5810,6 +5810,8 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -5810,6 +5810,8 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
5810 const src = block.nodeOffset(inst_data.src_node);5810 const src = block.nodeOffset(inst_data.src_node);
5811 const msg_inst = try sema.resolveInst(inst_data.operand);5811 const msg_inst = try sema.resolveInst(inst_data.operand);
58125812
5813 // `panicWithMsg` would perform this coercion for us, but we can get a better
5814 // source location if we do it here.
5813 const coerced_msg = try sema.coerce(block, Type.slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0));5815 const coerced_msg = try sema.coerce(block, Type.slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0));
58145816
5815 if (block.is_comptime) {5817 if (block.is_comptime) {
...@@ -5822,7 +5824,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -5822,7 +5824,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
5822 sema.branch_hint = .cold;5824 sema.branch_hint = .cold;
5823 }5825 }
58245826
5825 try callPanic(sema, block, src, .explicit_call, coerced_msg, .@"@panic");5827 try sema.panicWithMsg(block, src, coerced_msg, .@"@panic");
5826}5828}
58275829
5828fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {5830fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -7303,33 +7305,6 @@ fn callBuiltin(...@@ -7303,33 +7305,6 @@ fn callBuiltin(
7303 );7305 );
7304}7306}
73057307
7306const PanicCauseTag = @typeInfo(std.builtin.PanicCause).@"union".tag_type.?;
7307
7308fn callPanic(
7309 sema: *Sema,
7310 block: *Block,
7311 call_src: LazySrcLoc,
7312 tag: PanicCauseTag,
7313 payload: Air.Inst.Ref,
7314 call_operation: CallOperation,
7315) !void {
7316 const pt = sema.pt;
7317 const zcu = pt.zcu;
7318 if (!zcu.backendSupportsFeature(.panic_fn)) {
7319 _ = try block.addNoOp(.trap);
7320 return;
7321 }
7322 const panic_cause_ty = try pt.getBuiltinType("PanicCause");
7323 const panic_cause = try unionInitFromEnumTag(sema, block, call_src, panic_cause_ty, @intFromEnum(tag), payload);
7324 try preparePanic(sema, block, call_src);
7325 const panic_fn = Air.internedToRef(zcu.panic_func_index);
7326 const err_return_trace = try sema.getErrorReturnTrace(block);
7327 const opt_usize_ty = try pt.optionalType(.usize_type);
7328 const null_usize = try pt.nullValue(opt_usize_ty);
7329 const args: [3]Air.Inst.Ref = .{ panic_cause, err_return_trace, Air.internedToRef(null_usize.toIntern()) };
7330 try sema.callBuiltin(block, call_src, panic_fn, .auto, &args, call_operation);
7331}
7332
7333const CallOperation = enum {7308const CallOperation = enum {
7334 call,7309 call,
7335 @"@call",7310 @"@call",
...@@ -14233,7 +14208,11 @@ fn maybeErrorUnwrap(...@@ -14233,7 +14208,11 @@ fn maybeErrorUnwrap(
14233 .panic => {14208 .panic => {
14234 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;14209 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
14235 const msg_inst = try sema.resolveInst(inst_data.operand);14210 const msg_inst = try sema.resolveInst(inst_data.operand);
14236 try callPanic(sema, block, operand_src, .explicit_call, msg_inst, .@"@panic");14211
14212 const panic_fn = try pt.getBuiltinInnerType("Panic", "call");
14213 const err_return_trace = try sema.getErrorReturnTrace(block);
14214 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value };
14215 try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check");
14237 return true;14216 return true;
14238 },14217 },
14239 else => unreachable,14218 else => unreachable,
...@@ -17380,7 +17359,9 @@ fn analyzeArithmetic(...@@ -17380,7 +17359,9 @@ fn analyzeArithmetic(
1738017359
17381 if (block.wantSafety() and want_safety and scalar_tag == .int) {17360 if (block.wantSafety() and want_safety and scalar_tag == .int) {
17382 if (zcu.backendSupportsFeature(.safety_checked_instructions)) {17361 if (zcu.backendSupportsFeature(.safety_checked_instructions)) {
17383 if (air_tag != air_tag_safe) try sema.preparePanicIntegerOverflow(block, src);17362 if (air_tag != air_tag_safe) {
17363 _ = try sema.preparePanicId(block, src, .integer_overflow);
17364 }
17384 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);17365 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
17385 } else {17366 } else {
17386 const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {17367 const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {
...@@ -21683,16 +21664,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -21683,16 +21664,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
21683 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);21664 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21684 const src = block.nodeOffset(inst_data.src_node);21665 const src = block.nodeOffset(inst_data.src_node);
21685 const operand = try sema.resolveInst(inst_data.operand);21666 const operand = try sema.resolveInst(inst_data.operand);
21686 return analyzeTagName(sema, block, src, operand_src, operand);
21687}
21688
21689fn analyzeTagName(
21690 sema: *Sema,
21691 block: *Block,
21692 src: LazySrcLoc,
21693 operand_src: LazySrcLoc,
21694 operand: Air.Inst.Ref,
21695) CompileError!Air.Inst.Ref {
21696 const operand_ty = sema.typeOf(operand);21667 const operand_ty = sema.typeOf(operand);
21697 const pt = sema.pt;21668 const pt = sema.pt;
21698 const zcu = pt.zcu;21669 const zcu = pt.zcu;
...@@ -27663,7 +27634,7 @@ fn explainWhyTypeIsNotPacked(...@@ -27663,7 +27634,7 @@ fn explainWhyTypeIsNotPacked(
27663 }27634 }
27664}27635}
2766527636
27666fn preparePanic(sema: *Sema, block: *Block, src: LazySrcLoc) !void {27637fn prepareSimplePanic(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
27667 const pt = sema.pt;27638 const pt = sema.pt;
27668 const zcu = pt.zcu;27639 const zcu = pt.zcu;
2766927640
...@@ -27694,30 +27665,33 @@ fn preparePanic(sema: *Sema, block: *Block, src: LazySrcLoc) !void {...@@ -27694,30 +27665,33 @@ fn preparePanic(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
27694 .val = .none,27665 .val = .none,
27695 } });27666 } });
27696 }27667 }
27697
27698 if (zcu.panic_cause_type == .none) {
27699 const panic_cause_ty = try pt.getBuiltinType("PanicCause");
27700 try panic_cause_ty.resolveFields(pt);
27701 zcu.panic_cause_type = panic_cause_ty.toIntern();
27702 zcu.panic_cause_tag_type = panic_cause_ty.unionTagType(zcu).?.toIntern();
27703 }
27704}27668}
2770527669
27706fn preparePanicIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc) !void {27670/// Backends depend on panic decls being available when lowering safety-checked
27671/// instructions. This function ensures the panic function will be available to
27672/// be called during that time.
27673fn preparePanicId(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) !InternPool.Nav.Index {
27707 const pt = sema.pt;27674 const pt = sema.pt;
27708 const zcu = pt.zcu;27675 const zcu = pt.zcu;
27709 try preparePanic(sema, block, src);27676 const gpa = sema.gpa;
27710 if (zcu.panic_cause_integer_overflow == .none) {27677 if (zcu.panic_messages[@intFromEnum(panic_id)].unwrap()) |x| return x;
27711 const union_val = try pt.unionValue(27678
27712 Type.fromInterned(zcu.panic_cause_type),27679 try sema.prepareSimplePanic(block, src);
27713 try pt.enumValueFieldIndex(27680
27714 Type.fromInterned(zcu.panic_cause_tag_type),27681 const panic_messages_ty = try pt.getBuiltinType("panic_messages");
27715 @intFromEnum(PanicCauseTag.integer_overflow),27682 const msg_nav_index = (sema.namespaceLookup(
27716 ),27683 block,
27717 Value.void,27684 LazySrcLoc.unneeded,
27718 );27685 panic_messages_ty.getNamespaceIndex(zcu),
27719 zcu.panic_cause_integer_overflow = try pt.refValue(union_val.toIntern());27686 try zcu.intern_pool.getOrPutString(gpa, pt.tid, @tagName(panic_id), .no_embedded_nulls),
27720 }27687 ) catch |err| switch (err) {
27688 error.AnalysisFail => @panic("std.builtin.panic_messages is corrupt"),
27689 error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
27690 error.OutOfMemory => |e| return e,
27691 }).?;
27692 try sema.ensureNavResolved(src, msg_nav_index);
27693 zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional();
27694 return msg_nav_index;
27721}27695}
2772227696
27723fn addSafetyCheck(27697fn addSafetyCheck(
...@@ -27725,7 +27699,7 @@ fn addSafetyCheck(...@@ -27725,7 +27699,7 @@ fn addSafetyCheck(
27725 parent_block: *Block,27699 parent_block: *Block,
27726 src: LazySrcLoc,27700 src: LazySrcLoc,
27727 ok: Air.Inst.Ref,27701 ok: Air.Inst.Ref,
27728 panic_cause_tag: PanicCauseTag,27702 panic_id: Zcu.PanicId,
27729) !void {27703) !void {
27730 const gpa = sema.gpa;27704 const gpa = sema.gpa;
27731 assert(!parent_block.is_comptime);27705 assert(!parent_block.is_comptime);
...@@ -27743,7 +27717,7 @@ fn addSafetyCheck(...@@ -27743,7 +27717,7 @@ fn addSafetyCheck(
2774327717
27744 defer fail_block.instructions.deinit(gpa);27718 defer fail_block.instructions.deinit(gpa);
2774527719
27746 try sema.safetyPanic(&fail_block, src, panic_cause_tag);27720 try sema.safetyPanic(&fail_block, src, panic_id);
27747 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);27721 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
27748}27722}
2774927723
...@@ -27812,6 +27786,29 @@ fn addSafetyCheckExtra(...@@ -27812,6 +27786,29 @@ fn addSafetyCheckExtra(
27812 parent_block.instructions.appendAssumeCapacity(block_inst);27786 parent_block.instructions.appendAssumeCapacity(block_inst);
27813}27787}
2781427788
27789fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, operation: CallOperation) !void {
27790 const pt = sema.pt;
27791 const zcu = pt.zcu;
27792
27793 if (!zcu.backendSupportsFeature(.panic_fn)) {
27794 _ = try block.addNoOp(.trap);
27795 return;
27796 }
27797
27798 try sema.prepareSimplePanic(block, src);
27799
27800 const panic_func = zcu.funcInfo(zcu.panic_func_index);
27801 const panic_fn = try sema.analyzeNavVal(block, src, panic_func.owner_nav);
27802 const null_stack_trace = Air.internedToRef(zcu.null_stack_trace);
27803
27804 const opt_usize_ty = try pt.optionalType(.usize_type);
27805 const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
27806 .ty = opt_usize_ty.toIntern(),
27807 .val = .none,
27808 } })));
27809 try sema.callBuiltin(block, src, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }, operation);
27810}
27811
27815fn addSafetyCheckUnwrapError(27812fn addSafetyCheckUnwrapError(
27816 sema: *Sema,27813 sema: *Sema,
27817 parent_block: *Block,27814 parent_block: *Block,
...@@ -27849,7 +27846,7 @@ fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air....@@ -27849,7 +27846,7 @@ fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.
27849 if (!zcu.backendSupportsFeature(.panic_fn)) {27846 if (!zcu.backendSupportsFeature(.panic_fn)) {
27850 _ = try block.addNoOp(.trap);27847 _ = try block.addNoOp(.trap);
27851 } else {27848 } else {
27852 const panic_fn = try pt.getBuiltin("panicUnwrapError");27849 const panic_fn = try pt.getBuiltinInnerType("Panic", "unwrapError");
27853 const err_return_trace = try sema.getErrorReturnTrace(block);27850 const err_return_trace = try sema.getErrorReturnTrace(block);
27854 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };27851 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
27855 try sema.callBuiltin(block, src, panic_fn, .auto, &args, .@"safety check");27852 try sema.callBuiltin(block, src, panic_fn, .auto, &args, .@"safety check");
...@@ -27866,7 +27863,7 @@ fn addSafetyCheckIndexOob(...@@ -27866,7 +27863,7 @@ fn addSafetyCheckIndexOob(
27866) !void {27863) !void {
27867 assert(!parent_block.is_comptime);27864 assert(!parent_block.is_comptime);
27868 const ok = try parent_block.addBinOp(cmp_op, index, len);27865 const ok = try parent_block.addBinOp(cmp_op, index, len);
27869 return addSafetyCheckCall(sema, parent_block, src, ok, "panicOutOfBounds", &.{ index, len });27866 return addSafetyCheckCall(sema, parent_block, src, ok, "outOfBounds", &.{ index, len });
27870}27867}
2787127868
27872fn addSafetyCheckInactiveUnionField(27869fn addSafetyCheckInactiveUnionField(
...@@ -27878,7 +27875,7 @@ fn addSafetyCheckInactiveUnionField(...@@ -27878,7 +27875,7 @@ fn addSafetyCheckInactiveUnionField(
27878) !void {27875) !void {
27879 assert(!parent_block.is_comptime);27876 assert(!parent_block.is_comptime);
27880 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);27877 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
27881 return addSafetyCheckCall(sema, parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });27878 return addSafetyCheckCall(sema, parent_block, src, ok, "inactiveUnionField", &.{ active_tag, wanted_tag });
27882}27879}
2788327880
27884fn addSafetyCheckSentinelMismatch(27881fn addSafetyCheckSentinelMismatch(
...@@ -27919,7 +27916,7 @@ fn addSafetyCheckSentinelMismatch(...@@ -27919,7 +27916,7 @@ fn addSafetyCheckSentinelMismatch(
27919 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);27916 break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
27920 };27917 };
2792127918
27922 return addSafetyCheckCall(sema, parent_block, src, ok, "panicSentinelMismatch", &.{27919 return addSafetyCheckCall(sema, parent_block, src, ok, "sentinelMismatch", &.{
27923 expected_sentinel, actual_sentinel,27920 expected_sentinel, actual_sentinel,
27924 });27921 });
27925}27922}
...@@ -27953,7 +27950,7 @@ fn addSafetyCheckCall(...@@ -27953,7 +27950,7 @@ fn addSafetyCheckCall(
27953 if (!zcu.backendSupportsFeature(.panic_fn)) {27950 if (!zcu.backendSupportsFeature(.panic_fn)) {
27954 _ = try fail_block.addNoOp(.trap);27951 _ = try fail_block.addNoOp(.trap);
27955 } else {27952 } else {
27956 const panic_fn = try pt.getBuiltin(func_name);27953 const panic_fn = try pt.getBuiltinInnerType("Panic", func_name);
27957 try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");27954 try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");
27958 }27955 }
2795927956
...@@ -27961,8 +27958,10 @@ fn addSafetyCheckCall(...@@ -27961,8 +27958,10 @@ fn addSafetyCheckCall(
27961}27958}
2796227959
27963/// This does not set `sema.branch_hint`.27960/// This does not set `sema.branch_hint`.
27964fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_cause_tag: PanicCauseTag) CompileError!void {27961fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.PanicId) CompileError!void {
27965 try callPanic(sema, block, src, panic_cause_tag, .void_value, .@"safety check");27962 const msg_nav_index = try sema.preparePanicId(block, src, panic_id);
27963 const msg_inst = try sema.analyzeNavVal(block, src, msg_nav_index);
27964 try sema.panicWithMsg(block, src, msg_inst, .@"safety check");
27966}27965}
2796727966
27968fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {27967fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
...@@ -33456,7 +33455,7 @@ fn analyzeSlice(...@@ -33456,7 +33455,7 @@ fn analyzeSlice(
33456 assert(!block.is_comptime);33455 assert(!block.is_comptime);
33457 try sema.requireRuntimeBlock(block, src, runtime_src.?);33456 try sema.requireRuntimeBlock(block, src, runtime_src.?);
33458 const ok = try block.addBinOp(.cmp_lte, start, end);33457 const ok = try block.addBinOp(.cmp_lte, start, end);
33459 try sema.addSafetyCheckCall(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end });33458 try sema.addSafetyCheckCall(block, src, ok, "startGreaterThanEnd", &.{ start, end });
33460 }33459 }
33461 const new_len = if (by_length)33460 const new_len = if (by_length)
33462 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)33461 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
src/Zcu.zig+26-3
...@@ -210,17 +210,40 @@ all_type_references: std.ArrayListUnmanaged(TypeReference) = .empty,...@@ -210,17 +210,40 @@ all_type_references: std.ArrayListUnmanaged(TypeReference) = .empty,
210/// Freelist of indices in `all_type_references`.210/// Freelist of indices in `all_type_references`.
211free_type_references: std.ArrayListUnmanaged(u32) = .empty,211free_type_references: std.ArrayListUnmanaged(u32) = .empty,
212212
213panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,
213/// The panic function body.214/// The panic function body.
214panic_func_index: InternPool.Index = .none,215panic_func_index: InternPool.Index = .none,
215null_stack_trace: InternPool.Index = .none,216null_stack_trace: InternPool.Index = .none,
216panic_cause_type: InternPool.Index = .none,
217panic_cause_tag_type: InternPool.Index = .none,
218panic_cause_integer_overflow: InternPool.Index = .none,
219217
220generation: u32 = 0,218generation: u32 = 0,
221219
222pub const PerThread = @import("Zcu/PerThread.zig");220pub const PerThread = @import("Zcu/PerThread.zig");
223221
222pub const PanicId = enum {
223 reached_unreachable,
224 unwrap_null,
225 cast_to_null,
226 incorrect_alignment,
227 invalid_error_code,
228 cast_truncated_data,
229 negative_to_unsigned,
230 integer_overflow,
231 shl_overflow,
232 shr_overflow,
233 divide_by_zero,
234 exact_division_remainder,
235 integer_part_out_of_bounds,
236 corrupt_switch,
237 shift_rhs_too_big,
238 invalid_enum_value,
239 for_len_mismatch,
240 memcpy_len_mismatch,
241 memcpy_alias,
242 noreturn_returned,
243
244 pub const len = @typeInfo(PanicId).@"enum".fields.len;
245};
246
224pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);247pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
225248
226pub const CImportError = struct {249pub const CImportError = struct {
src/crash_report.zig+20-29
...@@ -152,16 +152,12 @@ fn writeFilePath(file: *Zcu.File, writer: anytype) !void {...@@ -152,16 +152,12 @@ fn writeFilePath(file: *Zcu.File, writer: anytype) !void {
152 try writer.writeAll(file.sub_file_path);152 try writer.writeAll(file.sub_file_path);
153}153}
154154
155pub fn compilerPanic(155pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, maybe_ret_addr: ?usize) noreturn {
156 cause: std.builtin.PanicCause,
157 error_return_trace: ?*std.builtin.StackTrace,
158 maybe_ret_addr: ?usize,
159) noreturn {
160 @branchHint(.cold);156 @branchHint(.cold);
161 PanicSwitch.preDispatch();157 PanicSwitch.preDispatch();
162 const ret_addr = maybe_ret_addr orelse @returnAddress();158 const ret_addr = maybe_ret_addr orelse @returnAddress();
163 const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } };159 const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } };
164 PanicSwitch.dispatch(error_return_trace, stack_ctx, cause);160 PanicSwitch.dispatch(error_return_trace, stack_ctx, msg);
165}161}
166162
167/// Attaches a global SIGSEGV handler163/// Attaches a global SIGSEGV handler
...@@ -212,7 +208,7 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa...@@ -212,7 +208,7 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
212 else => .not_supported,208 else => .not_supported,
213 };209 };
214210
215 PanicSwitch.dispatch(null, stack_ctx, .{ .explicit_call = error_msg });211 PanicSwitch.dispatch(null, stack_ctx, error_msg);
216}212}
217213
218const WindowsSegfaultMessage = union(enum) {214const WindowsSegfaultMessage = union(enum) {
...@@ -335,7 +331,7 @@ const PanicSwitch = struct {...@@ -335,7 +331,7 @@ const PanicSwitch = struct {
335 // it's happening and print a message.331 // it's happening and print a message.
336 var panic_state: *volatile PanicState = &panic_state_raw;332 var panic_state: *volatile PanicState = &panic_state_raw;
337 if (panic_state.awaiting_dispatch) {333 if (panic_state.awaiting_dispatch) {
338 dispatch(null, .{ .current = .{ .ret_addr = null } }, .{ .explicit_call = "Panic while preparing callstack" });334 dispatch(null, .{ .current = .{ .ret_addr = null } }, "Panic while preparing callstack");
339 }335 }
340 panic_state.awaiting_dispatch = true;336 panic_state.awaiting_dispatch = true;
341 }337 }
...@@ -355,17 +351,17 @@ const PanicSwitch = struct {...@@ -355,17 +351,17 @@ const PanicSwitch = struct {
355 pub fn dispatch(351 pub fn dispatch(
356 trace: ?*const std.builtin.StackTrace,352 trace: ?*const std.builtin.StackTrace,
357 stack_ctx: StackContext,353 stack_ctx: StackContext,
358 panic_cause: std.builtin.PanicCause,354 msg: []const u8,
359 ) noreturn {355 ) noreturn {
360 var panic_state: *volatile PanicState = &panic_state_raw;356 var panic_state: *volatile PanicState = &panic_state_raw;
361 debug.assert(panic_state.awaiting_dispatch);357 debug.assert(panic_state.awaiting_dispatch);
362 panic_state.awaiting_dispatch = false;358 panic_state.awaiting_dispatch = false;
363 nosuspend switch (panic_state.recover_stage) {359 nosuspend switch (panic_state.recover_stage) {
364 .initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, panic_cause }),360 .initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, msg }),
365 .report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, panic_cause }),361 .report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, msg }),
366 .release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, panic_cause }),362 .release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, msg }),
367 .release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, panic_cause }),363 .release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, msg }),
368 .abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, panic_cause }),364 .abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, msg }),
369 .silent_abort => goTo(abort, .{}),365 .silent_abort => goTo(abort, .{}),
370 };366 };
371 }367 }
...@@ -374,7 +370,7 @@ const PanicSwitch = struct {...@@ -374,7 +370,7 @@ const PanicSwitch = struct {
374 state: *volatile PanicState,370 state: *volatile PanicState,
375 trace: ?*const std.builtin.StackTrace,371 trace: ?*const std.builtin.StackTrace,
376 stack: StackContext,372 stack: StackContext,
377 panic_cause: std.builtin.PanicCause,373 msg: []const u8,
378 ) noreturn {374 ) noreturn {
379 // use a temporary so there's only one volatile store375 // use a temporary so there's only one volatile store
380 const new_state = PanicState{376 const new_state = PanicState{
...@@ -399,8 +395,6 @@ const PanicSwitch = struct {...@@ -399,8 +395,6 @@ const PanicSwitch = struct {
399 const current_thread_id = std.Thread.getCurrentId();395 const current_thread_id = std.Thread.getCurrentId();
400 stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state});396 stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state});
401 }397 }
402 var buffer: [1000]u8 = undefined;
403 const msg = buffer[0..std.debug.fmtPanicCause(&buffer, panic_cause)];
404 stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state});398 stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state});
405399
406 state.recover_stage = .report_stack;400 state.recover_stage = .report_stack;
...@@ -416,9 +410,9 @@ const PanicSwitch = struct {...@@ -416,9 +410,9 @@ const PanicSwitch = struct {
416 state: *volatile PanicState,410 state: *volatile PanicState,
417 trace: ?*const std.builtin.StackTrace,411 trace: ?*const std.builtin.StackTrace,
418 stack: StackContext,412 stack: StackContext,
419 panic_cause: std.builtin.PanicCause,413 msg: []const u8,
420 ) noreturn {414 ) noreturn {
421 recover(state, trace, stack, panic_cause);415 recover(state, trace, stack, msg);
422416
423 state.recover_stage = .release_mutex;417 state.recover_stage = .release_mutex;
424 const stderr = io.getStdErr().writer();418 const stderr = io.getStdErr().writer();
...@@ -441,9 +435,9 @@ const PanicSwitch = struct {...@@ -441,9 +435,9 @@ const PanicSwitch = struct {
441 state: *volatile PanicState,435 state: *volatile PanicState,
442 trace: ?*const std.builtin.StackTrace,436 trace: ?*const std.builtin.StackTrace,
443 stack: StackContext,437 stack: StackContext,
444 panic_cause: std.builtin.PanicCause,438 msg: []const u8,
445 ) noreturn {439 ) noreturn {
446 recover(state, trace, stack, panic_cause);440 recover(state, trace, stack, msg);
447 goTo(releaseMutex, .{state});441 goTo(releaseMutex, .{state});
448 }442 }
449443
...@@ -459,9 +453,9 @@ const PanicSwitch = struct {...@@ -459,9 +453,9 @@ const PanicSwitch = struct {
459 state: *volatile PanicState,453 state: *volatile PanicState,
460 trace: ?*const std.builtin.StackTrace,454 trace: ?*const std.builtin.StackTrace,
461 stack: StackContext,455 stack: StackContext,
462 panic_cause: std.builtin.PanicCause,456 msg: []const u8,
463 ) noreturn {457 ) noreturn {
464 recover(state, trace, stack, panic_cause);458 recover(state, trace, stack, msg);
465 goTo(releaseRefCount, .{state});459 goTo(releaseRefCount, .{state});
466 }460 }
467461
...@@ -487,9 +481,9 @@ const PanicSwitch = struct {...@@ -487,9 +481,9 @@ const PanicSwitch = struct {
487 state: *volatile PanicState,481 state: *volatile PanicState,
488 trace: ?*const std.builtin.StackTrace,482 trace: ?*const std.builtin.StackTrace,
489 stack: StackContext,483 stack: StackContext,
490 panic_cause: std.builtin.PanicCause,484 msg: []const u8,
491 ) noreturn {485 ) noreturn {
492 recover(state, trace, stack, panic_cause);486 recover(state, trace, stack, msg);
493487
494 state.recover_stage = .silent_abort;488 state.recover_stage = .silent_abort;
495 const stderr = io.getStdErr().writer();489 const stderr = io.getStdErr().writer();
...@@ -513,9 +507,8 @@ const PanicSwitch = struct {...@@ -513,9 +507,8 @@ const PanicSwitch = struct {
513 state: *volatile PanicState,507 state: *volatile PanicState,
514 trace: ?*const std.builtin.StackTrace,508 trace: ?*const std.builtin.StackTrace,
515 stack: StackContext,509 stack: StackContext,
516 panic_cause: std.builtin.PanicCause,510 msg: []const u8,
517 ) void {511 ) void {
518 var buffer: [1000]u8 = undefined;
519 switch (state.recover_verbosity) {512 switch (state.recover_verbosity) {
520 .message_and_stack => {513 .message_and_stack => {
521 // lower the verbosity, and restore it at the end if we don't panic.514 // lower the verbosity, and restore it at the end if we don't panic.
...@@ -523,7 +516,6 @@ const PanicSwitch = struct {...@@ -523,7 +516,6 @@ const PanicSwitch = struct {
523516
524 const stderr = io.getStdErr().writer();517 const stderr = io.getStdErr().writer();
525 stderr.writeAll("\nPanicked during a panic: ") catch {};518 stderr.writeAll("\nPanicked during a panic: ") catch {};
526 const msg = buffer[0..std.debug.fmtPanicCause(&buffer, panic_cause)];
527 stderr.writeAll(msg) catch {};519 stderr.writeAll(msg) catch {};
528 stderr.writeAll("\nInner panic stack:\n") catch {};520 stderr.writeAll("\nInner panic stack:\n") catch {};
529 if (trace) |t| {521 if (trace) |t| {
...@@ -538,7 +530,6 @@ const PanicSwitch = struct {...@@ -538,7 +530,6 @@ const PanicSwitch = struct {
538530
539 const stderr = io.getStdErr().writer();531 const stderr = io.getStdErr().writer();
540 stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {};532 stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {};
541 const msg = buffer[0..std.debug.fmtPanicCause(&buffer, panic_cause)];
542 stderr.writeAll(msg) catch {};533 stderr.writeAll(msg) catch {};
543 stderr.writeAll("\n") catch {};534 stderr.writeAll("\n") catch {};
544535