mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 15:12:31 +00:00
Merge da0eb66b67
into 87863a834b
This commit is contained in:
commit
f04c3cad69
56
src/Sema.zig
56
src/Sema.zig
@ -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;
|
||||
}
|
||||
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user