aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-08-24 20:27:11 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-08-24 22:20:31 -0700
commit7453f56e678c80928ababa2868c69cfe41647fed (patch)
tree689bfb73cab6314cb91898b174fcf12d50196009 /src
parentaf19909b9cde3d009f0306ac825f39912644bca6 (diff)
downloadzig-7453f56e678c80928ababa2868c69cfe41647fed.tar.gz
zig-7453f56e678c80928ababa2868c69cfe41647fed.zip
stage2: explicitly tagged enums no longer have one possible value
Previously, Zig had inconsistent semantics for an enum like this: `enum(u8){zero = 0}` Although in theory this can only hold one possible value, the tag `zero`, Zig no longer will treat the type this way. It will do loads and stores, as if the type has runtime bits. Closes #12619 Tests passed locally: * test-behavior * test-cases
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig26
-rw-r--r--src/Sema.zig7
-rw-r--r--src/arch/x86_64/CodeGen.zig4
-rw-r--r--src/codegen/llvm.zig8
-rw-r--r--src/type.zig51
5 files changed, 60 insertions, 36 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 34617ed3e2..a92849e127 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -1368,18 +1368,20 @@ pub const Union = struct {
}
}
payload_align = @maximum(payload_align, 1);
- if (!have_tag or fields.len <= 1) return .{
- .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align),
- .abi_align = payload_align,
- .most_aligned_field = most_aligned_field,
- .most_aligned_field_size = most_aligned_field_size,
- .biggest_field = biggest_field,
- .payload_size = payload_size,
- .payload_align = payload_align,
- .tag_align = 0,
- .tag_size = 0,
- .padding = 0,
- };
+ if (!have_tag or !u.tag_ty.hasRuntimeBits()) {
+ return .{
+ .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align),
+ .abi_align = payload_align,
+ .most_aligned_field = most_aligned_field,
+ .most_aligned_field_size = most_aligned_field_size,
+ .biggest_field = biggest_field,
+ .payload_size = payload_size,
+ .payload_align = payload_align,
+ .tag_align = 0,
+ .tag_size = 0,
+ .padding = 0,
+ };
+ }
// Put the tag before or after the payload depending on which one's
// alignment is greater.
const tag_size = u.tag_ty.abiSize(target);
diff --git a/src/Sema.zig b/src/Sema.zig
index b24a00150a..3e8005269f 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -28844,6 +28844,10 @@ pub fn typeHasOnePossibleValue(
.enum_numbered => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
+ // An explicit tag type is always provided for enum_numbered.
+ if (enum_obj.tag_ty.hasRuntimeBits()) {
+ return null;
+ }
if (enum_obj.fields.count() == 1) {
if (enum_obj.values.count() == 0) {
return Value.zero; // auto-numbered
@@ -28857,6 +28861,9 @@ pub fn typeHasOnePossibleValue(
.enum_full => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const enum_obj = resolved_ty.castTag(.enum_full).?.data;
+ if (enum_obj.tag_ty.hasRuntimeBits()) {
+ return null;
+ }
if (enum_obj.fields.count() == 1) {
if (enum_obj.values.count() == 0) {
return Value.zero; // auto-numbered
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 0f6b6baee3..106d2feec0 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -6524,13 +6524,13 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
const extra = self.air.extraData(Air.Block, ty_pl.payload);
_ = ty_pl;
_ = extra;
- return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
+ return self.fail("TODO implement x86 airCmpxchg", .{});
// return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
}
fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
- return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
+ return self.fail("TODO implement x86 airAtomicRaw", .{});
}
fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index cddbdc6822..d872057beb 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1860,7 +1860,7 @@ pub const Object = struct {
var offset: u64 = 0;
for (fields.values()) |field, i| {
- if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
+ if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
const field_size = field.ty.abiSize(target);
const field_align = field.alignment(target, layout);
@@ -2764,7 +2764,7 @@ pub const DeclGen = struct {
var any_underaligned_fields = false;
for (struct_obj.fields.values()) |field| {
- if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
+ if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
const field_align = field.alignment(target, struct_obj.layout);
const field_ty_align = field.ty.abiAlignment(target);
@@ -3443,7 +3443,7 @@ pub const DeclGen = struct {
var need_unnamed = false;
for (struct_obj.fields.values()) |field, i| {
- if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
+ if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
const field_align = field.alignment(target, struct_obj.layout);
big_align = @maximum(big_align, field_align);
@@ -9477,7 +9477,7 @@ fn llvmFieldIndex(
var llvm_field_index: c_uint = 0;
for (ty.structFields().values()) |field, i| {
- if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
+ if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
const field_align = field.alignment(target, layout);
big_align = @maximum(big_align, field_align);
diff --git a/src/type.zig b/src/type.zig
index 1592dcf469..cc6e5706ee 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -2310,6 +2310,8 @@ pub const Type = extern union {
/// fields will count towards the ABI size. For example, `struct {T: type, x: i32}`
/// hasRuntimeBits()=true and abiSize()=4
/// * the type has only one possible value, making its ABI size 0.
+ /// - an enum with an explicit tag type has the ABI size of the integer tag type,
+ /// making it one-possible-value only if the integer tag type has 0 bits.
/// When `ignore_comptime_only` is true, then types that are comptime only
/// may return false positives.
pub fn hasRuntimeBitsAdvanced(
@@ -2452,9 +2454,9 @@ pub const Type = extern union {
_ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
}
assert(struct_obj.haveFieldTypes());
- for (struct_obj.fields.values()) |value| {
- if (value.is_comptime) continue;
- if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit))
+ for (struct_obj.fields.values()) |field| {
+ if (field.is_comptime) continue;
+ if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit))
return true;
} else {
return false;
@@ -2463,7 +2465,7 @@ pub const Type = extern union {
.enum_full => {
const enum_full = ty.castTag(.enum_full).?.data;
- return enum_full.fields.count() >= 2;
+ return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit);
},
.enum_simple => {
const enum_simple = ty.castTag(.enum_simple).?.data;
@@ -2490,9 +2492,10 @@ pub const Type = extern union {
},
.union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data;
- if (union_obj.fields.count() > 0 and try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) {
+ if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) {
return true;
}
+
if (sema_kit) |sk| {
_ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
}
@@ -3125,7 +3128,11 @@ pub const Type = extern union {
.lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
};
if (union_obj.fields.count() == 0) {
- return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) };
+ if (have_tag) {
+ return abiAlignmentAdvanced(union_obj.tag_ty, target, strat);
+ } else {
+ return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) };
+ }
}
var max_align: u32 = 0;
@@ -4991,14 +4998,18 @@ pub const Type = extern union {
.enum_numbered => {
const enum_numbered = ty.castTag(.enum_numbered).?.data;
- if (enum_numbered.fields.count() == 1) {
- return enum_numbered.values.keys()[0];
- } else {
+ // An explicit tag type is always provided for enum_numbered.
+ if (enum_numbered.tag_ty.hasRuntimeBits()) {
return null;
}
+ assert(enum_numbered.fields.count() == 1);
+ return enum_numbered.values.keys()[0];
},
.enum_full => {
const enum_full = ty.castTag(.enum_full).?.data;
+ if (enum_full.tag_ty.hasRuntimeBits()) {
+ return null;
+ }
if (enum_full.fields.count() == 1) {
if (enum_full.values.count() == 0) {
return Value.zero;
@@ -5333,7 +5344,8 @@ pub const Type = extern union {
.enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty,
.enum_simple => {
const enum_simple = ty.castTag(.enum_simple).?.data;
- const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
+ const field_count = enum_simple.fields.count();
+ const bits: u16 = if (field_count == 0) 0 else std.math.log2_int_ceil(usize, field_count);
buffer.* = .{
.base = .{ .tag = .int_unsigned },
.data = bits,
@@ -5653,19 +5665,22 @@ pub const Type = extern union {
target: Target,
pub fn next(it: *StructOffsetIterator) ?FieldOffset {
- if (it.struct_obj.fields.count() <= it.field)
+ const i = it.field;
+ if (it.struct_obj.fields.count() <= i)
return null;
- const field = it.struct_obj.fields.values()[it.field];
- defer it.field += 1;
- if (!field.ty.hasRuntimeBits() or field.is_comptime)
- return FieldOffset{ .field = it.field, .offset = it.offset };
+ const field = it.struct_obj.fields.values()[i];
+ it.field += 1;
+
+ if (field.is_comptime or !field.ty.hasRuntimeBits()) {
+ return FieldOffset{ .field = i, .offset = it.offset };
+ }
const field_align = field.alignment(it.target, it.struct_obj.layout);
it.big_align = @maximum(it.big_align, field_align);
- it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
- defer it.offset += field.ty.abiSize(it.target);
- return FieldOffset{ .field = it.field, .offset = it.offset };
+ const field_offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
+ it.offset = field_offset + field.ty.abiSize(it.target);
+ return FieldOffset{ .field = i, .offset = field_offset };
}
};