| author | |
| committer | |
| log | e16ddad49f1b3e4ce10ce8fb653e48f24a9d4837 |
| tree | 131a0873355fecce3c1abac1d0f42b8cab27d515 |
| parent | cb616cb7972bb2f38ea4527c7ec0ae3cc0d64c7c |
* 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 { |
| 38 | pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void { | 38 | pub 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 | |||
| 42 | pub 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); |
| 892 | 892 | 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); |
| 895 | 895 | ||
| 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 | }, |
| 2703 | 2702 | ||
| 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; |
| 2 | const mem = @import("std").mem; | 2 | const mem = @import("std").mem; |
| 3 | const Tag = @import("std").meta.Tag; | 3 | const Tag = @import("std").meta.Tag; |
| 4 | 4 | ||
| 5 | const MultipleChoice = enum(u32) { | ||
| 6 | A = 20, | ||
| 7 | B = 40, | ||
| 8 | C = 60, | ||
| 9 | D = 1000, | ||
| 10 | }; | ||
| 11 | |||
| 12 | fn 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 | |||
| 22 | test "enum with specified tag values" { | ||
| 23 | try testEnumWithSpecifiedTagValues(MultipleChoice.C); | ||
| 24 | comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C); | ||
| 25 | } | ||
| 26 | |||
| 5 | test "non-exhaustive enum" { | 27 | test "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 | } |
| 190 | 212 | ||
| 191 | const MultipleChoice = enum(u32) { | ||
| 192 | A = 20, | ||
| 193 | B = 40, | ||
| 194 | C = 60, | ||
| 195 | D = 1000, | ||
| 196 | }; | ||
| 197 | |||
| 198 | test "enum with specified tag values" { | ||
| 199 | try testEnumWithSpecifiedTagValues(MultipleChoice.C); | ||
| 200 | comptime try testEnumWithSpecifiedTagValues(MultipleChoice.C); | ||
| 201 | } | ||
| 202 | |||
| 203 | fn 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 | |||
| 213 | const MultipleChoice2 = enum(u32) { | 213 | const 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 | } |
| 20 | 20 | ||
| 21 | test "float widening" { | 21 | test "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 | } |
| 36 | 30 | ||
| 37 | test "float widening f16 to f128" { | 31 | test "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/3282 | 32 | // 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; |