Commit cb616cb797
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 {
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,
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);
}
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,
- },
- .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) },
+ .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()),
+ };
+ },
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),
};
}
};
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,
};
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" {
{