authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-05 23:05:14-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-05 23:05:14-07:00
loge16ddad49f1b3e4ce10ce8fb653e48f24a9d4837
tree131a0873355fecce3c1abac1d0f42b8cab27d515
parentcb616cb7972bb2f38ea4527c7ec0ae3cc0d64c7c

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.

7 files changed, 67 insertions(+), 51 deletions(-)

src/Sema.zig+4-1
...@@ -1644,7 +1644,10 @@ fn zirEnumDecl(...@@ -1644,7 +1644,10 @@ fn zirEnumDecl(
1644 // that points to this default value expression rather than the struct.1644 // that points to this default value expression rather than the struct.
1645 // But only resolve the source location if we need to emit a compile error.1645 // But only resolve the source location if we need to emit a compile error.
1646 const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val;1646 const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val;
1647 enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty });1647 const copied_tag_val = try tag_val.copy(&new_decl_arena.allocator);
1648 enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
1649 .ty = enum_obj.tag_ty,
1650 });
1648 } else if (any_values) {1651 } else if (any_values) {
1649 const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i);1652 const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i);
1650 enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty });1653 enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty });
src/TypedValue.zig+4
...@@ -38,3 +38,7 @@ pub fn eql(a: TypedValue, b: TypedValue) bool {...@@ -38,3 +38,7 @@ pub fn eql(a: TypedValue, b: TypedValue) bool {
38pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void {38pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void {
39 return tv.val.hash(tv.ty, hasher);39 return tv.val.hash(tv.ty, hasher);
40}40}
41
42pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value {
43 return tv.val.enumToInt(tv.ty, buffer);
44}
src/codegen/llvm.zig+21-12
...@@ -889,9 +889,9 @@ pub const DeclGen = struct {...@@ -889,9 +889,9 @@ pub const DeclGen = struct {
889 .Int => {889 .Int => {
890 var bigint_space: Value.BigIntSpace = undefined;890 var bigint_space: Value.BigIntSpace = undefined;
891 const bigint = tv.val.toBigInt(&bigint_space);891 const bigint = tv.val.toBigInt(&bigint_space);
892892 const target = self.module.getTarget();
893 const llvm_type = try self.llvmType(tv.ty);893 const int_info = tv.ty.intInfo(target);
894 if (bigint.eqZero()) return llvm_type.constNull();894 const llvm_type = self.context.intType(int_info.bits);
895895
896 const unsigned_val = if (bigint.limbs.len == 1)896 const unsigned_val = if (bigint.limbs.len == 1)
897 llvm_type.constInt(bigint.limbs[0], .False)897 llvm_type.constInt(bigint.limbs[0], .False)
...@@ -903,15 +903,24 @@ pub const DeclGen = struct {...@@ -903,15 +903,24 @@ pub const DeclGen = struct {
903 return unsigned_val;903 return unsigned_val;
904 },904 },
905 .Enum => {905 .Enum => {
906 const llvm_type = try self.llvmType(tv.ty);906 var int_buffer: Value.Payload.U64 = undefined;
907 const uint: u64 = uint: {907 const int_val = tv.enumToInt(&int_buffer);
908 if (tv.val.castTag(.enum_field_index)) |payload| {908
909 break :uint payload.data;909 var bigint_space: Value.BigIntSpace = undefined;
910 }910 const bigint = int_val.toBigInt(&bigint_space);
911 break :uint tv.val.toUnsignedInt();911
912 };912 const target = self.module.getTarget();
913 const llvm_int = llvm_type.constInt(uint, .False);913 const int_info = tv.ty.intInfo(target);
914 return llvm_int;914 const llvm_type = self.context.intType(int_info.bits);
915
916 const unsigned_val = if (bigint.limbs.len == 1)
917 llvm_type.constInt(bigint.limbs[0], .False)
918 else
919 llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr);
920 if (!bigint.positive) {
921 return llvm.constNeg(unsigned_val);
922 }
923 return unsigned_val;
915 },924 },
916 .Float => {925 .Float => {
917 const llvm_ty = try self.llvmType(tv.ty);926 const llvm_ty = try self.llvmType(tv.ty);
src/type.zig+3-4
...@@ -2695,10 +2695,9 @@ pub const Type = extern union {...@@ -2695,10 +2695,9 @@ pub const Type = extern union {
2695 .enum_numbered => ty = self.castTag(.enum_numbered).?.data.tag_ty,2695 .enum_numbered => ty = self.castTag(.enum_numbered).?.data.tag_ty,
2696 .enum_simple => {2696 .enum_simple => {
2697 const enum_obj = self.castTag(.enum_simple).?.data;2697 const enum_obj = self.castTag(.enum_simple).?.data;
2698 return .{2698 const field_count = enum_obj.fields.count();
2699 .signedness = .unsigned,2699 if (field_count == 0) return .{ .signedness = .unsigned, .bits = 0 };
2700 .bits = smallestUnsignedBits(enum_obj.fields.count()),2700 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };
2701 };
2702 },2701 },
27032702
2704 else => unreachable,2703 else => unreachable,
src/value.zig+13
...@@ -858,6 +858,19 @@ pub const Value = extern union {...@@ -858,6 +858,19 @@ pub const Value = extern union {
858 return Value.initPayload(&buffer.base);858 return Value.initPayload(&buffer.base);
859 }859 }
860 },860 },
861 .enum_numbered => {
862 const enum_obj = ty.castTag(.enum_numbered).?.data;
863 if (enum_obj.values.count() != 0) {
864 return enum_obj.values.keys()[field_index];
865 } else {
866 // Field index and integer values are the same.
867 buffer.* = .{
868 .base = .{ .tag = .int_u64 },
869 .data = field_index,
870 };
871 return Value.initPayload(&buffer.base);
872 }
873 },
861 .enum_simple => {874 .enum_simple => {
862 // Field index and integer values are the same.875 // Field index and integer values are the same.
863 buffer.* = .{876 buffer.* = .{
test/behavior/enum_stage1.zig+22-22
...@@ -2,6 +2,28 @@ const expect = @import("std").testing.expect;...@@ -2,6 +2,28 @@ const expect = @import("std").testing.expect;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const Tag = @import("std").meta.Tag;3const Tag = @import("std").meta.Tag;
44
5const MultipleChoice = enum(u32) {
6 A = 20,
7 B = 40,
8 C = 60,
9 D = 1000,
10};
11
12fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
13 try expect(@enumToInt(x) == 60);
14 try expect(1234 == switch (x) {
15 MultipleChoice.A => 1,
16 MultipleChoice.B => 2,
17 MultipleChoice.C => @as(u32, 1234),
18 MultipleChoice.D => 4,
19 });
20}
21
22test "enum with specified tag values" {
23 try testEnumWithSpecifiedTagValues(MultipleChoice.C);
24 comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C);
25}
26
5test "non-exhaustive enum" {27test "non-exhaustive enum" {
6 const S = struct {28 const S = struct {
7 const E = enum(u8) {29 const E = enum(u8) {
...@@ -188,28 +210,6 @@ fn testCastEnumTag(value: Small2) !void {...@@ -188,28 +210,6 @@ fn testCastEnumTag(value: Small2) !void {
188 try expect(@enumToInt(value) == 1);210 try expect(@enumToInt(value) == 1);
189}211}
190212
191const MultipleChoice = enum(u32) {
192 A = 20,
193 B = 40,
194 C = 60,
195 D = 1000,
196};
197
198test "enum with specified tag values" {
199 try testEnumWithSpecifiedTagValues(MultipleChoice.C);
200 comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C);
201}
202
203fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
204 try expect(@enumToInt(x) == 60);
205 try expect(1234 == switch (x) {
206 MultipleChoice.A => 1,
207 MultipleChoice.B => 2,
208 MultipleChoice.C => @as(u32, 1234),
209 MultipleChoice.D => 4,
210 });
211}
212
213const MultipleChoice2 = enum(u32) {213const MultipleChoice2 = enum(u32) {
214 Unspecified1,214 Unspecified1,
215 A = 20,215 A = 20,
test/behavior/widening.zig-12
...@@ -19,12 +19,6 @@ test "implicit unsigned integer to signed integer" {...@@ -19,12 +19,6 @@ test "implicit unsigned integer to signed integer" {
19}19}
2020
21test "float widening" {21test "float widening" {
22 if (@import("builtin").zig_is_stage2) {
23 // This test is passing but it depends on compiler-rt symbols, which
24 // cannot yet be built with stage2 due to
25 // "TODO implement equality comparison between a union's tag value and an enum literal"
26 return error.SkipZigTest;
27 }
28 var a: f16 = 12.34;22 var a: f16 = 12.34;
29 var b: f32 = a;23 var b: f32 = a;
30 var c: f64 = b;24 var c: f64 = b;
...@@ -35,12 +29,6 @@ test "float widening" {...@@ -35,12 +29,6 @@ test "float widening" {
35}29}
3630
37test "float widening f16 to f128" {31test "float widening f16 to f128" {
38 if (@import("builtin").zig_is_stage2) {
39 // This test is passing but it depends on compiler-rt symbols, which
40 // cannot yet be built with stage2 due to
41 // "TODO implement equality comparison between a union's tag value and an enum literal"
42 return error.SkipZigTest;
43 }
44 // TODO https://github.com/ziglang/zig/issues/328232 // TODO https://github.com/ziglang/zig/issues/3282
45 if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest;33 if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest;
46 if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest;34 if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest;