zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit e16ddad49f1b3e4ce10ce8fb653e48f24a9d4837 (tree)
parent cb616cb7972bb2f38ea4527c7ec0ae3cc0d64c7c
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Tue,  5 Oct 2021 23:05:14 -0700

stage2: enum fixes

 * Sema: fix a missing copy on enum tag values
 * LLVM backend: fix lowering of enum constant values for enums with
   specified tag values.
 * Value: fix enumToInt for `enum_numbered` cases.

The float widening behavior tests which rely on compiler-rt symbols are
now passing.

Diffstat:
Msrc/Sema.zig | 5++++-
Msrc/TypedValue.zig | 4++++
Msrc/codegen/llvm.zig | 33+++++++++++++++++++++------------
Msrc/type.zig | 7+++----
Msrc/value.zig | 13+++++++++++++
Mtest/behavior/enum_stage1.zig | 44++++++++++++++++++++++----------------------
Mtest/behavior/widening.zig | 12------------
7 files changed, 67 insertions(+), 51 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;