plan: Phase 4 — retire the legacy interp (ONE-evaluator end state)

Audited the 5 roles interp.zig still serves (A comptime folds, B #insert,
C post-link bundler, D #compiler hooks, E bail diagnostics) and the shared
substrate (Value tagged union + host_ffi bridge).

User decision: UNIFY — the VM gains a host-FFI escape + real-pointer
translation and runs the post-link bundler too; interp.zig fully deleted.

Dependency-ordered sub-phases recorded in PLAN-COMPILER-VM.md:
4A finish comptime ops (box_any/unbox_any, out/print, global_addr, trace) ->
4B VM-native diagnostics -> 4C #insert -> 4D host FFI + #compiler hooks ->
4E post-link bundler (+ dedicated bundle tests) -> 4F flip default + delete
interp.zig/Value + re-express define/make_enum over the compiler-API.

No code change — planning + checkpoint only.
This commit is contained in:
agra
2026-06-18 16:45:25 +03:00
parent 736f64e664
commit 3283effa97
2 changed files with 87 additions and 5 deletions

View File

@@ -34,11 +34,16 @@ with ONE welded mechanism. Branch: `reify` (off `master`). Update after every st
> `0632` runs **HANDLED with ZERO fallback** (incl. the `define(declare, type_info(T))` round-trips
> `0619`/`0622`/`0623`); VM output byte-matches legacy. `enum_init`/`define`/`type_info` bail loudly
> on a `backing_type` tagged union rather than silent-clobber. **697/0 BOTH gates + all unit tests.**
> **THE NEXT STEP:** drive the remaining VM fallbacks (now ALL genuinely-non-metatype emit-time side
> effects — `print`/`out` 0613, `global_addr` 0600, `compiler_call` #compiler hooks 0602/0603,
> inline-asm global 1654) to empty (port or confirm each is legitimately non-comptime), THEN — with
> explicit user go-ahead — flip the VM to default + delete `interp.zig` (the end-state ONE evaluator),
> and re-express `define`/`make_enum` as sx over the compiler-API (allocation works on the sole VM).
> **THE NEXT STEP — Phase 4 (legacy-interp retirement) is now PLANNED in `PLAN-COMPILER-VM.md`.**
> Audited the 5 roles the legacy interp still serves (A comptime folds · B `#insert` · C post-link
> bundler · D `#compiler` hooks · E bail diagnostics). **User decision (2026-06-18): UNIFY** — the VM
> gains a host-FFI escape + real-pointer translation and runs the post-link bundler too; `interp.zig`
> fully deleted (true ONE evaluator). Dependency-ordered sub-phases: **4A** finish comptime ops
> (box_any/unbox_any · out/print · global_addr · trace) → **4B** VM-native diagnostics → **4C**
> `#insert` → **4D** host FFI + `#compiler` hooks → **4E** bundler (+ dedicated bundle tests, no
> corpus guard) → **4F** flip default + delete `interp.zig`/`Value` + re-express `define`/`make_enum`.
> Starting at **4A.1 (box_any/unbox_any)**. See `PLAN-COMPILER-VM.md` → Phase 4 for the full plan +
> top risks (flat-pointer escape on buffer realloc; bundler test coverage).
> Earlier landed: dedicated `Type` builtin TypeId (`6844fb9`/`94f60c5`/`554871b`); WRITE side
> declare_type/register_type/pointer_to VM-native (`66005af`); real lowering-time Context (`eb68d9e`);
> metatype construction declare/define/enum_init (`d0ebc55`).

View File

@@ -360,6 +360,83 @@ With native-byte comptime values, re-home the compiler-API:
**Verification:** the metatype + `#compiler` surfaces are gone, re-expressed as sx over
the exposed compiler-API; full corpus green.
### Phase 4 — Retire the legacy interp (the ONE-evaluator end state)
The metatype CONSTRUCTION + REFLECTION surface is VM-native (steps 7/8 — `0614``0624`,
`0632` all HANDLED). This phase moves EVERYTHING ELSE off `interp.zig` and deletes it.
**What the legacy interp is still used for (audited 2026-06-18) — five roles:**
| Role | Wired to VM? | Site |
|------|--------------|------|
| **A. Comptime folds** (type-fn / `::` const-init / `#run`) | ✅ VM + legacy fallback | `comptime.zig:530`, `emit_llvm.zig:871`/`971` |
| **B. `#insert` string eval** | ❌ legacy-only (VM wiring reverted — 0737 malformed-IR crash) | `comptime.zig:634` |
| **C. Post-link bundler** (`platform.bundle` — Info.plist/codesign/process/fs) | ❌ legacy-only | `core.zig:invokeByFuncId` ← `main.zig:769` |
| **D. `#compiler` hooks** (`compiler_call` — BuildOptions/bundling) | ❌ legacy-only; `Value`-based ABI | `compiler_hooks.zig`, `interp.zig:1130` |
| **E. Bail diagnostics** (`Interpreter.last_bail_*` statics) | n/a | `main.zig:464` |
Shared substrate everything traffics in: the **`Value`** tagged union (the
`regToValue`/`valueToReg` bridge + the hooks + `core.zig`) and the **host-FFI bridge**
(`host_ffi.zig` + `interp.callExtern` — dlsym + cdecl trampolines for real libc).
**DECISION (2026-06-18, user): UNIFY.** The VM gains a host-FFI escape + real-pointer
translation and runs BOTH sandboxed comptime folds AND the unsandboxed post-link bundler.
`interp.zig` is fully deleted — true ONE evaluator, two modes (sandboxed / host-effects).
**Remaining comptime-fold gaps** (full corpus fallback inventory — 15 examples; 1179/1180
are legitimate negative-test bails that BECOME VM diagnostics, 1145 is a scan artifact):
`box_any`/`unbox_any` (6), `out`/print (2), `global_addr` (1), trace frames (1),
`compiler_call` (2 — role D).
**Sub-phases (dependency order; each its own session, both gates 697/0 after each):**
- **4A — finish comptime ops (small, parity-guarded).** Drive the fold fallback list to
empty except `compiler_call`:
- **4A.1** `box_any`/`unbox_any`. Word case = alloc 16B `{tag@0, value@8}`, tag =
`source_type.index()` (matches legacy comptime; note runtime `anyTag` normalizes
arbitrary-width ints), value via `writeField(source_type)` (so f32 etc. round-trip);
unbox = `readField(addr+8, target)`. Aggregate-Any payload needs the runtime
pointer-in-value-slot shape (`coerceToI64` alloca+ptrtoint) — implement or bail loudly.
- **4A.2** `out`/print → add a VM output buffer; flush through the same path as
`core.flushInterpOutput`.
- **4A.3** `global_addr` (address-of a global in flat memory).
- **4A.4** trace frames (`sx_trace_*` / `interp_print_frames`).
- **4B — VM-native diagnostics (role E). MUST land before deleting legacy.** Today a VM
bail silently falls back; with legacy gone the VM bail IS the user-facing build-gating
diagnostic. Surface the VM's `detail`/span/file into what `main.zig` renders; turn
1179/1180-style bails into proper diagnostics. No diagnostic may regress.
- **4C — `#insert` on the VM (role B).** Re-wire `evalComptimeString` through `tryEval`;
the lowering-time-IR hardening that forced the 0737 revert is already in place. Verify
the `#insert` corpus parity.
- **4D — host FFI + `#compiler` hooks on the VM (role D + substrate). The big one.**
- **4D.1** Host-FFI escape in `Vm.invoke`: for an extern/bodyless callee not modeled by
`callMemBuiltin`, route to `host_ffi` trampolines — marshal each arg (scalar word, or
flat `Addr` → real host pointer `machine.mem.items.ptr + addr`), call, map the return.
**Central hazard:** flat-memory buffer realloc-on-growth invalidates a live host
pointer — PIN the buffer (or copy) while a host pointer is outstanding. (Note: `#run`
already calls host extern via the interp today, so host effects at comptime are not a
new policy.)
- **4D.2** `compiler_call`: route to the hook registry. Transitional shim first — convert
flat↔`Value` at the boundary (`regToValue`/`valueToReg`) so hooks work unchanged; then
(in 4F) rewrite `HookFn` to a flat-memory ABI to drop `Value`.
- **4E — post-link bundler on the VM (role C).** Depends on 4D. Route
`core.invokeByFuncId` / `main.zig`'s post-link call through the VM. **No corpus
coverage** (only runs on `sx build --bundle/--apk`) — add dedicated bundle smoke tests
(min `.app` + `.apk`); gate on real bundle builds, not just `zig build test`. Riskiest
phase.
- **4F — flip + delete (the end state).** Flip the VM to default (retire `-Dcomptime-flat`);
delete `interp.zig` (`Interpreter`/`Value`/`defineEnum`…/`reflectTypeInfo`/`callExtern`);
drop the `regToValue`/`valueToReg` bridge; rewrite `HookFn` to the flat-memory ABI
(drops `Value`); simplify `core.zig` (`invokeByFuncId` → VM) + `main.zig` (`last_bail_*`
→ VM diagnostics); remove the dual-path `compiler_lib` handlers (keep only VM-native
`callCompilerFn`); re-express `define`/`make_enum` as sx over the compiler-API (allocation
works on the sole evaluator) and land the original 0141 repro as a corpus test.
**Dependencies:** 4A → (4B, 4C independent) ; 4D → 4E ; all → 4F.
**Top risks:** (1) flat-pointer escape to host FFI on buffer realloc (4D.1); (2) the bundler
has no corpus guard (4E needs dedicated tests); (3) `Value` deletion is gated on the
flat-memory `HookFn` rewrite (4D.2→4F).
## Open questions (resolve as reached, record decisions here)
- **Host-ABI vs target-ABI split.** The compiler runs on the host, so its OWN exposed