diff --git a/src/Air.zig b/src/Air.zig index 72aa603645..900b1f57e5 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -272,8 +272,11 @@ pub const Inst = struct { /// Uses the `ty_op` field. fpext, /// Returns an integer with a different type than the operand. The new type may have - /// fewer, the same, or more bits than the operand type. However, the instruction + /// fewer, the same, or more bits than the operand type. The new type may also + /// differ in signedness from the operand type. However, the instruction /// guarantees that the same integer value fits in both types. + /// The new type may also be an enum type, in which case the integer cast operates on + /// the integer tag type of the enum. /// See `trunc` for integer truncation. /// Uses the `ty_op` field. intcast, diff --git a/src/Sema.zig b/src/Sema.zig index 137d2ca2fd..993cde739e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4312,7 +4312,8 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } try sema.requireRuntimeBlock(block, src); - return block.addTyOp(.bitcast, dest_ty, operand); + // TODO insert safety check to make sure the value matches an enum value + return block.addTyOp(.intcast, dest_ty, operand); } /// Pointer in, pointer out. @@ -5050,6 +5051,7 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } try sema.requireRuntimeBlock(block, operand_src); + // TODO insert safety check to make sure the value fits in the dest type return block.addTyOp(.intcast, dest_type, operand); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c9e9b240bd..00df4346d2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2381,8 +2381,11 @@ pub const FuncGen = struct { .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""), .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""), } + } else if (operand_info.bits > dest_info.bits) { + return self.builder.buildTrunc(operand, dest_llvm_ty, ""); + } else { + return operand; } - return self.builder.buildTrunc(operand, dest_llvm_ty, ""); } fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/src/type.zig b/src/type.zig index 317d2dacbe..d6362cf31e 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2657,38 +2657,49 @@ pub const Type = extern union { }; } - /// Asserts the type is an integer. + /// Asserts the type is an integer or enum. pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { - return switch (self.tag()) { - .int_unsigned => .{ + var ty = self; + while (true) switch (ty.tag()) { + .int_unsigned => return .{ .signedness = .unsigned, - .bits = self.castTag(.int_unsigned).?.data, + .bits = ty.castTag(.int_unsigned).?.data, }, - .int_signed => .{ + .int_signed => return .{ .signedness = .signed, - .bits = self.castTag(.int_signed).?.data, + .bits = ty.castTag(.int_signed).?.data, + }, + .u1 => return .{ .signedness = .unsigned, .bits = 1 }, + .u8 => return .{ .signedness = .unsigned, .bits = 8 }, + .i8 => return .{ .signedness = .signed, .bits = 8 }, + .u16 => return .{ .signedness = .unsigned, .bits = 16 }, + .i16 => return .{ .signedness = .signed, .bits = 16 }, + .u32 => return .{ .signedness = .unsigned, .bits = 32 }, + .i32 => return .{ .signedness = .signed, .bits = 32 }, + .u64 => return .{ .signedness = .unsigned, .bits = 64 }, + .i64 => return .{ .signedness = .signed, .bits = 64 }, + .u128 => return .{ .signedness = .unsigned, .bits = 128 }, + .i128 => return .{ .signedness = .signed, .bits = 128 }, + .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, + .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, + .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, + .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, + .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, + .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, + .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, + .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, + .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, + .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, + + .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, + .enum_numbered => ty = self.castTag(.enum_numbered).?.data.tag_ty, + .enum_simple => { + const enum_obj = self.castTag(.enum_simple).?.data; + return .{ + .signedness = .unsigned, + .bits = smallestUnsignedBits(enum_obj.fields.count()), + }; }, - .u1 => .{ .signedness = .unsigned, .bits = 1 }, - .u8 => .{ .signedness = .unsigned, .bits = 8 }, - .i8 => .{ .signedness = .signed, .bits = 8 }, - .u16 => .{ .signedness = .unsigned, .bits = 16 }, - .i16 => .{ .signedness = .signed, .bits = 16 }, - .u32 => .{ .signedness = .unsigned, .bits = 32 }, - .i32 => .{ .signedness = .signed, .bits = 32 }, - .u64 => .{ .signedness = .unsigned, .bits = 64 }, - .i64 => .{ .signedness = .signed, .bits = 64 }, - .u128 => .{ .signedness = .unsigned, .bits = 128 }, - .i128 => .{ .signedness = .signed, .bits = 128 }, - .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, - .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, - .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, - .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, - .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, - .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, - .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, - .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, - .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, - .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, else => unreachable, }; @@ -3905,20 +3916,22 @@ pub const Type = extern union { return Type.initPayload(&type_payload.base); } + pub fn smallestUnsignedBits(max: u64) u16 { + if (max == 0) return 0; + const base = std.math.log2(max); + const upper = (@as(u64, 1) << @intCast(u6, base)) - 1; + return @intCast(u16, base + @boolToInt(upper < max)); + } + pub fn smallestUnsignedInt(arena: *Allocator, max: u64) !Type { - const bits = bits: { - if (max == 0) break :bits 0; - const base = std.math.log2(max); - const upper = (@as(u64, 1) << @intCast(u6, base)) - 1; - break :bits base + @boolToInt(upper < max); - }; - return switch (@intCast(u16, bits)) { + const bits = smallestUnsignedBits(max); + return switch (bits) { 1 => initTag(.u1), 8 => initTag(.u8), 16 => initTag(.u16), 32 => initTag(.u32), 64 => initTag(.u64), - else => |b| return Tag.int_unsigned.create(arena, b), + else => return Tag.int_unsigned.create(arena, bits), }; } }; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index bd47481b5f..684115b992 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -17,6 +17,15 @@ test "enum to int" { try shouldEqual(Number.Four, 4); } +fn testIntToEnumEval(x: i32) !void { + try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three); +} +const IntToEnumNumber = enum { Zero, One, Two, Three, Four }; + +test "int to enum" { + try testIntToEnumEval(3); +} + const ValueCount1 = enum { I0, }; diff --git a/test/behavior/enum_stage1.zig b/test/behavior/enum_stage1.zig index 2e73e55aa4..dc5cdf4369 100644 --- a/test/behavior/enum_stage1.zig +++ b/test/behavior/enum_stage1.zig @@ -104,18 +104,6 @@ const Bar = enum { A, B, C, D }; const Number = enum { Zero, One, Two, Three, Four }; -fn shouldEqual(n: Number, expected: u3) !void { - try expect(@enumToInt(n) == expected); -} - -test "int to enum" { - try testIntToEnumEval(3); -} -fn testIntToEnumEval(x: i32) !void { - try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three); -} -const IntToEnumNumber = enum { Zero, One, Two, Three, Four }; - test "@tagName" { try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); @@ -131,544 +119,9 @@ fn testEnumTagNameBare(n: anytype) []const u8 { } const BareNumber = enum { One, Two, Three }; - const NonExhaustive = enum(u8) { A, B, _ }; - -const ValueCount1 = enum { - I0, -}; -const ValueCount2 = enum { - I0, - I1, -}; -const ValueCount256 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, -}; -const ValueCount257 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, - I256, -}; - -const Small2 = enum(u2) { - One, - Two, -}; -const Small = enum(u2) { - One, - Two, - Three, - Four, -}; +const Small2 = enum(u2) { One, Two }; +const Small = enum(u2) { One, Two, Three, Four }; test "set enum tag type" { {