diff --git a/build.zig b/build.zig index c9928bd8..cc7d92e4 100644 --- a/build.zig +++ b/build.zig @@ -244,8 +244,19 @@ pub fn build(b: *std.Build) void { "comptime-flat", "Default comptime evaluation to the flat-memory VM (legacy interp as fallback)", ) orelse false; + // `-Dcomptime-flat-strict` (or env `SX_COMPTIME_FLAT_STRICT`): run EVERY comptime + // eval on the VM with NO legacy fallback — a VM bail becomes a build-gating error + // naming the reason. The enumeration gate for retiring `interp.zig`: when the + // corpus is green under strict mode, the VM handles everything and legacy can be + // deleted. Implies `comptime_flat`. + const comptime_flat_strict = b.option( + bool, + "comptime-flat-strict", + "Run all comptime eval on the VM with NO fallback; a bail is a hard error (interp-retirement gate)", + ) orelse false; const build_opts = b.addOptions(); build_opts.addOption(bool, "comptime_flat", comptime_flat); + build_opts.addOption(bool, "comptime_flat_strict", comptime_flat_strict); mod.addOptions("build_opts", build_opts); const mod_tests = b.addTest(.{ diff --git a/current/CHECKPOINT-COMPILER-API.md b/current/CHECKPOINT-COMPILER-API.md index 1545fef2..203f13bc 100644 --- a/current/CHECKPOINT-COMPILER-API.md +++ b/current/CHECKPOINT-COMPILER-API.md @@ -373,6 +373,35 @@ when reached (sentinels or accessor fns; see the design doc Risks). `List` growth; orthogonal, see `current/CHECKPOINT-METATYPE.md`.) ## Log +- **Phase 4 — STRICT no-fallback mode (the interp-retirement enumeration gate) + full gap list (2026-06-18).** + Added `-Dcomptime-flat-strict` / env `SX_COMPTIME_FLAT_STRICT` (implies `comptime_flat`): at all + THREE comptime sites (type-fn in `lower/comptime.zig`, const-init + `#run` in `emit_llvm.zig`) a VM + bail becomes a build-gating error naming the reason INSTEAD of falling back to legacy. This forces + every comptime eval onto the VM so the complete gap set is enumerable in one sweep; when the corpus + is green under strict mode AND every example MATCHES legacy, the VM handles everything and + `interp.zig` can be deleted (4F). Default behaviour unchanged — **699/0 both default gates**. + (Fixed a wiring bug: the type-fn site's local `comptime_flat` didn't include the strict flag, so + every type-fn falsely reported ``; now strict implies flat there too.) + **THE DELETION CHECKLIST (19 strict bails, swept via `SX_COMPTIME_FLAT_STRICT=1` over examples+issues; + 0103/0800 "WRONG" were false positives — raw heap-pointer addresses the corpus normalizes):** + - `switch_br` (5): 0114, 0521, 0522, 0524, 1035 — port the type-category multi-way branch (trivial + jump). **CAUTION:** porting it (+`type_name`) UNMASKS a silent-wrong in 0114 — a `[]Type` slice + materialized when a pack (`$args`) is passed ACROSS A CALL reads its `string` element as + ``. Must fix that VM pack-Type-materialization bug, not just add the op. + - `compiler_call` (6): 0602, 0603, 1604, 1609, 1611, 1615 — the **BuildOptions → `abi(.zig) extern + compiler`** migration (delete `#compiler`/`compiler_call`; thread `BuildConfig` into the VM). Big. + - `out` (2): 0613, 1038 — comptime print. Direct write to fd 1, BUT only safe when the WHOLE eval is + VM-handled (a print-then-bail double-prints under the legacy re-run — 0613). Flip atomically. + - `type_name` (1): 0520 — reflection reader (`.type_value` word / Any-box tag → `table.typeName`). + - `global_addr` (1): 0600 — only `&__sx_default_context` is materialised (mirror legacy). + - `interp_print_frames` (1): 1034 — return-trace frame printing. + - VM-native diagnostics (4B) (2): 1179, 1180 — NEGATIVE tests; the VM bail (`define: enum has no + variants` / `duplicate variant name`) IS the expected outcome → must surface as the proper + build-gating diagnostic, not the generic strict error. + - dlsym not found (1): 1654 — a target-specific `extern` (asm global) called at comptime; likely a + legitimately-unresolvable case → confirm it stays a clean diagnostic. + **Sweep command:** `SX_COMPTIME_FLAT_STRICT=1 ./zig-out/bin/sx run ` per example, diff vs legacy; + a strict bail prints `... bailed on the VM (strict, no fallback): `. - **Phase 4D.2 (VM plan) — extern SLICE/string args (→ NUL-terminated `char*`) + float guards (2026-06-18).** Extracted `marshalExternArg`: a scalar/pointer WORD passes verbatim (a `cstring` arg already works as a pointer word via 4D.1); a `string`/slice `{ptr,len}` fat pointer is copied into a diff --git a/src/ir/emit_llvm.zig b/src/ir/emit_llvm.zig index 9e3b62cc..57721cae 100644 --- a/src/ir/emit_llvm.zig +++ b/src/ir/emit_llvm.zig @@ -127,6 +127,11 @@ pub const LLVMEmitter = struct { // for porting the next ops. Default OFF. comptime_flat_trace: bool = false, + // When set (`-Dcomptime-flat-strict` / env `SX_COMPTIME_FLAT_STRICT`), a VM bail + // does NOT fall back to the legacy interpreter — it becomes a build-gating error. + // The enumeration gate for retiring `interp.zig`. Implies `comptime_flat`. + comptime_flat_strict: bool = false, + // Allocator for temporary bookkeeping alloc: Allocator, @@ -337,8 +342,10 @@ pub const LLVMEmitter = struct { .frame_str_cache = std.StringHashMap(c.LLVMValueRef).init(alloc), // Enabled by the `-Dcomptime-flat` build flag OR the `SX_COMPTIME_FLAT` // env var (either turns it on); default OFF (legacy interpreter). - .comptime_flat = build_opts.comptime_flat or std.c.getenv("SX_COMPTIME_FLAT") != null, + .comptime_flat = build_opts.comptime_flat or std.c.getenv("SX_COMPTIME_FLAT") != null or + build_opts.comptime_flat_strict or std.c.getenv("SX_COMPTIME_FLAT_STRICT") != null, .comptime_flat_trace = std.c.getenv("SX_COMPTIME_FLAT_TRACE") != null, + .comptime_flat_strict = build_opts.comptime_flat_strict or std.c.getenv("SX_COMPTIME_FLAT_STRICT") != null, }; } @@ -878,6 +885,13 @@ pub const LLVMEmitter = struct { std.debug.print("[comptime-vm] fallback run '{s}': {s}\n", .{ fname, comptime_vm.last_bail_reason orelse "" }); } const result = vm_result orelse fallback: { + // Strict mode: NO fallback — a VM bail is a build-gating error naming + // the reason (the interp-retirement enumeration gate). + if (self.comptime_flat_strict) { + std.debug.print("error: comptime `#run` ({s}) bailed on the VM (strict, no fallback): {s}\n", .{ fname, comptime_vm.last_bail_reason orelse "" }); + self.comptime_failed = true; + break :fallback Value.void_val; + } // The VM bailed: discard any return-trace frames it pushed before // bailing (`sx_trace_push` is a side effect on the shared buffer), // else the legacy re-run double-pushes them (see 1035). @@ -982,6 +996,14 @@ pub const LLVMEmitter = struct { } } const result = vm_result orelse fallback: { + // Strict mode: NO fallback — a VM bail is a build-gating error + // (the interp-retirement enumeration gate). + if (self.comptime_flat_strict) { + const gname = self.ir_mod.types.getString(global.name); + std.debug.print("error: comptime init of '{s}' bailed on the VM (strict, no fallback): {s}\n", .{ gname, comptime_vm.last_bail_reason orelse "" }); + self.comptime_failed = true; + break :fallback Value.void_val; + } // The VM bailed: discard any return-trace frames it pushed // before bailing, so the legacy re-run doesn't double-push. if (self.comptime_flat) sx_trace_clear(); diff --git a/src/ir/lower/comptime.zig b/src/ir/lower/comptime.zig index b4d36a44..cd5e4b52 100644 --- a/src/ir/lower/comptime.zig +++ b/src/ir/lower/comptime.zig @@ -525,7 +525,9 @@ pub fn runComptimeTypeFunc(self: *Lowering, func_id: FuncId, span: ast.Span) ?Ty // The VM is hardened against malformed lowering-time IR (it BAILS, never // panics; see `comptime_vm.refTy`/`badRef`). Today this is near-pure fallback; // it lights up as `Type` modeling + the VM-native write side land. - const comptime_flat = build_opts.comptime_flat or std.c.getenv("SX_COMPTIME_FLAT") != null; + // Strict mode implies flat (run the VM, then hard-error instead of falling back). + const comptime_flat = build_opts.comptime_flat or std.c.getenv("SX_COMPTIME_FLAT") != null or + build_opts.comptime_flat_strict or std.c.getenv("SX_COMPTIME_FLAT_STRICT") != null; const vm_result: ?interp_mod.Value = if (comptime_flat) comptime_vm.tryEval(self.alloc, self.module, func_id) else @@ -541,6 +543,14 @@ pub fn runComptimeTypeFunc(self: *Lowering, func_id: FuncId, span: ast.Span) ?Ty return checkComptimeTypeResult(self, tid_vm, span); } + // Strict mode: NO fallback — a VM bail is a build-gating failure naming the + // reason (the interp-retirement enumeration gate). Returning null leaves the + // type unresolved → a downstream diagnostic fails the build. + if (build_opts.comptime_flat_strict or std.c.getenv("SX_COMPTIME_FLAT_STRICT") != null) { + std.debug.print("error: comptime type-fn bailed on the VM (strict, no fallback): {s}\n", .{comptime_vm.last_bail_reason orelse ""}); + return null; + } + const result = interp.call(func_id, &.{}) catch |err| { // A comptime type construction (declare/define, reflection) that bails // must surface a build-gating diagnostic naming the reason — NOT poison