Skip to content

Commit 088418a

Browse files
committed
Sema: allow empty unions
1 parent 3ee4a00 commit 088418a

File tree

8 files changed

+41
-22
lines changed

8 files changed

+41
-22
lines changed

src/AstGen.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4557,8 +4557,8 @@ fn unionDeclInner(
45574557
wip_members.appendToField(@enumToInt(tag_value));
45584558
}
45594559
}
4560-
if (field_count == 0) {
4561-
return astgen.failNode(node, "union declarations must have at least one tag", .{});
4560+
if (field_count == 0 and (auto_enum_tok != null or arg_node != 0)) {
4561+
return astgen.failNode(node, "tagged union declarations must have at least one tag", .{});
45624562
}
45634563

45644564
if (!block_scope.isEmpty()) {

src/Sema.zig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16891,8 +16891,8 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1689116891
.abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
1689216892
};
1689316893
}
16894-
} else {
16895-
return sema.fail(block, src, "unions must have at least one field", .{});
16894+
} else if (!tag_type_val.isNull()) {
16895+
return sema.fail(block, src, "tagged unions must have at least one field", .{});
1689616896
}
1689716897

1689816898
if (tag_ty_field_names) |names| {
@@ -28722,7 +28722,9 @@ pub fn typeHasOnePossibleValue(
2872228722
const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
2872328723
const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
2872428724
return null;
28725-
const only_field = union_obj.fields.values()[0];
28725+
const fields = union_obj.fields.values();
28726+
if (fields.len == 0) return Value.initTag(.empty_struct_value);
28727+
const only_field = fields[0];
2872628728
if (only_field.ty.eql(resolved_ty, sema.mod)) {
2872728729
const msg = try Module.ErrorMsg.create(
2872828730
sema.gpa,
@@ -29462,7 +29464,7 @@ fn unionFieldAlignment(
2946229464
field: Module.Union.Field,
2946329465
) !u32 {
2946429466
if (field.ty.zigTypeTag() == .NoReturn) {
29465-
return 0;
29467+
return @as(u32, 0);
2946629468
} else if (field.abi_align == 0) {
2946729469
return sema.typeAbiAlignment(block, src, field.ty);
2946829470
} else {

src/print_zir.zig

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,7 @@ const Writer = struct {
14431443
try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag);
14441444

14451445
if (decls_len == 0) {
1446-
try stream.writeAll("{}, ");
1446+
try stream.writeAll("{}");
14471447
} else {
14481448
const prev_parent_decl_node = self.parent_decl_node;
14491449
if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
@@ -1454,15 +1454,20 @@ const Writer = struct {
14541454
extra_index = try self.writeDecls(stream, decls_len, extra_index);
14551455
self.indent -= 2;
14561456
try stream.writeByteNTimes(' ', self.indent);
1457-
try stream.writeAll("}, ");
1457+
try stream.writeAll("}");
14581458
}
14591459

1460-
assert(fields_len != 0);
1461-
14621460
if (tag_type_ref != .none) {
1463-
try self.writeInstRef(stream, tag_type_ref);
14641461
try stream.writeAll(", ");
1462+
try self.writeInstRef(stream, tag_type_ref);
1463+
}
1464+
1465+
if (fields_len == 0) {
1466+
try stream.writeAll("})");
1467+
try self.writeSrcNode(stream, src_node);
1468+
return;
14651469
}
1470+
try stream.writeAll(", ");
14661471

14671472
const body = self.code.extra[extra_index..][0..body_len];
14681473
extra_index += body.len;

src/type.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3096,6 +3096,9 @@ pub const Type = extern union {
30963096
union_obj: *Module.Union,
30973097
have_tag: bool,
30983098
) Module.CompileError!AbiAlignmentAdvanced {
3099+
if (union_obj.fields.count() == 0) {
3100+
return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) };
3101+
}
30993102
const sema_kit = switch (strat) {
31003103
.sema_kit => |sk| sk,
31013104
else => null,

test/behavior/union.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,3 +1301,19 @@ test "noreturn field in union" {
13011301
}
13021302
try expect(count == 5);
13031303
}
1304+
1305+
test "empty union" {
1306+
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1307+
1308+
const U = union {};
1309+
try expect(@sizeOf(U) == 0);
1310+
try expect(@alignOf(U) == 0);
1311+
}
1312+
1313+
test "empty extern union" {
1314+
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1315+
1316+
const U = extern union {};
1317+
try expect(@sizeOf(U) == 0);
1318+
try expect(@alignOf(U) == 1);
1319+
}

test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const Untagged = @Type(.{
22
.Union = .{
33
.layout = .Auto,
4-
.tag_type = null,
4+
.tag_type = enum { a, b },
55
.fields = &.{},
66
.decls = &.{},
77
},
@@ -14,4 +14,4 @@ export fn entry() void {
1414
// backend=stage2
1515
// target=native
1616
//
17-
// :1:18: error: unions must have at least one field
17+
// :1:18: error: tagged unions must have at least one field

test/cases/compile_errors/union_fields_with_value_assignments.zig

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const Foo = union {};
1+
const Foo = union(enum) {};
22

33
// error
44
// backend=stage2
55
// target=native
66
//
7-
// :1:13: error: union declarations must have at least one tag
7+
// :1:13: error: tagged union declarations must have at least one tag

0 commit comments

Comments
 (0)