comptime: empty-member types are valid for all kinds; keep never-defined declare rejected
A comptime-constructed type with NO members is now VALID for every kind
(empty struct, empty tuple, empty enum, empty tagged_union) — only a bare
`declare("X")` placeholder that is never completed by a matching `define`
stays rejected (it would panic codegen).
- comptime_vm.zig registerTypeVm: drop the blanket "a type with no members
is never valid" rejection. The per-kind loops are vacuous for an empty
member list and the dup-name checks stay correct.
- types.zig TaggedUnionInfo: add `defined: bool = true`. Every real
construction (normal unions, error sets, register_type completion) is
"defined" by default; only the two declare-PLACEHOLDER sites set it false:
comptime_vm.declareNominal and lower/comptime.preregisterForwardTypes.
- lower/comptime.checkComptimeTypeResult: reject on `!defined` (never-defined
placeholder) instead of `fields.len == 0`, so an explicitly-defined empty
union passes through while a never-completed declare is still gated.
- types.zig typeSizeBytes(tagged_union): floor the payload area at 8 bytes
when no field carries a payload, mirroring the LLVM lowering — fixes a
verifySizes panic on an empty/all-void tagged_union (IR sized to tag-only,
LLVM laid out tag + [8 x i8]).
Tests:
- examples/1179: repurposed from "empty enum rejected" (now valid) to the
never-defined `declare` case (the remaining rejection); preserves its
issue-0140 regression role.
- examples/1180 (duplicate variant): still rejected, unchanged output.
- examples/0641 (new): construct empty struct/tuple/enum/tagged_union via
define/declare; instantiate the constructible ones; exit 0.
This commit is contained in:
@@ -1801,7 +1801,12 @@ pub const Vm = struct {
|
||||
var members = std.ArrayList(NamedMember).empty;
|
||||
defer members.deinit(self.gpa);
|
||||
try self.decodeMemberSlice(table, members_word, slice_ty, &members);
|
||||
if (members.items.len == 0) return self.failMsg("comptime register_type: a type with no members is never valid");
|
||||
// A comptime-constructed type with NO members is VALID for every kind
|
||||
// (empty struct / tuple / enum / tagged_union). The per-kind loops below
|
||||
// are vacuous for an empty member list and the dup-name checks stay
|
||||
// correct. The completion always sets `defined = true`, so the result is
|
||||
// distinguishable from a never-completed `declare(...)` placeholder
|
||||
// (which carries `defined = false`).
|
||||
|
||||
const tbl = @constCast(table);
|
||||
// The slot's nominal identity — accept the forward `tagged_union` from
|
||||
@@ -1853,7 +1858,7 @@ pub const Vm = struct {
|
||||
const tbl = @constCast(table);
|
||||
const name_id = tbl.internString(text);
|
||||
if (tbl.findByName(name_id)) |existing| return existing;
|
||||
return tbl.internNominal(.{ .tagged_union = .{ .name = name_id, .fields = &.{}, .tag_type = .i64 } }, 0);
|
||||
return tbl.internNominal(.{ .tagged_union = .{ .name = name_id, .fields = &.{}, .tag_type = .i64, .defined = false } }, 0);
|
||||
}
|
||||
|
||||
/// Decode a `[]{ name: string, ty: Type }` slice from comptime memory into interned
|
||||
|
||||
Reference in New Issue
Block a user