comptime VM: Phase 3 — register_type write side + payloadless-enum fixes

The mutating compiler-API, minting types LAZILY at lowering time (single pass,
the existing runComptimeTypeFunc path — so the write side is legacy-only; the
VM isn't wired at lowering time, and the read-side readers stay dual-path):

  declare_type(name) -> Type            forward nominal handle (≈ declare)
  pointer_to(t) -> Type                 build *T references
  register_type(handle, kind, members)  ONE kind-branching fill (≈ unified define)

register_type branches on kind IN THE COMPILER (subsuming define's per-kind
dispatch); codes match type_kind: 1 struct, 2 actual .@"enum", 3 tagged_union,
4 tuple. Members are {name: string, ty: Type}. A non-generic `-> Type` builder is
now flagged is_comptime (decl.zig) so its dead body permits the welded calls.

Graph support: forward declare_type handles + pointer_to express a mutually-
recursive A<->B graph (*A, *B, B-by-value) before bodies are filled. register_type
is idempotent — re-filling a nominal slot (a minting module reached via two import
edges) re-mints identically rather than erroring (nominalIdent reads identity from
any nominal kind).

Fixes (issue 0142):
- A fully payloadless comptime-minted enum was minted as an all-void tagged_union,
  whose IR size disagrees with its LLVM size -> verifySizes panic. Now mints a real
  .@"enum" (register_type kind 2 AND the metatype defineEnum).
- Bare `EnumType.variant` qualified construction of a payloadless variant wasn't
  supported (failed for hand-written enums too — the type name lowered to a Type
  value). Added in lowerFieldAccess via isPayloadlessVariant; payload-carrying
  variants keep their call form.

Examples: 0631 (graph + actual enum + reflection), 0632 (make_enum all-void),
0633/0634/0635 (namespaced / bare / multi-edge import of a minted type), 0187
(qualified variant construction). Unit tests added.

Parity 697/697 (gate OFF and -Dcomptime-flat).
This commit is contained in:
agra
2026-06-18 10:47:36 +03:00
parent 27bc301651
commit 9e3aabcf76
35 changed files with 657 additions and 15 deletions

View File

@@ -325,6 +325,26 @@ when reached (sentinels or accessor fns; see the design doc Risks).
`List` growth; orthogonal, see `current/CHECKPOINT-METATYPE.md`.)
## Log
- **Phase 3 P3.3 (VM plan) — WRITE side: declare_type + pointer_to + ONE kind-branching register_type (2026-06-18).**
The mutating compiler-API: `declare_type(name) -> Type` (forward handle), `pointer_to(t) -> Type`
(build `*T`), and `register_type(handle, kind, members: []Member) -> Type` which branches on
`kind` IN THE COMPILER (subsuming define's per-kind dispatch). Take/return real `Type` values
(matching meta.sx declare/define). **Timing (per user): mint LAZILY at lowering time, single
pass** (the existing `runComptimeTypeFunc`), so the write side is **legacy-only** (`compiler_lib`
handlers) — the VM isn't wired at lowering time, no VM mirror needed; readers stay dual-path.
A non-generic `-> Type` builder is now flagged `is_comptime` (decl.zig) so its dead body permits
the welded calls. **Graph:** forward handles + `pointer_to` express mutually-recursive A↔B (`*A`,
`*B`, B-by-value); `register_type` is **idempotent** (re-fill a nominal slot reached via two
import edges — `nominalIdent`). `kind` codes match `type_kind` (1 struct · 2 actual `.@"enum"` ·
3 tagged_union · 4 tuple). **Fixed two bugs (issue 0142):** (a) a fully payloadless minted enum
was an all-void tagged_union → verifySizes panic; now a real `.@"enum"` (register_type kind 2 AND
metatype `defineEnum`); (b) bare `EnumType.variant` payloadless qualified construction wasn't
supported (failed for hand-written enums too) — added in `lowerFieldAccess` (`isPayloadlessVariant`).
Examples 0631 (graph + actual enum + reflection), 0632 (make_enum all-void), 0633/0634/0635
(namespaced / bare / multi-edge import of a minted type), 0187 (qualified variant construction).
**Parity 697/697** (gate ON and OFF); unit tests added. **Next (P3.4):** re-express
declare/define/type_info as sx over the compiler-API + delete the bespoke interp arms (needs the
VM hardened for lowering-time IR, or the metatype migrated onto the legacy compiler-API calls).
- **Phase 3 P3.2b (VM plan) — kind + enum-value readers: `type_kind` + `type_field_value`; READ side complete (2026-06-18).**
The last two read-only readers the metatype's `type_info(T)` needs (added to
`compiler_lib.bound_fns` AND `Vm.callCompilerFn`, each backed by a `TypeTable` query both

View File

@@ -302,18 +302,36 @@ host through it:
- **READ side now complete:** `find_type` + `type_kind` + `type_field_count` +
`type_field_name` + `type_field_type` + `type_nominal_name` + `type_field_value` cover
everything `reflectTypeInfo` reads.
- **Next (P3.3) — ONE `register_type(info)` write fn (revised direction, 2026-06-18):** per
the user, the mutating side is NOT per-kind (`register_struct`/`register_enum`/…) but a
SINGLE function that takes a type-info value and **branches on the kind in the compiler**,
minting the right `TypeInfo`. This subsumes `define`'s `defineStruct`/`defineEnum`/
`defineTuple` dispatch into one host-side switch. Open design points to resolve when
reached: the flat-memory shape of the `info` argument the sx side passes (a tagged
`{ kind, payload }` over the readers' handle types), the mutable-table / host-ABI-vs-
target-ABI boundary, and pointer-escape/lifetime (escaping field arrays copied into
compiler-owned memory at the boundary). Re-expressing `declare`/`define`/`type_info` as sx
(the metatype, which runs at LOWERING time) still needs the VM hardened against malformed
lowering-time IR first — keep that on the legacy path until then (see the resume note in
CHECKPOINT-COMPILER-API.md).
- **(P3.3) WRITE side — `declare_type` + `pointer_to` + ONE kind-branching `register_type` (DONE).**
The mutating side is a SINGLE `register_type(handle, kind, members)` that branches on `kind`
IN THE COMPILER (subsuming `define`'s `defineStruct`/`defineEnum`/`defineTuple`), plus
`declare_type(name) -> Type` (forward handle) and `pointer_to(t) -> Type` (build `*T`
references). They take/return real `Type` values (matching meta.sx's declare/define).
- **Timing decision (per the user):** mint LAZILY at LOWERING time (single pass, NOT a
pre-emit phase, NOT two-pass) — the existing `runComptimeTypeFunc` path. So the write
side is **legacy-only** (`compiler_lib` handlers); the VM isn't wired at lowering time, so
no VM mirror is needed (the read-side readers stay dual-path for emit-time reflection). A
non-generic `-> Type` builder is now flagged `is_comptime` (`decl.zig`) so its dead body
permits the welded calls (the comptime-only gate).
- **Graph support:** forward `declare_type` handles + `pointer_to` express a
mutually-recursive A↔B graph (`*A`, `*B`, B-by-value) before bodies are filled.
`register_type` is **idempotent** — re-filling a nominal slot (same module reached via two
import edges) re-mints identically instead of erroring (`nominalIdent` reads identity from
any nominal kind). `kind` codes match `type_kind`: 1 struct · 2 enum (actual `.@"enum"`) ·
3 tagged_union · 4 tuple.
- **Two bugs fixed en route** (issue 0142): (a) a fully payloadless comptime-minted enum
was minted as an all-void `tagged_union` → `verifySizes` panic; now mints a real
`.@"enum"` (both `register_type` kind 2 AND the metatype `defineEnum`). (b) bare
`EnumType.variant` qualified construction of a payloadless variant wasn't supported (failed
for hand-written enums too) — added in `lowerFieldAccess` (`isPayloadlessVariant`).
- Examples: `0631` (graph + actual-enum + reflection), `0632` (make_enum all-void),
`0633`/`0634`/`0635` (namespaced / bare / multi-edge import of a minted type), `0187`
(qualified variant construction). Parity 697/697 (gate ON and OFF); unit tests added.
- **Next (P3.4):** re-express `declare`/`define`/`type_info` as sx over the read+write
compiler-API and DELETE the bespoke interp arms — needs the VM hardened against malformed
lowering-time IR first (the metatype runs at lowering time), so either harden + wire the VM
there, or migrate the metatype onto the legacy compiler-API calls first. Decide when reached.
Phase 2 (bytecode) is the orthogonal speed work.
### Phase 3 — Compiler-API on flat memory (resume the stream — no weld)
With native-byte comptime values, re-home the compiler-API: