zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit c1a5caa4545264b476951e844818f2abe103f41c (tree)
parent 6daa37ded905431f3a25508a42590031b0905ab9
Author: mlugg <mlugg@mlugg.co.uk>
Date:   Sun,  1 Jun 2025 07:41:24 +0100

compiler: combine `@intCast` safety checks

`castTruncatedData` was a poorly worded error (all shrinking casts
"truncate bits", it's just that we assume those bits to be zext/sext of
the other bits!), and `negativeToUnsigned` was a pointless distinction
which forced the compiler to emit worse code (since two separate safety
checks were required for casting e.g. 'i32' to 'u16') and wasn't even
implemented correctly. This commit combines those safety panics into one
function, `integerOutOfBounds`. The name maybe isn't perfect, but that's
not hugely important; what matters is the new default message, which is
clearer than the old ones: "integer does not fit in destination type".

Diffstat:
Mdoc/langref/test_intCast_builtin.zig | 2+-
Mlib/std/debug.zig | 12++++++------
Mlib/std/debug/no_panic.zig | 11+++++------
Mlib/std/debug/simple_panic.zig | 12++++++------
Msrc/Air/Legalize.zig | 2+-
Msrc/Sema.zig | 16++++++----------
Msrc/Zcu.zig | 12++++--------
Msrc/codegen/llvm.zig | 10+++-------
Mtest/cases/compile_errors/bad_panic_call_signature.zig | 5+----
Mtest/cases/compile_errors/bad_panic_generic_signature.zig | 5+----
Mtest/cases/safety/@intCast to u0.zig | 2+-
Mtest/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig | 2+-
Mtest/cases/safety/signed integer not fitting in cast to unsigned integer.zig | 2+-
Mtest/cases/safety/signed-unsigned vector cast.zig | 2+-
Mtest/cases/safety/truncating vector cast.zig | 2+-
Mtest/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig | 2+-
Mtest/cases/safety/unsigned-signed vector cast.zig | 2+-
Mtest/cases/safety/value does not fit in shortening cast - u0.zig | 2+-
Mtest/cases/safety/value does not fit in shortening cast.zig | 2+-
Mtest/incremental/change_panic_handler_explicit | 15+++------------
20 files changed, 46 insertions(+), 74 deletions(-)

