mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 23:22:44 +00:00
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.
This commit is contained in:
parent
cb616cb797
commit
e16ddad49f
@ -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 });
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.* = .{
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user