diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-08-24 20:27:11 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-08-24 22:20:31 -0700 |
| commit | 7453f56e678c80928ababa2868c69cfe41647fed (patch) | |
| tree | 689bfb73cab6314cb91898b174fcf12d50196009 /src | |
| parent | af19909b9cde3d009f0306ac825f39912644bca6 (diff) | |
| download | zig-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.zig | 26 | ||||
| -rw-r--r-- | src/Sema.zig | 7 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 4 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 8 | ||||
| -rw-r--r-- | src/type.zig | 51 |
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 }; } }; |
