comptime VM: Phase 3 — find_type + type_field_count reflection readers

First read-only compiler-API reflection readers, bound the same way as the
intern/text_of seed (compiler_lib.bound_fns + Vm.callCompilerFn, native on flat
memory, no marshaling). A type handle is a plain u32 TypeId (like StringId), so
both stay clean scalar host-calls:

  find_type(name: StringId) -> TypeId          (TypeTable.findByName; unresolved/0 if absent)
  type_field_count(t: TypeId) -> i64           (new TypeTable.memberCount; loud-bail, no silent 0)

memberCount is the single source both the legacy handler and the VM read, so the
two paths can't drift. find_type returns a non-optional TypeId using the
unresolved(0) sentinel for not-found rather than ?Type — a Type value is
.any-typed (which the flat-memory VM does not represent) and an optional can't
cross the legacy<->VM eval boundary; unresolved is the project-blessed "no type"
marker.

Example 0628 chains intern -> find_type -> type_field_count (+ a not-found
lookup), folded at #run, VM-HANDLED natively. VM unit test added.

Parity 689/689 (gate OFF and -Dcomptime-flat).
This commit is contained in:
agra
2026-06-18 09:25:26 +03:00
parent 0367d96d9b
commit a9302a8b50
10 changed files with 247 additions and 15 deletions

View File

@@ -26,22 +26,28 @@ with ONE welded mechanism. Branch: `reify` (off `master`). Update after every st
> breaks cross-compilation — host vs target layout — and loses the sandbox. A
> flat-memory VM keeps both while getting native bytes + speed.)
>
> **Next action (2026-06-18):** Phase 1.final op-porting is essentially COMPLETE — the VM
> handles **36** real corpus const-inits (0 → 16 → 27 → 31 → 36), with only **2** fallbacks
> left, both principled (`intern` = the welded compiler-API fn, Phase 3; inline-asm global
> `1654`, never comptime-evaluable). Parity **688/688** (gate ON and OFF). The VM now covers
> scalars/control-flow/aggregates/strings/optionals/enums, calls+recursion, the implicit
> context + full allocator protocol, globals, and failables + return traces. BOTH comptime
> call sites (const-init + `#run` side-effects) route through the VM with legacy fallback.
> **The forward work is Phase 2 (bytecode) and Phase 3 (compiler-API on flat memory)**; flipping the VM to
> default + deleting the legacy path awaits those. See `PLAN-COMPILER-VM.md` Phase 1.final
> Status steps 710 (Phase 3 seed: `intern`/`text_of` native on the VM — `0626` handled).
> Build/verify: `zig build && zig build test` (688, gate OFF). Run the corpus ON the VM:
> **Next action (2026-06-18):** **Phase 3 is UNDER WAY.** The VM now hosts the first
> read-only reflection readers — `find_type(name: StringId) -> TypeId` and
> `type_field_count(t: TypeId) -> i64` — bound exactly like the `intern`/`text_of` seed
> (a type handle is a plain `u32` `TypeId`, so the calls stay clean scalar host-calls).
> Example `0628` chains `intern → find_type → type_field_count`, VM-HANDLED natively.
> Parity **689/689** (gate ON and OFF), VM unit test added. Phase 1.final op-porting was
> already complete (the VM covers scalars/control-flow/aggregates/strings/optionals/enums,
> calls+recursion, the implicit context + full allocator protocol, globals, failables +
> return traces); both comptime call sites route through the VM with legacy fallback.
> **Forward (P3.2):** more read-only readers on the same `TypeId`-handle shape
> (`type_name`, `field_name`, `field_type`, kind queries), then `register_struct` (the
> first MUTATING fn — mints a `TypeId`; resolve the mutable-table / host-ABI-vs-target-ABI
> boundary deliberately). Re-expressing `declare`/`define`/`type_info` as sx (the metatype,
> which runs at LOWERING time) needs the VM hardened against malformed lowering-time IR
> first — keep it on the legacy path until then. Phase 2 (bytecode) is the orthogonal
> speed work. **Decision recorded:** `find_type` returns a non-optional `TypeId` using the
> `unresolved` (0) sentinel, NOT `?Type` (a `Type` value is `.any`-typed, which the VM
> doesn't represent, and an optional can't cross the eval bridge) — see `PLAN-COMPILER-VM.md`
> Phase 3 progress note.
> Build/verify: `zig build && zig build test` (689, gate OFF). Run the corpus ON the VM:
> `zig build test -Dcomptime-flat` (the build flag) OR env `SX_COMPTIME_FLAT=1`. Coverage
> trace: `SX_COMPTIME_FLAT_TRACE=1`. **Forward: Phase 3 — grow the compiler-API on the VM**
> (`find_type` / `register_struct` / reflection readers via `Vm.callCompilerFn`, then
> re-express `declare`/`define`/`type_info` as sx and delete the bespoke interp arms);
> Phase 2 (bytecode) is the orthogonal speed work.
> trace: `SX_COMPTIME_FLAT_TRACE=1`.
### (superseded) prior weld resume
Phase 1 done; Phase 2 welded structs were working via reflection + memory-order
@@ -312,6 +318,24 @@ when reached (sentinels or accessor fns; see the design doc Risks).
`List` growth; orthogonal, see `current/CHECKPOINT-METATYPE.md`.)
## Log
- **Phase 3 P3.1 (VM plan) — first read-only reflection readers: `find_type` + `type_field_count` (2026-06-18).**
Two more `compiler`-library fns, bound the same way as the `intern`/`text_of` seed (added
to `compiler_lib.bound_fns` for the legacy handler + the welded-decl export check, AND to
`Vm.callCompilerFn` for the native flat-memory path — NO marshaling). A **type handle is a
plain `u32` `TypeId`** (like `StringId`), so both keep the seed's clean scalar shape:
`find_type(name: StringId) -> TypeId` (`TypeTable.findByName`, `unresolved`/0 if absent) and
`type_field_count(t: TypeId) -> i64` (a NEW `TypeTable.memberCount` query — struct/union/
tagged-union fields, enum variants, array/vector length — called by BOTH paths so they
can't drift; bails loudly, never a silent 0). New example `0628-comptime-compiler-find-type`
chains `intern → find_type → type_field_count` (and a not-found lookup → 0), both folded at
`#run`, both VM-HANDLED natively (trace confirms no fallback). VM unit test added
(`find_type` + `type_field_count`, struct found → 3 fields, missing → `unresolved`).
**Parity 689/689** (gate ON and OFF). **Decision (resolves the plan's `find_type → ?Type`
sketch):** return a NON-optional `TypeId` with the `unresolved` (0) sentinel for not-found,
NOT `?Type` — a `Type` value resolves to `.any` (which the flat-memory VM doesn't represent)
and an optional can't cross the legacy↔VM eval boundary; `unresolved` is the project-blessed
unmistakable "no type" marker. Forward (P3.2): more readers on the same handle shape
(`type_name`/`field_name`/`field_type`/kind), then `register_struct` (first mutating fn).
- **VM robustness — `Frame` bounds-check; lowering-time `#insert` wiring explored + reverted (2026-06-18).**
Explored wiring the VM at the LOWERING-time comptime site (`evalComptimeString`, the
`#insert` string fold). 12/13 `#insert` examples ran on the VM with parity, but `0737`