diff --git a/src/Sema.zig b/src/Sema.zig index 993cde739e..32970ccd40 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1644,7 +1644,10 @@ fn zirEnumDecl( // that points to this default value expression rather than the struct. // But only resolve the source location if we need to emit a compile error. const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; - enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); + const copied_tag_val = try tag_val.copy(&new_decl_arena.allocator); + enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ + .ty = enum_obj.tag_ty, + }); } else if (any_values) { const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i); enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 83242b5329..09ded3745e 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -38,3 +38,7 @@ pub fn eql(a: TypedValue, b: TypedValue) bool { pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void { return tv.val.hash(tv.ty, hasher); } + +pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { + return tv.val.enumToInt(tv.ty, buffer); +} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 00df4346d2..ceaca07a49 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -889,9 +889,9 @@ pub const DeclGen = struct { .Int => { var bigint_space: Value.BigIntSpace = undefined; const bigint = tv.val.toBigInt(&bigint_space); - - const llvm_type = try self.llvmType(tv.ty); - if (bigint.eqZero()) return llvm_type.constNull(); + const target = self.module.getTarget(); + const int_info = tv.ty.intInfo(target); + const llvm_type = self.context.intType(int_info.bits); const unsigned_val = if (bigint.limbs.len == 1) llvm_type.constInt(bigint.limbs[0], .False) @@ -903,15 +903,24 @@ pub const DeclGen = struct { return unsigned_val; }, .Enum => { - const llvm_type = try self.llvmType(tv.ty); - const uint: u64 = uint: { - if (tv.val.castTag(.enum_field_index)) |payload| { - break :uint payload.data; - } - break :uint tv.val.toUnsignedInt(); - }; - const llvm_int = llvm_type.constInt(uint, .False); - return llvm_int; + var int_buffer: Value.Payload.U64 = undefined; + const int_val = tv.enumToInt(&int_buffer); + + var bigint_space: Value.BigIntSpace = undefined; + const bigint = int_val.toBigInt(&bigint_space); + + const target = self.module.getTarget(); + const int_info = tv.ty.intInfo(target); + const llvm_type = self.context.intType(int_info.bits); + + const unsigned_val = if (bigint.limbs.len == 1) + llvm_type.constInt(bigint.limbs[0], .False) + else + llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr); + if (!bigint.positive) { + return llvm.constNeg(unsigned_val); + } + return unsigned_val; }, .Float => { const llvm_ty = try self.llvmType(tv.ty); diff --git a/src/type.zig b/src/type.zig index d6362cf31e..e7356e4842 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2695,10 +2695,9 @@ pub const Type = extern union { .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()), - }; + const field_count = enum_obj.fields.count(); + if (field_count == 0) return .{ .signedness = .unsigned, .bits = 0 }; + return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) }; }, else => unreachable, diff --git a/src/value.zig b/src/value.zig index 90d98bd539..4e8c79c93b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -858,6 +858,19 @@ pub const Value = extern union { return Value.initPayload(&buffer.base); } }, + .enum_numbered => { + const enum_obj = ty.castTag(.enum_numbered).?.data; + if (enum_obj.values.count() != 0) { + return enum_obj.values.keys()[field_index]; + } else { + // Field index and integer values are the same. + buffer.* = .{ + .base = .{ .tag = .int_u64 }, + .data = field_index, + }; + return Value.initPayload(&buffer.base); + } + }, .enum_simple => { // Field index and integer values are the same. buffer.* = .{ diff --git a/test/behavior/enum_stage1.zig b/test/behavior/enum_stage1.zig index dc5cdf4369..fbc73215ba 100644 --- a/test/behavior/enum_stage1.zig +++ b/test/behavior/enum_stage1.zig @@ -2,6 +2,28 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; const Tag = @import("std").meta.Tag; +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; + +fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void { + try expect(@enumToInt(x) == 60); + try expect(1234 == switch (x) { + MultipleChoice.A => 1, + MultipleChoice.B => 2, + MultipleChoice.C => @as(u32, 1234), + MultipleChoice.D => 4, + }); +} + +test "enum with specified tag values" { + try testEnumWithSpecifiedTagValues(MultipleChoice.C); + comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C); +} + test "non-exhaustive enum" { const S = struct { const E = enum(u8) { @@ -188,28 +210,6 @@ fn testCastEnumTag(value: Small2) !void { try expect(@enumToInt(value) == 1); } -const MultipleChoice = enum(u32) { - A = 20, - B = 40, - C = 60, - D = 1000, -}; - -test "enum with specified tag values" { - try testEnumWithSpecifiedTagValues(MultipleChoice.C); - comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C); -} - -fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void { - try expect(@enumToInt(x) == 60); - try expect(1234 == switch (x) { - MultipleChoice.A => 1, - MultipleChoice.B => 2, - MultipleChoice.C => @as(u32, 1234), - MultipleChoice.D => 4, - }); -} - const MultipleChoice2 = enum(u32) { Unspecified1, A = 20, diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index 9c1694b368..21fe731ca5 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -19,12 +19,6 @@ test "implicit unsigned integer to signed integer" { } test "float widening" { - if (@import("builtin").zig_is_stage2) { - // This test is passing but it depends on compiler-rt symbols, which - // cannot yet be built with stage2 due to - // "TODO implement equality comparison between a union's tag value and an enum literal" - return error.SkipZigTest; - } var a: f16 = 12.34; var b: f32 = a; var c: f64 = b; @@ -35,12 +29,6 @@ test "float widening" { } test "float widening f16 to f128" { - if (@import("builtin").zig_is_stage2) { - // This test is passing but it depends on compiler-rt symbols, which - // cannot yet be built with stage2 due to - // "TODO implement equality comparison between a union's tag value and an enum literal" - return error.SkipZigTest; - } // TODO https://github.com/ziglang/zig/issues/3282 if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest; if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest;