This commit is contained in:
gabeuehlein 2024-11-26 11:04:39 +11:00 committed by GitHub
commit e9e6ae8de7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 101 additions and 5 deletions

View File

@ -9092,11 +9092,57 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try sema.requireRuntimeBlock(block, src, operand_src);
const result = try block.addTyOp(.intcast, dest_ty, operand);
if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(zcu) and
zcu.backendSupportsFeature(.is_named_enum_value))
{
const ok = try block.addUnOp(.is_named_enum_value, result);
try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
if (block.wantSafety()) {
if (dest_ty.isNonexhaustiveEnum(zcu)) {
// these checks are similar to those generated by `intCast`,
// except that `invalid_enum_value` is the panic reason for all
// failures.
const operand_ty = sema.typeOf(operand);
const operand_int_info = sema.typeOf(operand).intInfo(zcu);
const dest_tag_ty = dest_ty.intTagType(zcu);
const dest_int_info = dest_tag_ty.intInfo(zcu);
var ok: ?Air.Inst.Ref = null;
if (operand_int_info.bits > dest_int_info.bits) {
// narrowing cast; operand <= dest_max_int required, and operand >= 0 may be
// required if operand is signed and destination int is unsigned
const dest_max_int = Air.internedToRef((try dest_tag_ty.maxIntScalar(pt, operand_ty)).toIntern());
// operand <= maxInt(dest_tag_ty)
const le_check = try block.addBinOp(.cmp_lte, operand, dest_max_int);
if (operand_int_info.signedness == .signed) {
const dest_min_int = Air.internedToRef((try dest_tag_ty.minIntScalar(pt, operand_ty)).toIntern());
const ge_zero_check = try block.addBinOp(.cmp_gte, operand, dest_min_int);
// operand >= minInt(dest_tag_ty) and operand <= maxInt(dest_tag_ty)
ok = try block.addBinOp(.bool_and, le_check, ge_zero_check);
} else {
ok = le_check;
}
} else if (operand_int_info.bits == dest_int_info.bits) {
// checks are only needed here if the operand type's sign differs
// from the destination type's tag's sign.
if (operand_int_info.signedness == .unsigned and dest_int_info.signedness == .signed) {
const dest_max_int = try dest_tag_ty.maxIntScalar(pt, operand_ty);
// operand <= maxInt(dest_tag_ty)
ok = try block.addBinOp(.cmp_lte, operand, Air.internedToRef(dest_max_int.toIntern()));
} else if (operand_int_info.signedness == .signed and dest_int_info.signedness == .unsigned) {
const zero = try pt.intValue_i64(operand_ty, 0);
// operand >= 0
ok = try block.addBinOp(.cmp_gte, operand, Air.internedToRef(zero.toIntern()));
} // else => operand and destination int are the same type; no checks needed
} else {
// extending cast; no checks needed unless the operand is signed
// and the destination is unsigned
if (operand_int_info.signedness == .signed and dest_int_info.signedness == .unsigned) {
const zero = try pt.intValue_i64(operand_ty, 0);
// operand >= 0
ok = try block.addBinOp(.cmp_gte, operand, Air.internedToRef(zero.toIntern()));
}
}
if (ok) |check|
try sema.addSafetyCheck(block, src, check, .invalid_enum_value);
} else if (zcu.backendSupportsFeature(.is_named_enum_value)) {
const ok = try block.addUnOp(.is_named_enum_value, result);
try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
}
}
return result;
}

View File

@ -0,0 +1,30 @@
const std = @import("std");
const SignedWithVariants = enum(i4) { a, b, _ };
const UnsignedWithVariants = enum(u4) { a, b, _ };
const SignedEmpty = enum(i6) { _ };
const UnsignedEmpty = enum(u6) { _ };
pub fn main() void {
inline for (.{ SignedWithVariants, UnsignedWithVariants, SignedEmpty, UnsignedEmpty }) |EnumTy| {
const TagType = @typeInfo(EnumTy).@"enum".tag_type;
var v: isize = std.math.minInt(TagType);
while (v < std.math.maxInt(TagType)) : (v += 1) {
const variant = @as(EnumTy, @enumFromInt(v));
assert(@as(@TypeOf(v), @intCast(@intFromEnum(variant))) == v);
}
const max = std.math.maxInt(TagType);
const max_variant = @as(EnumTy, @enumFromInt(max));
assert(@as(@TypeOf(max), @intCast(@intFromEnum(max_variant))) == max);
}
}
fn assert(ok: bool) void {
if (!ok) unreachable;
}
// run
// backend=stage2,llvm

View File

@ -0,0 +1,20 @@
const std = @import("std");
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
if (std.mem.eql(u8, message, "invalid enum value")) {
std.process.exit(0);
}
std.process.exit(1);
}
pub fn main() void {
const E = enum(u4) { _ };
var invalid: u16 = 16;
_ = &invalid;
_ = @as(E, @enumFromInt(invalid));
std.process.exit(1);
}
// run
// backend=llvm
// target=native