# 0142 — comptime-minted all-void (fully payloadless) enum > **RESOLVED (2026-06-18).** Two distinct issues were tangled in the original > report (the "binds to `Any`" symptom was a *syntax* misdiagnosis): > > 1. **Real bug:** `defineEnum` (and the new `register_type`) minted a fully > payloadless enum as an all-void `tagged_union`, whose IR size disagrees with > its LLVM size → `verifySizes` panic at codegen. **Fix:** mint a real > `.@"enum"` when every variant is payloadless (`src/ir/interp.zig` > `defineEnum`; `src/ir/compiler_lib.zig` `handleRegisterType` kind 2). > 2. **Missing syntax (the "Any" error):** `EnumType.variant` qualified > construction of a *payloadless* variant wasn't supported (it failed for > hand-written enums too — `field 'X' not found on type 'Any'`, because the > type name lowered to a `Type` value). **Fix:** `src/ir/lower/expr.zig` > `lowerFieldAccess` now recognises a bare `Enum.variant` payloadless literal > (mirroring the `alias.Enum.variant` namespace path), via the new > `isPayloadlessVariant`. Payload-carrying variants keep their call form > (`Shape.circle(2.0)`). > > Regression tests: `examples/0632-comptime-metatype-make-enum-payloadless.sx` > (make_enum all-void), `examples/0187-types-enum-qualified-variant.sx` (qualified > construction), `examples/0631`/`0633`/`0634` (compiler-API minted enums, bare + > namespaced import). ## Symptom (as originally — partly a syntax misdiagnosis; see banner) A comptime type-fn that mints a **fully payloadless** enum (every variant tagless, `payload = void`) via `make_enum` / `declare` + `define` returns a type whose alias binds to `Any` instead of the minted enum — so any later use of the alias as a type fails with `field '' not found on type 'Any'`. - **Observed:** `Suit :: make_suit()` (all-void variants) → `Suit.spades` errors `field 'spades' not found on type 'Any'`. - **Expected:** `Suit` is the minted enum; `Suit.spades` constructs the variant (exactly as it does when at least one variant carries a payload). The type **is** minted correctly — reflecting it through the comptime compiler API shows `kind = 2` (enum) and the right variant count; only the type-fn's **return value / alias binding** is wrong. A *mixed* variant list (≥1 non-void payload) works end-to-end; only the all-void case fails. This is independent of the new `register_type` write API — it reproduces with the shipped metatype `make_enum`. ## Reproduction ```sx #import "modules/std.sx"; #import "modules/std/meta.sx"; make_suit :: () -> Type { return make_enum("Suit", EnumVariant.[ EnumVariant.{ name = "hearts", payload = void }, EnumVariant.{ name = "spades", payload = void }, ]); } Suit :: make_suit(); main :: () { s := Suit.spades; if s == { case .hearts: { print("hearts\n"); } case .spades: { print("spades\n"); } } } ``` Run: `./zig-out/bin/sx run repro.sx` → `error: field 'spades' not found on type 'Any'`. Contrast (works — one variant carries a payload): ```sx make_lvl :: () -> Type { return make_enum("Lvl", EnumVariant.[ EnumVariant.{ name = "info", payload = void }, EnumVariant.{ name = "fatal", payload = i64 }, // ← non-void makes it work ]); } Lvl :: make_lvl(); ``` (This is why `examples/0620-comptime-metatype-make-enum.sx` passes — its variant list is mixed, not all-void.) ## Investigation prompt A comptime type-fn returning a *fully payloadless* enum (`define` → `tagged_union` with every field `ty == .void`) binds the result alias to `Any` (`TypeId` 13) instead of the minted type, even though the type is correctly registered in the table (findable by name; reflects as kind=2). A mixed list (≥1 non-void payload) returns the correct `TypeId`. Find why the all-void case yields `Any`. Suspected area: - `src/ir/lower/comptime.zig` `runComptimeTypeFunc` / `evalComptimeType` — the result path. `runComptimeTypeFunc` already special-cases a zero-FIELD `tagged_union` (declared-but-never-defined); check whether an all-void (non-zero-field) `tagged_union`/`enum` is being normalized, rejected, or coalesced to `.any` somewhere on the way back. Print the `TypeId` returned by `result.asTypeId()` for the all-void vs mixed case to localize. - `src/ir/interp.zig` `defineEnum` (≈2157) / `defineType` — what TypeId/`Value` it returns for an all-void variant set; whether an all-void `tagged_union` interns/dedupes to a builtin (note: a 2-variant all-void union has *no payload storage*, so its structural key may collide with something — or `replaceKeyedInfo` may leave the handle pointing at a coalesced slot). - Whether an all-void payloadless enum should mint as `.@"enum"` (payloadless) rather than an all-void `.tagged_union` in the first place — and whether the `.any` leak is downstream of that representation choice. Likely fix: ensure the type-fn returns the real minted `TypeId` for an all-void payloadless enum (don't coalesce/normalize it to `.any`), or mint it as a proper `.@"enum"`. Whatever the cause, surface it — never silently substitute `.any`. Verification: run the repro above → expect `spades` printed (exit 0). Also confirm `examples/0620` still passes and add an all-void variant case as a regression example. ## Context (why this was hit) Surfaced while building the comptime compiler-API **write side** (Phase 3 of `current/PLAN-COMPILER-VM.md`): `register_type(handle, kind, members)` minting an **actual payloadless enum** (`kind = 2 → .@"enum"`). The new write API mints the type correctly (reflection confirms kind=2/count=2), but the *alias binding* of a fully-payloadless minted type hits this pre-existing metatype bug — so the "actual enum" example can't be verified end-to-end until this is fixed. The `register_type` work (struct, tagged_union with payloads, and the mutually-recursive A↔B graph) is otherwise working; it is **uncommitted**, paused pending this fix.