authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-05-19 17:01:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-05 03:29:37+02:00
loge88566253d65a83e3564f95504fe034b4287c340
treeec55973b4700ead9f55cd253b9e4ed7151167b6a
parentf611a72e2eb7bba2ef5fc5f578ced3c711ff7374

sema: improve @errorCast safety check message


10 files changed, 55 insertions(+), 6 deletions(-)

lib/std/debug.zig+4
...@@ -151,6 +151,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type {...@@ -151,6 +151,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type {
151 @branchHint(.cold);151 @branchHint(.cold);
152 call("invalid error code", @returnAddress());152 call("invalid error code", @returnAddress());
153 }153 }
154 pub fn unexpectedErrorCode(err: anyerror) noreturn {
155 @branchHint(.cold);
156 std.debug.panicExtra(@returnAddress(), "unexpected error code, found error.{s}", .{@errorName(err)});
157 }
154 pub fn integerOutOfBounds() noreturn {158 pub fn integerOutOfBounds() noreturn {
155 @branchHint(.cold);159 @branchHint(.cold);
156 call("integer does not fit in destination type", @returnAddress());160 call("integer does not fit in destination type", @returnAddress());
lib/std/debug/no_panic.zig+5
...@@ -65,6 +65,11 @@ pub fn invalidErrorCode() noreturn {...@@ -65,6 +65,11 @@ pub fn invalidErrorCode() noreturn {
65 @trap();65 @trap();
66}66}
6767
68pub fn unexpectedErrorCode(_: anyerror) noreturn {
69 @branchHint(.cold);
70 @trap();
71}
72
68pub fn integerOutOfBounds() noreturn {73pub fn integerOutOfBounds() noreturn {
69 @branchHint(.cold);74 @branchHint(.cold);
70 @trap();75 @trap();
lib/std/debug/simple_panic.zig+31
...@@ -20,110 +20,141 @@ pub fn call(msg: []const u8, ra: ?usize) noreturn {...@@ -20,110 +20,141 @@ pub fn call(msg: []const u8, ra: ?usize) noreturn {
20}20}
2121
22pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {22pub fn sentinelMismatch(expected: anytype, found: @TypeOf(expected)) noreturn {
23 @branchHint(.cold);
23 _ = found;24 _ = found;
24 call("sentinel mismatch", null);25 call("sentinel mismatch", null);
25}26}
2627
27pub fn unwrapError(err: anyerror) noreturn {28pub fn unwrapError(err: anyerror) noreturn {
29 @branchHint(.cold);
28 _ = &err;30 _ = &err;
29 call("attempt to unwrap error", null);31 call("attempt to unwrap error", null);
30}32}
3133
32pub fn outOfBounds(index: usize, len: usize) noreturn {34pub fn outOfBounds(index: usize, len: usize) noreturn {
35 @branchHint(.cold);
33 _ = index;36 _ = index;
34 _ = len;37 _ = len;
35 call("index out of bounds", null);38 call("index out of bounds", null);
36}39}
3740
38pub fn startGreaterThanEnd(start: usize, end: usize) noreturn {41pub fn startGreaterThanEnd(start: usize, end: usize) noreturn {
42 @branchHint(.cold);
39 _ = start;43 _ = start;
40 _ = end;44 _ = end;
41 call("start index is larger than end index", null);45 call("start index is larger than end index", null);
42}46}
4347
44pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {48pub fn inactiveUnionField(active: anytype, accessed: @TypeOf(active)) noreturn {
49 @branchHint(.cold);
45 _ = accessed;50 _ = accessed;
46 call("access of inactive union field", null);51 call("access of inactive union field", null);
47}52}
4853
49pub fn sliceCastLenRemainder(src_len: usize) noreturn {54pub fn sliceCastLenRemainder(src_len: usize) noreturn {
55 @branchHint(.cold);
50 _ = src_len;56 _ = src_len;
51 call("slice length does not divide exactly into destination elements", null);57 call("slice length does not divide exactly into destination elements", null);
52}58}
5359
54pub fn reachedUnreachable() noreturn {60pub fn reachedUnreachable() noreturn {
61 @branchHint(.cold);
55 call("reached unreachable code", null);62 call("reached unreachable code", null);
56}63}
5764
58pub fn unwrapNull() noreturn {65pub fn unwrapNull() noreturn {
66 @branchHint(.cold);
59 call("attempt to use null value", null);67 call("attempt to use null value", null);
60}68}
6169
62pub fn castToNull() noreturn {70pub fn castToNull() noreturn {
71 @branchHint(.cold);
63 call("cast causes pointer to be null", null);72 call("cast causes pointer to be null", null);
64}73}
6574
66pub fn incorrectAlignment() noreturn {75pub fn incorrectAlignment() noreturn {
76 @branchHint(.cold);
67 call("incorrect alignment", null);77 call("incorrect alignment", null);
68}78}
6979
70pub fn invalidErrorCode() noreturn {80pub fn invalidErrorCode() noreturn {
81 @branchHint(.cold);
71 call("invalid error code", null);82 call("invalid error code", null);
72}83}
7384
85pub fn unexpectedErrorCode(err: anyerror) noreturn {
86 @branchHint(.cold);
87 _ = err;
88 call("unexpected error code", null);
89}
90
74pub fn integerOutOfBounds() noreturn {91pub fn integerOutOfBounds() noreturn {
92 @branchHint(.cold);
75 call("integer does not fit in destination type", null);93 call("integer does not fit in destination type", null);
76}94}
7795
78pub fn integerOverflow() noreturn {96pub fn integerOverflow() noreturn {
97 @branchHint(.cold);
79 call("integer overflow", null);98 call("integer overflow", null);
80}99}
81100
82pub fn shlOverflow() noreturn {101pub fn shlOverflow() noreturn {
102 @branchHint(.cold);
83 call("left shift overflowed bits", null);103 call("left shift overflowed bits", null);
84}104}
85105
86pub fn shrOverflow() noreturn {106pub fn shrOverflow() noreturn {
107 @branchHint(.cold);
87 call("right shift overflowed bits", null);108 call("right shift overflowed bits", null);
88}109}
89110
90pub fn divideByZero() noreturn {111pub fn divideByZero() noreturn {
112 @branchHint(.cold);
91 call("division by zero", null);113 call("division by zero", null);
92}114}
93115
94pub fn exactDivisionRemainder() noreturn {116pub fn exactDivisionRemainder() noreturn {
117 @branchHint(.cold);
95 call("exact division produced remainder", null);118 call("exact division produced remainder", null);
96}119}
97120
98pub fn integerPartOutOfBounds() noreturn {121pub fn integerPartOutOfBounds() noreturn {
122 @branchHint(.cold);
99 call("integer part of floating point value out of bounds", null);123 call("integer part of floating point value out of bounds", null);
100}124}
101125
102pub fn corruptSwitch() noreturn {126pub fn corruptSwitch() noreturn {
127 @branchHint(.cold);
103 call("switch on corrupt value", null);128 call("switch on corrupt value", null);
104}129}
105130
106pub fn shiftRhsTooBig() noreturn {131pub fn shiftRhsTooBig() noreturn {
132 @branchHint(.cold);
107 call("shift amount is greater than the type size", null);133 call("shift amount is greater than the type size", null);
108}134}
109135
110pub fn invalidEnumValue() noreturn {136pub fn invalidEnumValue() noreturn {
137 @branchHint(.cold);
111 call("invalid enum value", null);138 call("invalid enum value", null);
112}139}
113140
114pub fn forLenMismatch() noreturn {141pub fn forLenMismatch() noreturn {
142 @branchHint(.cold);
115 call("for loop over objects with non-equal lengths", null);143 call("for loop over objects with non-equal lengths", null);
116}144}
117145
118pub fn copyLenMismatch() noreturn {146pub fn copyLenMismatch() noreturn {
147 @branchHint(.cold);
119 call("source and destination have non-equal lengths", null);148 call("source and destination have non-equal lengths", null);
120}149}
121150
122pub fn memcpyAlias() noreturn {151pub fn memcpyAlias() noreturn {
152 @branchHint(.cold);
123 call("@memcpy arguments alias", null);153 call("@memcpy arguments alias", null);
124}154}
125155
126pub fn noreturnReturned() noreturn {156pub fn noreturnReturned() noreturn {
157 @branchHint(.cold);
127 call("'noreturn' function returned", null);158 call("'noreturn' function returned", null);
128}159}
129160
src/Sema.zig+6-4
...@@ -21675,16 +21675,16 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData...@@ -21675,16 +21675,16 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
21675 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);21675 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
21676 if (result == .disjoint) {21676 if (result == .disjoint) {
21677 // Error must be zero.21677 // Error must be zero.
21678 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);21678 try sema.addSafetyCheckCall(block, src, is_zero, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21679 } else {21679 } else {
21680 // Error must be in destination set or zero.21680 // Error must be in destination set or zero.
21681 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);21681 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
21682 const ok = try block.addBinOp(.bit_or, has_value, is_zero);21682 const ok = try block.addBinOp(.bit_or, has_value, is_zero);
21683 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);21683 try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21684 }21684 }
21685 } else {21685 } else {
21686 const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);21686 const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
21687 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);21687 try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21688 }21688 }
21689 }21689 }
2169021690
...@@ -35031,7 +35031,9 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.StdLangDecl) CompileError!Typ...@@ -35031,7 +35031,9 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.StdLangDecl) CompileError!Typ
35031 }),35031 }),
3503235032
35033 // `fn (anyerror) noreturn`35033 // `fn (anyerror) noreturn`
35034 .@"panic.unwrapError" => try pt.funcType(.{35034 .@"panic.unwrapError",
35035 .@"panic.unexpectedErrorCode",
35036 => try pt.funcType(.{
35035 .param_types = &.{.anyerror_type},35037 .param_types = &.{.anyerror_type},
35036 .return_type = .noreturn_type,35038 .return_type = .noreturn_type,
35037 }),35039 }),
src/Zcu.zig+2
...@@ -503,6 +503,7 @@ pub const StdLangDecl = enum {...@@ -503,6 +503,7 @@ pub const StdLangDecl = enum {
503 @"panic.castToNull",503 @"panic.castToNull",
504 @"panic.incorrectAlignment",504 @"panic.incorrectAlignment",
505 @"panic.invalidErrorCode",505 @"panic.invalidErrorCode",
506 @"panic.unexpectedErrorCode",
506 @"panic.integerOutOfBounds",507 @"panic.integerOutOfBounds",
507 @"panic.integerOverflow",508 @"panic.integerOverflow",
508 @"panic.shlOverflow",509 @"panic.shlOverflow",
...@@ -593,6 +594,7 @@ pub const StdLangDecl = enum {...@@ -593,6 +594,7 @@ pub const StdLangDecl = enum {
593 .@"panic.castToNull",594 .@"panic.castToNull",
594 .@"panic.incorrectAlignment",595 .@"panic.incorrectAlignment",
595 .@"panic.invalidErrorCode",596 .@"panic.invalidErrorCode",
597 .@"panic.unexpectedErrorCode",
596 .@"panic.integerOutOfBounds",598 .@"panic.integerOutOfBounds",
597 .@"panic.integerOverflow",599 .@"panic.integerOverflow",
598 .@"panic.shlOverflow",600 .@"panic.shlOverflow",
test/cases/compile_errors/bad_panic_call_signature.zig+1
...@@ -15,6 +15,7 @@ pub const panic = struct {...@@ -15,6 +15,7 @@ pub const panic = struct {
15 pub const castToNull = simple_panic.castToNull;15 pub const castToNull = simple_panic.castToNull;
16 pub const incorrectAlignment = simple_panic.incorrectAlignment;16 pub const incorrectAlignment = simple_panic.incorrectAlignment;
17 pub const invalidErrorCode = simple_panic.invalidErrorCode;17 pub const invalidErrorCode = simple_panic.invalidErrorCode;
18 pub const unexpectedErrorCode = simple_panic.unexpectedErrorCode;
18 pub const integerOutOfBounds = simple_panic.integerOutOfBounds;19 pub const integerOutOfBounds = simple_panic.integerOutOfBounds;
19 pub const integerOverflow = simple_panic.integerOverflow;20 pub const integerOverflow = simple_panic.integerOverflow;
20 pub const shlOverflow = simple_panic.shlOverflow;21 pub const shlOverflow = simple_panic.shlOverflow;
test/cases/compile_errors/bad_panic_generic_signature.zig+1
...@@ -11,6 +11,7 @@ pub const panic = struct {...@@ -11,6 +11,7 @@ pub const panic = struct {
11 pub const castToNull = simple_panic.castToNull;11 pub const castToNull = simple_panic.castToNull;
12 pub const incorrectAlignment = simple_panic.incorrectAlignment;12 pub const incorrectAlignment = simple_panic.incorrectAlignment;
13 pub const invalidErrorCode = simple_panic.invalidErrorCode;13 pub const invalidErrorCode = simple_panic.invalidErrorCode;
14 pub const unexpectedErrorCode = simple_panic.unexpectedErrorCode;
14 pub const integerOutOfBounds = simple_panic.integerOutOfBounds;15 pub const integerOutOfBounds = simple_panic.integerOutOfBounds;
15 pub const integerOverflow = simple_panic.integerOverflow;16 pub const integerOverflow = simple_panic.integerOverflow;
16 pub const shlOverflow = simple_panic.shlOverflow;17 pub const shlOverflow = simple_panic.shlOverflow;
test/cases/safety/@errorCast error not present in destination.zig +1-1
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid error code")) {5 if (std.mem.eql(u8, message, "unexpected error code, found error.B")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
test/cases/safety/@errorCast error union casted to disjoint set.zig +1-1
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "invalid error code")) {5 if (std.mem.eql(u8, message, "unexpected error code, found error.Bar")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
test/incremental/change_panic_handler_explicit+3
...@@ -23,6 +23,7 @@ pub const panic = struct {...@@ -23,6 +23,7 @@ pub const panic = struct {
23 pub const castToNull = no_panic.castToNull;23 pub const castToNull = no_panic.castToNull;
24 pub const incorrectAlignment = no_panic.incorrectAlignment;24 pub const incorrectAlignment = no_panic.incorrectAlignment;
25 pub const invalidErrorCode = no_panic.invalidErrorCode;25 pub const invalidErrorCode = no_panic.invalidErrorCode;
26 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
26 pub const integerOutOfBounds = no_panic.integerOutOfBounds;27 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
27 pub const shlOverflow = no_panic.shlOverflow;28 pub const shlOverflow = no_panic.shlOverflow;
28 pub const shrOverflow = no_panic.shrOverflow;29 pub const shrOverflow = no_panic.shrOverflow;
...@@ -72,6 +73,7 @@ pub const panic = struct {...@@ -72,6 +73,7 @@ pub const panic = struct {
72 pub const castToNull = no_panic.castToNull;73 pub const castToNull = no_panic.castToNull;
73 pub const incorrectAlignment = no_panic.incorrectAlignment;74 pub const incorrectAlignment = no_panic.incorrectAlignment;
74 pub const invalidErrorCode = no_panic.invalidErrorCode;75 pub const invalidErrorCode = no_panic.invalidErrorCode;
76 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
75 pub const integerOutOfBounds = no_panic.integerOutOfBounds;77 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
76 pub const shlOverflow = no_panic.shlOverflow;78 pub const shlOverflow = no_panic.shlOverflow;
77 pub const shrOverflow = no_panic.shrOverflow;79 pub const shrOverflow = no_panic.shrOverflow;
...@@ -121,6 +123,7 @@ pub const panic = struct {...@@ -121,6 +123,7 @@ pub const panic = struct {
121 pub const castToNull = no_panic.castToNull;123 pub const castToNull = no_panic.castToNull;
122 pub const incorrectAlignment = no_panic.incorrectAlignment;124 pub const incorrectAlignment = no_panic.incorrectAlignment;
123 pub const invalidErrorCode = no_panic.invalidErrorCode;125 pub const invalidErrorCode = no_panic.invalidErrorCode;
126 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
124 pub const integerOutOfBounds = no_panic.integerOutOfBounds;127 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
125 pub const shlOverflow = no_panic.shlOverflow;128 pub const shlOverflow = no_panic.shlOverflow;
126 pub const shrOverflow = no_panic.shrOverflow;129 pub const shrOverflow = no_panic.shrOverflow;