diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 16581b7782..cc43e11a7d 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -3,7 +3,7 @@ const io = std.io; const os = std.os; const math = std.math; const mem = std.mem; -const debug = std.debug; +const assert = std.debug.assert; const File = std.fs.File; const native_endian = @import("builtin").target.cpu.arch.endian(); @@ -872,14 +872,14 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct { }; comptime { - debug.assert(@sizeOf(Elf32_Ehdr) == 52); - debug.assert(@sizeOf(Elf64_Ehdr) == 64); + assert(@sizeOf(Elf32_Ehdr) == 52); + assert(@sizeOf(Elf64_Ehdr) == 64); - debug.assert(@sizeOf(Elf32_Phdr) == 32); - debug.assert(@sizeOf(Elf64_Phdr) == 56); + assert(@sizeOf(Elf32_Phdr) == 32); + assert(@sizeOf(Elf64_Phdr) == 56); - debug.assert(@sizeOf(Elf32_Shdr) == 40); - debug.assert(@sizeOf(Elf64_Shdr) == 64); + assert(@sizeOf(Elf32_Shdr) == 40); + assert(@sizeOf(Elf64_Shdr) == 64); } pub const Auxv = switch (@sizeOf(usize)) { 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 }; } }; diff --git a/test/behavior.zig b/test/behavior.zig index ba8379cd72..12edd6f9a3 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -26,7 +26,6 @@ test { _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); - _ = @import("behavior/bugs/1111.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1310.zig"); _ = @import("behavior/bugs/1381.zig"); diff --git a/test/behavior/bugs/1111.zig b/test/behavior/bugs/1111.zig deleted file mode 100644 index d274befaf3..0000000000 --- a/test/behavior/bugs/1111.zig +++ /dev/null @@ -1,11 +0,0 @@ -const Foo = enum(c_int) { - Bar = -1, -}; - -test "issue 1111 fixed" { - const v = Foo.Bar; - - switch (v) { - Foo.Bar => return, - } -} diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 709d30af33..9e96163cc0 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; +const assert = std.debug.assert; const mem = std.mem; const Tag = std.meta.Tag; @@ -1128,3 +1129,44 @@ test "tag name functions are unique" { _ = a; } } + +test "size of enum with only one tag which has explicit integer tag type" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + const E = enum(u8) { nope = 10 }; + const S0 = struct { e: E }; + const S1 = extern struct { e: E }; + //const U = union(E) { nope: void }; + comptime assert(@sizeOf(E) == 1); + comptime assert(@sizeOf(S0) == 1); + comptime assert(@sizeOf(S1) == 1); + //comptime assert(@sizeOf(U) == 1); + + var s1: S1 = undefined; + s1.e = .nope; + try expect(s1.e == .nope); + const ptr = @ptrCast(*u8, &s1); + try expect(ptr.* == 10); + + var s0: S0 = undefined; + s0.e = .nope; + try expect(s0.e == .nope); +} + +test "switch on an extern enum with negative value" { + // TODO x86, wasm backends fail because they assume that enum tag types are unsigned + if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; + + const Foo = enum(c_int) { + Bar = -1, + }; + + const v = Foo.Bar; + + switch (v) { + Foo.Bar => return, + } +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 92f277b946..e2078c66df 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; +const assert = std.debug.assert; const expectEqual = std.testing.expectEqual; const Tag = std.meta.Tag; @@ -1065,6 +1066,8 @@ test "@unionInit on union with tag but no fields" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { const Type = enum(u8) { no_op = 105 }; @@ -1079,11 +1082,7 @@ test "@unionInit on union with tag but no fields" { }; comptime { - if (builtin.zig_backend == .stage1) { - // stage1 gets the wrong answer here - } else { - std.debug.assert(@sizeOf(Data) == 0); - } + assert(@sizeOf(Data) == 1); } fn doTheTest() !void {