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".
This commit is contained in:
mlugg 2025-06-01 07:41:24 +01:00
parent 6daa37ded9
commit c1a5caa454
No known key found for this signature in database
GPG key ID: 3F5B7DCCBF4AF02E
20 changed files with 46 additions and 74 deletions

View file

@ -5,4 +5,4 @@ test "integer cast panic" {
_ = b;
}
// test_error=cast truncated bits
// test_error=integer does not fit in destination type

View file

@ -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());

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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",

View file

@ -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",

View file

@ -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 };
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;