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:
@@ -148,6 +148,14 @@ pub const TypeInfo = union(enum) {
|
||||
backing_type: ?TypeId = null, // enum struct backing (e.g. { tag: u32; _: u32; payload: [30]u32; })
|
||||
explicit_tag_values: ?[]const i64 = null, // explicit variant values (e.g., quit :: 0x100)
|
||||
nominal_id: u32 = 0, // stable nominal identity; 0 == structural (legacy)
|
||||
// True for every real construction (normal unions, error sets, and a
|
||||
// `register_type`/`define` completion). False ONLY for a `declare(...)`
|
||||
// forward PLACEHOLDER that has not yet been completed — a 0-field
|
||||
// tagged_union that is indistinguishable from an explicitly-defined
|
||||
// empty union by field count alone. `checkComptimeTypeResult` rejects
|
||||
// `defined == false` (declared but never defined) while accepting a
|
||||
// legitimately-empty `defined == true` union.
|
||||
defined: bool = true,
|
||||
};
|
||||
|
||||
pub const ArrayInfo = struct {
|
||||
@@ -879,6 +887,12 @@ pub const TypeTable = struct {
|
||||
const fs = self.typeSizeBytes(f.ty);
|
||||
if (fs > max_payload) max_payload = fs;
|
||||
}
|
||||
// Mirror the LLVM lowering (backend/llvm/types.zig): the payload
|
||||
// area is laid out as `[max_size x i8]` with a floor of 8 when no
|
||||
// field carries a payload (all-void / empty union). Without this
|
||||
// floor an empty/all-void tagged_union sizes to tag_size only,
|
||||
// diverging from the LLVM type and tripping verifySizes.
|
||||
if (max_payload == 0) max_payload = 8;
|
||||
const tag_size = self.typeSizeBytes(u.tag_type);
|
||||
const raw = max_payload + tag_size;
|
||||
break :blk (raw + 7) & ~@as(usize, 7);
|
||||
|
||||
Reference in New Issue
Block a user