From 9aeac329a25ddf43bf3afdfe8c381682226dafdf Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Tue, 2 Jun 2026 16:36:15 +0200 Subject: [PATCH] Sema: require `noreturn` as backing integer of empty enums Empty enums are uninstantiable, so their backing integer should be too. This avoids a bunch of weird special cases during semantic analysis. If you were previously using `enum {}`, your code should continue to work fine. If you were previously using `enum(uN) {}` (where `uN` is any integer type), you'll probably want to use `enum(uN) { _ }` instead. --- doc/langref.html.in | 2 +- doc/langref/test_enums.zig | 6 ++++ lib/std/meta.zig | 6 ++-- lib/std/zig.zig | 2 -- lib/std/zig/parser_test.zig | 2 +- src/InternPool.zig | 9 ++---- src/Sema.zig | 29 ++--------------- src/Sema/type_resolution.zig | 28 ++++++++++++---- src/Type.zig | 12 +++++-- src/link/Dwarf.zig | 1 + test/behavior/enum.zig | 23 ------------- test/behavior/type.zig | 2 +- .../enum_noreturn_backing_type.zig | 16 ++++++++++ ...non-extern-compatible_integer_tag_type.zig | 30 ++++++++++------- .../compile_errors/initialize_empty_union.zig | 4 +-- .../compile_errors/instantiate_empty_enum.zig | 32 +++++++++++++++++++ .../int_from_enum_undefined.zig | 11 ------- ...truct_with_fields_of_not_allowed_types.zig | 8 +++++ ...union_with_fields_of_not_allowed_types.zig | 8 +++++ ...e_for_tagged_union_with_no_enum_fields.zig | 2 +- .../sizeof_alignof_empty_union.zig | 4 +-- 21 files changed, 137 insertions(+), 100 deletions(-) create mode 100644 test/cases/compile_errors/enum_noreturn_backing_type.zig create mode 100644 test/cases/compile_errors/instantiate_empty_enum.zig delete mode 100644 test/cases/compile_errors/int_from_enum_undefined.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 4766e8d508d31b76b6258501ef1226f7d809c1e1..7930468882d7eeae945dfeb77f62ac74dcf0e16a 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2401,7 +2401,7 @@ or {#header_open|enum#} {#code|test_enums.zig#} - {#see_also|@typeInfo|@tagName|@sizeOf#} + {#see_also|@typeInfo|@tagName|@sizeOf|noreturn#} {#header_open|extern enum#}