diff --git a/doc/langref/test_intCast_builtin.zig b/doc/langref/test_intCast_builtin.zig @@ -5,4 +5,4 @@ test "integer cast panic" { _ = b; } -// test_error=cast truncated bits +// test_error=integer does not fit in destination type diff --git a/lib/std/debug.zig b/lib/std/debug.zig @@ -78,13 +78,9 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type { @branchHint(.cold); call("invalid error code", @returnAddress()); } - pub fn castTruncatedData() noreturn { + pub fn integerOutOfBounds() noreturn { @branchHint(.cold); - call("integer cast truncated bits", @returnAddress()); - } - pub fn negativeToUnsigned() noreturn { - @branchHint(.cold); - call("attempt to cast negative value to unsigned integer", @returnAddress()); + call("integer does not fit in destination type", @returnAddress()); } pub fn integerOverflow() noreturn { @branchHint(.cold); @@ -128,6 +124,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type { } /// Delete after next zig1.wasm update pub const memcpyLenMismatch = copyLenMismatch; + /// Delete after next zig1.wasm update + pub const castTruncatedData = integerOutOfBounds; + /// Delete after next zig1.wasm update + pub const negativeToUnsigned = integerOutOfBounds; pub fn copyLenMismatch() noreturn { @branchHint(.cold); call("source and destination arguments have non-equal lengths", @returnAddress()); diff --git a/lib/std/debug/no_panic.zig b/lib/std/debug/no_panic.zig @@ -65,12 +65,7 @@ pub fn invalidErrorCode() noreturn { @trap(); } -pub fn castTruncatedData() noreturn { - @branchHint(.cold); - @trap(); -} - -pub fn negativeToUnsigned() noreturn { +pub fn integerOutOfBounds() noreturn { @branchHint(.cold); @trap(); } @@ -127,6 +122,10 @@ pub fn forLenMismatch() noreturn { /// Delete after next zig1.wasm update pub const memcpyLenMismatch = copyLenMismatch; +/// Delete after next zig1.wasm update +pub const castTruncatedData = integerOutOfBounds; +/// Delete after next zig1.wasm update +pub const negativeToUnsigned = integerOutOfBounds; pub fn copyLenMismatch() noreturn { @branchHint(.cold); diff --git a/lib/std/debug/simple_panic.zig b/lib/std/debug/simple_panic.zig @@ -72,12 +72,8 @@ pub fn invalidErrorCode() noreturn { call("invalid error code", null); } -pub fn castTruncatedData() noreturn { - call("integer cast truncated bits", null); -} - -pub fn negativeToUnsigned() noreturn { - call("attempt to cast negative value to unsigned integer", null); +pub fn integerOutOfBounds() noreturn { + call("integer does not fit in destination type", null); } pub fn integerOverflow() noreturn { @@ -122,6 +118,10 @@ pub fn forLenMismatch() noreturn { /// Delete after next zig1.wasm update pub const memcpyLenMismatch = copyLenMismatch; +/// Delete after next zig1.wasm update +pub const castTruncatedData = integerOutOfBounds; +/// Delete after next zig1.wasm update +pub const negativeToUnsigned = integerOutOfBounds; pub fn copyLenMismatch() noreturn { call("source and destination have non-equal lengths", null); diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig @@ -1307,7 +1307,7 @@ fn safeIntcastBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Error!Air.In var main_block: Block = .init(&inst_buf); var cur_block: *Block = &main_block; - const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .cast_truncated_data; + const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds; if (have_min_check or have_max_check) { const dest_int_ty = if (dest_is_enum) dest_ty.intTagType(zcu) else dest_ty; diff --git a/src/Sema.zig b/src/Sema.zig @@ -10263,7 +10263,7 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); const operand = try sema.resolveInst(extra.rhs); - return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src, true, false); + return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src); } fn intCast( @@ -10274,8 +10274,6 @@ fn intCast( dest_ty_src: LazySrcLoc, operand: Air.Inst.Ref, operand_src: LazySrcLoc, - runtime_safety: bool, - safety_panics_are_enum: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; @@ -10294,7 +10292,7 @@ fn intCast( if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 - if (runtime_safety and block.wantSafety()) { + if (block.wantSafety()) { try sema.requireRuntimeBlock(block, src, operand_src); const wanted_info = dest_scalar_ty.intInfo(zcu); const wanted_bits = wanted_info.bits; @@ -10311,7 +10309,7 @@ fn intCast( const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst); break :ok is_in_range; }; - try sema.addSafetyCheck(block, src, ok, if (safety_panics_are_enum) .invalid_enum_value else .cast_truncated_data); + try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds); } } @@ -10319,10 +10317,9 @@ fn intCast( } try sema.requireRuntimeBlock(block, src, operand_src); - if (runtime_safety and block.wantSafety()) { + if (block.wantSafety()) { if (zcu.backendSupportsFeature(.panic_fn)) { - _ = try sema.preparePanicId(src, .negative_to_unsigned); - _ = try sema.preparePanicId(src, .cast_truncated_data); + _ = try sema.preparePanicId(src, .integer_out_of_bounds); } return block.addTyOp(.intcast_safe, dest_ty, operand); } @@ -37984,8 +37981,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ .@"panic.castToNull", .@"panic.incorrectAlignment", .@"panic.invalidErrorCode", - .@"panic.castTruncatedData", - .@"panic.negativeToUnsigned", + .@"panic.integerOutOfBounds", .@"panic.integerOverflow", .@"panic.shlOverflow", .@"panic.shrOverflow", diff --git a/src/Zcu.zig b/src/Zcu.zig @@ -441,8 +441,7 @@ pub const BuiltinDecl = enum { @"panic.castToNull", @"panic.incorrectAlignment", @"panic.invalidErrorCode", - @"panic.castTruncatedData", - @"panic.negativeToUnsigned", + @"panic.integerOutOfBounds", @"panic.integerOverflow", @"panic.shlOverflow", @"panic.shrOverflow", @@ -518,8 +517,7 @@ pub const BuiltinDecl = enum { .@"panic.castToNull", .@"panic.incorrectAlignment", .@"panic.invalidErrorCode", - .@"panic.castTruncatedData", - .@"panic.negativeToUnsigned", + .@"panic.integerOutOfBounds", .@"panic.integerOverflow", .@"panic.shlOverflow", .@"panic.shrOverflow", @@ -585,8 +583,7 @@ pub const SimplePanicId = enum { cast_to_null, incorrect_alignment, invalid_error_code, - cast_truncated_data, - negative_to_unsigned, + integer_out_of_bounds, integer_overflow, shl_overflow, shr_overflow, @@ -609,8 +606,7 @@ pub const SimplePanicId = enum { .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_out_of_bounds => .@"panic.integerOutOfBounds", .integer_overflow => .@"panic.integerOverflow", .shl_overflow => .@"panic.shlOverflow", .shr_overflow => .@"panic.shrOverflow", diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -9189,11 +9189,7 @@ pub const FuncGen = struct { const is_vector = operand_ty.zigTypeTag(zcu) == .vector; assert(is_vector == (dest_ty.zigTypeTag(zcu) == .vector)); - const min_panic_id: Zcu.SimplePanicId, const max_panic_id: Zcu.SimplePanicId = id: { - if (dest_is_enum) break :id .{ .invalid_enum_value, .invalid_enum_value }; - if (dest_info.signedness == .unsigned) break :id .{ .negative_to_unsigned, .cast_truncated_data }; - break :id .{ .cast_truncated_data, .cast_truncated_data }; - }; + const panic_id: Zcu.SimplePanicId = if (dest_is_enum) .invalid_enum_value else .integer_out_of_bounds; if (have_min_check) { const min_const_scalar = try minIntConst(&o.builder, dest_scalar, operand_scalar_llvm_ty, zcu); @@ -9207,7 +9203,7 @@ pub const FuncGen = struct { const ok_block = try fg.wip.block(1, "IntMinOk"); _ = try fg.wip.brCond(ok, ok_block, fail_block, .none); fg.wip.cursor = .{ .block = fail_block }; - try fg.buildSimplePanic(min_panic_id); + try fg.buildSimplePanic(panic_id); fg.wip.cursor = .{ .block = ok_block }; } @@ -9223,7 +9219,7 @@ pub const FuncGen = struct { const ok_block = try fg.wip.block(1, "IntMaxOk"); _ = try fg.wip.brCond(ok, ok_block, fail_block, .none); fg.wip.cursor = .{ .block = fail_block }; - try fg.buildSimplePanic(max_panic_id); + try fg.buildSimplePanic(panic_id); fg.wip.cursor = .{ .block = ok_block }; } } diff --git a/test/cases/compile_errors/bad_panic_call_signature.zig b/test/cases/compile_errors/bad_panic_call_signature.zig @@ -15,8 +15,7 @@ pub const panic = struct { 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 integerOutOfBounds = simple_panic.integerOutOfBounds; pub const integerOverflow = simple_panic.integerOverflow; pub const shlOverflow = simple_panic.shlOverflow; pub const shrOverflow = simple_panic.shrOverflow; @@ -27,8 +26,6 @@ pub const panic = struct { pub const shiftRhsTooBig = simple_panic.shiftRhsTooBig; pub const invalidEnumValue = simple_panic.invalidEnumValue; pub const forLenMismatch = simple_panic.forLenMismatch; - /// Delete after next zig1.wasm update - pub const memcpyLenMismatch = copyLenMismatch; pub const copyLenMismatch = simple_panic.copyLenMismatch; pub const memcpyAlias = simple_panic.memcpyAlias; pub const noreturnReturned = simple_panic.noreturnReturned; diff --git a/test/cases/compile_errors/bad_panic_generic_signature.zig b/test/cases/compile_errors/bad_panic_generic_signature.zig @@ -11,8 +11,7 @@ pub const panic = struct { 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 integerOutOfBounds = simple_panic.integerOutOfBounds; pub const integerOverflow = simple_panic.integerOverflow; pub const shlOverflow = simple_panic.shlOverflow; pub const shrOverflow = simple_panic.shrOverflow; @@ -23,8 +22,6 @@ pub const panic = struct { pub const shiftRhsTooBig = simple_panic.shiftRhsTooBig; pub const invalidEnumValue = simple_panic.invalidEnumValue; pub const forLenMismatch = simple_panic.forLenMismatch; - /// Delete after next zig1.wasm update - pub const memcpyLenMismatch = copyLenMismatch; pub const copyLenMismatch = simple_panic.copyLenMismatch; pub const memcpyAlias = simple_panic.memcpyAlias; pub const noreturnReturned = simple_panic.noreturnReturned; diff --git a/test/cases/safety/@intCast to u0.zig b/test/cases/safety/@intCast to u0.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/signed-unsigned vector cast.zig b/test/cases/safety/signed-unsigned vector cast.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/truncating vector cast.zig b/test/cases/safety/truncating vector cast.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/unsigned-signed vector cast.zig b/test/cases/safety/unsigned-signed vector cast.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/value does not fit in shortening cast - u0.zig b/test/cases/safety/value does not fit in shortening cast - u0.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/cases/safety/value does not fit in shortening cast.zig b/test/cases/safety/value does not fit in shortening cast.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "integer cast truncated bits")) { + if (std.mem.eql(u8, message, "integer does not fit in destination type")) { std.process.exit(0); } std.process.exit(1); diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit @@ -26,8 +26,7 @@ pub const panic = struct { 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 integerOutOfBounds = no_panic.integerOutOfBounds; pub const shlOverflow = no_panic.shlOverflow; pub const shrOverflow = no_panic.shrOverflow; pub const divideByZero = no_panic.divideByZero; @@ -37,8 +36,6 @@ pub const panic = struct { pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; pub const invalidEnumValue = no_panic.invalidEnumValue; pub const forLenMismatch = no_panic.forLenMismatch; - /// Delete after next zig1.wasm update - pub const memcpyLenMismatch = copyLenMismatch; pub const copyLenMismatch = no_panic.copyLenMismatch; pub const memcpyAlias = no_panic.memcpyAlias; pub const noreturnReturned = no_panic.noreturnReturned; @@ -75,8 +72,7 @@ pub const panic = struct { 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 integerOutOfBounds = no_panic.integerOutOfBounds; pub const shlOverflow = no_panic.shlOverflow; pub const shrOverflow = no_panic.shrOverflow; pub const divideByZero = no_panic.divideByZero; @@ -86,8 +82,6 @@ pub const panic = struct { pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; pub const invalidEnumValue = no_panic.invalidEnumValue; pub const forLenMismatch = no_panic.forLenMismatch; - /// Delete after next zig1.wasm update - pub const memcpyLenMismatch = copyLenMismatch; pub const copyLenMismatch = no_panic.copyLenMismatch; pub const memcpyAlias = no_panic.memcpyAlias; pub const noreturnReturned = no_panic.noreturnReturned; @@ -124,8 +118,7 @@ pub const panic = struct { 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 integerOutOfBounds = no_panic.integerOutOfBounds; pub const shlOverflow = no_panic.shlOverflow; pub const shrOverflow = no_panic.shrOverflow; pub const divideByZero = no_panic.divideByZero; @@ -135,8 +128,6 @@ pub const panic = struct { pub const shiftRhsTooBig = no_panic.shiftRhsTooBig; pub const invalidEnumValue = no_panic.invalidEnumValue; pub const forLenMismatch = no_panic.forLenMismatch; - /// Delete after next zig1.wasm update - pub const memcpyLenMismatch = copyLenMismatch; pub const copyLenMismatch = no_panic.copyLenMismatch; pub const memcpyAlias = no_panic.memcpyAlias; pub const noreturnReturned = no_panic.noreturnReturned;