diff --git a/doc/langref/test_enums.zig b/doc/langref/test_enums.zig index 9502e7ae74c05c9b786c69847c3cfd5aa08888c3..9da7c160edcabd10a2e8fc07a6e7e480edaffb2c 100644 --- a/doc/langref/test_enums.zig +++ b/doc/langref/test_enums.zig @@ -111,4 +111,10 @@ test "@tagName" { try expectEqualStrings(@tagName(Small.three), "three"); } +// Empty enums are uninstantiable, their tag type is always noreturn. +const Empty = enum {}; +test "empty enum" { + try expectEqual(noreturn, @typeInfo(Empty).@"enum".tag_type); +} + // test diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 14270ba9d86763f72a5a75eb5f846dee00759954..78e26d6c8f4f99dd04dcf0dcf3e79e8ae14e20d2 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -408,7 +408,8 @@ pub fn FieldEnum(comptime T: type) type { else => {}, } - const IntTag = std.math.IntFittingRange(0, field_names.len -| 1); + if (field_names.len == 0) return enum {}; + const IntTag = std.math.IntFittingRange(0, field_names.len - 1); return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len)); } @@ -469,7 +470,8 @@ test FieldEnum { pub fn DeclEnum(comptime T: type) type { const decl_names = declarations(T); - const IntTag = std.math.IntFittingRange(0, decl_names.len -| 1); + if (decl_names.len == 0) return enum {}; + const IntTag = std.math.IntFittingRange(0, decl_names.len - 1); return @Enum(IntTag, .exhaustive, decl_names, &std.simd.iota(IntTag, decl_names.len)); } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 3568ecf4d32886675b4b075112c285acbed84329..424effacc3e7f1b38a977ce9e685dc3ced6dd83f 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -906,7 +906,6 @@ pub const SimpleComptimeReason = enum(u32) { slice_single_item_ptr_bounds, stored_to_comptime_field, stored_to_comptime_var, - casted_to_comptime_enum, casted_to_comptime_int, casted_to_comptime_float, std_lang_decl, @@ -989,7 +988,6 @@ pub const SimpleComptimeReason = enum(u32) { .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds", .stored_to_comptime_field => "value stored to a comptime field must be comptime-known", .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known", - .casted_to_comptime_enum => "value casted to enum with 'comptime_int' tag type must be comptime-known", .casted_to_comptime_int => "value casted to 'comptime_int' must be comptime-known", .casted_to_comptime_float => "value casted to 'comptime_float' must be comptime-known", .std_lang_decl => "'std.lang' declaration values must be comptime-known", diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e46ac087648f95f8ac4b40c42c33ad248ecac645..c471a2d20f1c5d9d8a7d75ce9e2bde4502418e43 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1120,7 +1120,7 @@ test "zig fmt: empty enum decls" { \\const A = enum {}; \\const B = enum(u32) {}; \\const C = extern enum(c_int) {}; - \\const D = packed enum(u8) {}; + \\const D = packed enum(noreturn) {}; \\ ); } diff --git a/src/InternPool.zig b/src/InternPool.zig index eae7f32c7f17782b694a21d06c52bfe456049dcf..632bd75ca0f5ec2b1198f4bd8fac0bdc76fdbe0b 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -5040,7 +5040,6 @@ pub const Tag = enum(u8) { /// The set of values that are encoded this way is: /// * An array or vector which has length 0. /// * A struct which has all fields comptime-known. - /// * An empty enum or union. TODO: this value's existence is strange, because such a type in reality has no values. See #15909 /// data is Index of the type, which is known to be zero bits at runtime. only_possible_value, /// data is extra index to Key.Union. @@ -7740,12 +7739,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: }), .enum_tag => |enum_tag| { - assert(ip.isEnumType(enum_tag.ty)); - switch (ip.indexToKey(enum_tag.ty)) { - .simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))), - .enum_type => assert(ip.typeOf(enum_tag.int) == ip.loadEnumType(enum_tag.ty).int_tag_type), - else => unreachable, - } + const enum_obj = ip.loadEnumType(enum_tag.ty); + assert(ip.typeOf(enum_tag.int) == enum_obj.int_tag_type); items.appendAssumeCapacity(.{ .tag = .enum_tag, .data = try addExtra(extra, enum_tag), diff --git a/src/Sema.zig b/src/Sema.zig index 4b29027056be47f0bccaa0250f7b30a3a8980667..12fc7cb5fc145b79ab9da6f9da193ed78aa1d338 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7854,14 +7854,7 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }; const enum_tag_ty = sema.typeOf(enum_tag); const int_tag_ty = enum_tag_ty.intTagType(zcu); - - // TODO: use correct solution - // https://github.com/ziglang/zig/issues/15909 - if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) { - return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{ - enum_tag_ty.fmt(pt), - }); - } + assert(int_tag_ty.classify(zcu) != .no_possible_value); if (sema.resolveValue(enum_tag)) |enum_tag_val| { if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty); @@ -7910,10 +7903,6 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); } - if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) { - return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum }); - } - if (try dest_ty.onePossibleValue(pt)) |opv| { if (block.wantSafety()) { // The operand is runtime-known but the result is comptime-known. In @@ -9940,13 +9929,6 @@ fn analyzeSwitchBlock( operand_ty.containerLayout(zcu) != .@"packed"; const err_set = operand_ty.zigTypeTag(zcu) == .error_set; - if (item_ty.zigTypeTag(zcu) == .@"enum" and - validated_switch.seen_enum_fields.len == 0 and - !operand_ty.isNonexhaustiveEnum(zcu)) - { - return .void_value; // switch on empty enum/union - } - const cond_ref = switch (operand) { .simple => |s| s.cond, .loop => |l| l.init_cond, @@ -19567,14 +19549,6 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air operand_ty.fmt(pt), }), }; - if (enum_ty.enumFieldCount(zcu) == 0) { - // TODO I don't think this is the correct way to handle this but - // it prevents a crash. - // https://github.com/ziglang/zig/issues/15909 - return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{ - enum_ty.fmt(pt), - }); - } const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { @@ -33985,6 +33959,7 @@ fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { // The `tagValueIndex` function call below relies on the type being the integer tag type. // `getCoerced` assumes the value will fit the new type. const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type); + if (int_tag_ty.classify(zcu) == .no_possible_value) return false; if (!int.intFitsInType(int_tag_ty, null, zcu)) return false; const int_coerced = try pt.getCoerced(int, int_tag_ty); return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 116ee444f05dc2c575f6b2481378c8f11c8d9fd0..5c4211d22980d2d5daedb08b80e1f151990bddcc 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -1325,15 +1325,31 @@ pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void { const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index); break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref); }; + const empty_exhaustive = enum_obj.field_names.len == 0 and !enum_obj.nonexhaustive; const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: { - if (int_tag_ty.zigTypeTag(zcu) != .int) return sema.fail( - &block, - block.src(.container_arg), - "expected integer tag type, found '{f}'", - .{int_tag_ty.fmt(pt)}, - ); + switch (int_tag_ty.zigTypeTag(zcu)) { + .int => if (empty_exhaustive) return sema.fail( + &block, + block.src(.container_arg), + "empty exhaustive enums must be backed by 'noreturn'", + .{}, + ), + .noreturn => if (!empty_exhaustive) return sema.fail( + &block, + block.src(.container_arg), + "non-empty enums cannot be backed by 'noreturn'", + .{}, + ), + else => return sema.fail( + &block, + block.src(.container_arg), + "expected integer tag type, found '{f}'", + .{int_tag_ty.fmt(pt)}, + ), + } break :ty int_tag_ty; } else ty: { + if (empty_exhaustive) break :ty .noreturn; // Infer the int tag type from the field count const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1); break :ty try pt.intType(.unsigned, bits); diff --git a/src/Type.zig b/src/Type.zig index 6372c1e157172f6a7b4ee97f3d441af76e9a500b..b56a4868bc587ca5473001779e107fd221068975 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3058,9 +3058,15 @@ pub fn unpackable(ty: Type, zcu: *const Zcu) ?UnpackableReason { .one, .many, .c => .pointer, }, - .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).int_tag_mode) { - .explicit => null, - .auto => .{ .enum_inferred_int_tag = ty }, + .@"enum" => { + const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern()); + return switch (enum_obj.int_tag_mode) { + .explicit => if (enum_obj.int_tag_type != .noreturn_type) + null + else + .other, + .auto => .{ .enum_inferred_int_tag = ty }, + }; }, .@"struct" => switch (ty.containerLayout(zcu)) { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index b6ff6c754bc97b0efc78522b30dcf302d9ad072d..baa5be93e937a72c22193b3e159bd4df3b960ac1 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -5098,6 +5098,7 @@ fn DeclValEnum(comptime T: type) type { if (min_value == null or min_value.? > value) min_value = value; if (max_value == null or max_value.? < value) max_value = value; } + if (fields_len == 0) return enum {}; const TagInt = std.math.IntFittingRange(min_value orelse 0, max_value orelse 0); var field_vals: [fields_len]TagInt = undefined; for (field_names[0..fields_len], &field_vals) |name, *val| val.* = @field(T, name); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index ca253a102ff5351d730e5ad6f004755e0c225c5f..aa664a46ff2a5f96f7b9b8175394676df235b379 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1451,29 +1451,6 @@ test "comptime @enumFromInt with signed arithmetic" { comptime assert(@intFromEnum(x) == 0); } -test "switch on empty enum" { - const E = enum {}; - var e: E = undefined; - _ = &e; - switch (e) {} -} - -test "switch on empty enum with a specified tag type" { - const E = enum(u8) {}; - var e: E = undefined; - _ = &e; - switch (e) {} -} - -test "empty enum passed as argument" { - const E = enum { - fn f(e: @This()) void { - switch (e) {} - } - }; - E.f(@as(E, undefined)); -} - test "enum int tag type uses declaration inside the enum" { const static = struct { const E = enum(E.IntTag) { diff --git a/test/behavior/type.zig b/test/behavior/type.zig index c2d1fc2f4fbf36674bdf3eab2b506fd4acd1fdb4..bf012e0061f86384fa985e74353fe4e16c12492b 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -270,7 +270,7 @@ test "Type.Union from empty regular enum" { } test "Type.Union from empty Type.Enum" { - const E = @Enum(u0, .exhaustive, &.{}, &.{}); + const E = @Enum(noreturn, .exhaustive, &.{}, &.{}); const U = @Union(.auto, E, &.{}, &.{}, &.{}); try testing.expectEqual(@typeInfo(U).@"union".field_names.len, 0); } diff --git a/test/cases/compile_errors/enum_noreturn_backing_type.zig b/test/cases/compile_errors/enum_noreturn_backing_type.zig new file mode 100644 index 0000000000000000000000000000000000000000..d2ca3252ecb427ddf1d52ac76648967438e8ae1c --- /dev/null +++ b/test/cases/compile_errors/enum_noreturn_backing_type.zig @@ -0,0 +1,16 @@ +const E1 = enum(u32) {}; +export fn entry1() void { + const e: E1 = undefined; + _ = e; +} + +const E2 = enum(noreturn) { a, b, c }; +export fn entry2() void { + const e: E2 = undefined; + _ = e; +} + +// error +// +// :1:17: error: empty exhaustive enums must be backed by 'noreturn' +// :7:17: error: non-empty enums cannot be backed by 'noreturn' diff --git a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig index fdf0f9aadd08021eb5a34367e80b93ade4ea1b3b..1dd3dd5b517ae85f4a077c90dbf65697c3b72bf4 100644 --- a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig +++ b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -1,15 +1,23 @@ -pub const E = enum(u31) { A, B, C }; -pub const S = extern struct { - e: E, -}; -export fn entry() void { - const s: S = undefined; - _ = s; +export fn entry1() void { + const E = enum(u31) { A, B, C }; + _ = @sizeOf(extern struct { + x: E, + }); +} +export fn entry2() void { + const E = enum(noreturn) {}; + _ = @sizeOf(extern struct { + x: E, + }); } // error // -// :3:8: error: extern structs cannot contain fields of type 'tmp.E' -// :1:15: note: enum tag type 'u31' is not extern compatible -// :1:15: note: only integers with 0 or power of two bits are extern compatible -// :1:15: note: enum declared here +// :4:12: error: extern structs cannot contain fields of type 'tmp.entry1.E' +// :2:15: note: enum tag type 'u31' is not extern compatible +// :2:15: note: only integers with 0 or power of two bits are extern compatible +// :2:15: note: enum declared here +// :10:12: error: extern structs cannot contain fields of type 'tmp.entry2.E' +// :8:15: note: enum tag type 'noreturn' is not extern compatible +// :8:15: note: 'noreturn' is only allowed as a return type +// :8:15: note: enum declared here diff --git a/test/cases/compile_errors/initialize_empty_union.zig b/test/cases/compile_errors/initialize_empty_union.zig index 2847446b2516a1eb4f968a8bb31b24abc7bd132d..a7945a105b6d973411f6d2e31810ae6e5f7e595e 100644 --- a/test/cases/compile_errors/initialize_empty_union.zig +++ b/test/cases/compile_errors/initialize_empty_union.zig @@ -1,10 +1,10 @@ const EnumInferred = enum {}; -const EnumExplicit = enum(u8) {}; +const EnumExplicit = enum(noreturn) {}; const EnumNonexhaustive = enum(u8) { _ }; const U0 = union {}; const U1 = union(enum) {}; -const U2 = union(enum(u8)) {}; +const U2 = union(enum(noreturn)) {}; const U3 = union(EnumInferred) {}; const U4 = union(EnumExplicit) {}; const U5 = union(EnumNonexhaustive) {}; diff --git a/test/cases/compile_errors/instantiate_empty_enum.zig b/test/cases/compile_errors/instantiate_empty_enum.zig new file mode 100644 index 0000000000000000000000000000000000000000..bc469024c841db6a5cc95dc33785046fe9dc317c --- /dev/null +++ b/test/cases/compile_errors/instantiate_empty_enum.zig @@ -0,0 +1,32 @@ +const E = enum {}; + +export fn entry1() void { + const e: E = undefined; + _ = e; +} + +export fn entry2() void { + const e: E = @enumFromInt(@as(u8, undefined)); + _ = e; +} + +export fn entry3() void { + const e: E = .a; + _ = e; +} + +export fn entry4() void { + const e: E = @enumFromInt(0); + _ = e; +} + +// error +// +// :4:18: error: expected type 'tmp.E', found '@TypeOf(undefined)' +// :4:18: note: cannot coerce to uninstantiable type 'tmp.E' +// :1:11: note: enum declared here +// :9:31: error: use of undefined value here causes illegal behavior +// :14:19: error: enum 'tmp.E' has no member named 'a' +// :1:11: note: enum declared here +// :19:18: error: enum 'tmp.E' has no tag with value '0' +// :1:11: note: enum declared here diff --git a/test/cases/compile_errors/int_from_enum_undefined.zig b/test/cases/compile_errors/int_from_enum_undefined.zig deleted file mode 100644 index 96aaa48c280064a7f886fd583ad97a0359b86477..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/int_from_enum_undefined.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn a() void { - const E = enum {}; - var e: E = undefined; - _ = &e; - _ = @intFromEnum(e); -} - -// error -// -// :5:22: error: cannot use @intFromEnum on empty enum 'tmp.a.E' -// :2:15: note: enum declared here diff --git a/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig index d7a5d355c9d26a565b179d3610255a55bafaaab7..efe1f9157f0c9e6b5552e71cc17fed01b8fe1df6 100644 --- a/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig +++ b/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig @@ -81,6 +81,12 @@ export fn entry15() void { x: *const u32, }); } +export fn entry16() void { + const E = enum(noreturn) {}; + _ = @sizeOf(packed struct { + x: E, + }); +} // error // @@ -114,3 +120,5 @@ export fn entry15() void { // :81:12: error: packed structs cannot contain fields of type '*const u32' // :81:12: note: pointers cannot be directly bitpacked // :81:12: note: consider using 'usize' and '@intFromPtr' +// :87:12: error: packed structs cannot contain fields of type 'tmp.entry16.E' +// :87:12: note: type does not have a bit-packed representation diff --git a/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig index d0e09742b5080e165af9fc105ff62fad177ff993..a479141e5a4ab67e6809cad5c460459bc8babac4 100644 --- a/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig +++ b/test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig @@ -10,6 +10,12 @@ export fn entry1() void { x: *const u32, }); } +export fn entry2() void { + const E = enum(noreturn) {}; + _ = @sizeOf(packed union { + x: E, + }); +} // error // @@ -19,3 +25,5 @@ export fn entry1() void { // :10:12: error: packed unions cannot contain fields of type '*const u32' // :10:12: note: pointers cannot be directly bitpacked // :10:12: note: consider using 'usize' and '@intFromPtr' +// :16:12: error: packed unions cannot contain fields of type 'tmp.entry2.E' +// :16:12: note: type does not have a bit-packed representation diff --git a/test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig index e9c27b7eeafa92db4b461173e196c3ce91eb26a2..d204d18f195dddca616550c57b10465f38c06218 100644 --- a/test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig +++ b/test/cases/compile_errors/reify_type_for_tagged_union_with_no_enum_fields.zig @@ -1,4 +1,4 @@ -const Tag = @Enum(u0, .exhaustive, &.{}, &.{}); +const Tag = @Enum(noreturn, .exhaustive, &.{}, &.{}); const Tagged = @Union(.auto, Tag, &.{ "signed", "unsigned" }, &.{ i32, u32 }, &@splat(.{})); export fn entry() void { const tagged: Tagged = undefined; diff --git a/test/cases/compile_errors/sizeof_alignof_empty_union.zig b/test/cases/compile_errors/sizeof_alignof_empty_union.zig index 58e6aa635088c30cab0c1d7306f088406fcb34d1..1144670907582660a5e8b03224d5aceb61f6de3e 100644 --- a/test/cases/compile_errors/sizeof_alignof_empty_union.zig +++ b/test/cases/compile_errors/sizeof_alignof_empty_union.zig @@ -1,10 +1,10 @@ const EnumInferred = enum {}; -const EnumExplicit = enum(u8) {}; +const EnumExplicit = enum(noreturn) {}; const EnumNonexhaustive = enum(u8) { _ }; const U0 = union {}; const U1 = union(enum) {}; -const U2 = union(enum(u8)) {}; +const U2 = union(enum(noreturn)) {}; const U3 = union(EnumInferred) {}; const U4 = union(EnumExplicit) {}; const U5 = union(EnumNonexhaustive) {}; -- 2.54.0