From 3c0e0852a835f12a5c350b94e232858065e1b74f Mon Sep 17 00:00:00 2001 From: agra Date: Thu, 18 Jun 2026 14:41:33 +0300 Subject: [PATCH] =?UTF-8?q?issue=200141:=20re-refine=20root=20cause=20?= =?UTF-8?q?=E2=80=94=20IR=20is=20correct;=20it's=20a=20legacy=20slot=5Fptr?= =?UTF-8?q?=20chain=20+=20null=20comptime=20allocator=20(VM=20has=20neithe?= =?UTF-8?q?r=20failure=20mode)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...mptime-list-growth-in-type-construction.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/issues/0141-comptime-list-growth-in-type-construction.md b/issues/0141-comptime-list-growth-in-type-construction.md index 0972323c..1337a77b 100644 --- a/issues/0141-comptime-list-growth-in-type-construction.md +++ b/issues/0141-comptime-list-growth-in-type-construction.md @@ -123,6 +123,47 @@ i.e. what about the generic `List(T)` instantiation is incomplete then that flip the field-access decision (start at `lower/expr.zig:lowerFieldAccess` / `lowerFieldAccessOnType` and the `*T`-receiver path). +## Root cause — RE-REFINED (2026-06-18): the IR is CORRECT; it's a legacy slot_ptr chain + null comptime allocator + +Direction 1's premise is **wrong** — the IR is NOT mis-lowered. Instrumented +`lowerFieldAccess` (the `*T`-receiver auto-deref) on the repro: `list.len` lowers +with `obj_ty = *List__EnumVariant` (kind=**pointer**), so the auto-deref fires +(`obj = load(list, List); struct_get(obj, len_idx)`) — **byte-identical** to the +runtime/`#run` shape. There is no `struct_get`-vs-`struct_gep` divergence in the +READ path (field reads ALWAYS lower to load+`struct_get` via `expr.zig:~915`; +`struct_gep` is only the WRITE/lvalue path — the "38 hits" above were writes). + +The bail is purely in the **legacy interp's slot_ptr semantics**. Instrumented the +`.struct_get` arm: for `list.len` the base is a `slot_ptr`, and the arm's own +`slot_ptr` auto-deref (`loadSlot`) yields **another `slot_ptr`** (a `*List` param +→ slot_ptr → slot_ptr chain), which no `switch(base)` case resolves → "base has no +fields". So `load(*List)` produced a slot_ptr the interp can't flatten to the +aggregate at this call shape. + +There is a SECOND, independent blocker that survives even if the slot_ptr chain is +fixed: `List.append` grows on the first call (cap 0→4) → `context.allocator.alloc_bytes` +→ `call_indirect` on a **null `alloc_fn`** at lowering time (the comptime allocator +is null at type-construction time — confirmed in BOTH evaluators; see the +CHECKPOINT-COMPILER-API 2026-06-18 entry). So the repro needs BOTH the slot_ptr +chain AND the comptime allocator fixed. + +**Strategic implication (2026-06-18).** Both blockers are LEGACY-interp issues +(slot_ptr chains; null comptime allocator dispatch — "raw fn-pointers from extern +calls aren't dispatchable in interp"). The flat-memory comptime VM (`comptime_vm.zig`) +has neither failure mode by construction: it uses flat byte memory (no slot_ptr +chains) and models `malloc`/`call_indirect` natively. So the principled fix is NOT +to patch the legacy interp (code slated for deletion), but to let the VM evaluate +these type-fns: (a) re-express the metatype `define`/`make_enum` over the +compiler-API so the type-fn body hits NO `call_builtin(define)` (which forces a +legacy fallback today), and (b) make `materializeDefaultContext` lay the REAL +allocator func-refs at lowering time (the global exists by Pass 1c, so the +`layoutConst` path should populate them — verify it handles the inline-protocol +`Allocator` field, which may need a `.protocol`/inline-struct arm). Then a +List-building type-fn runs entirely on the VM. The remaining tension is dual-path +validation: while the legacy still fails, a corpus example can't pass gate-OFF — so +this lands cleanly only alongside the VM-default flip + legacy deletion (the +end-state). Until then it stays a deferred enhancement, not a blocker. + **Plumbing (only after STEP 0 is green):** 1. `scanDecls` (`decl.zig:777` site): instead of `evalComptimeType` now, (a) pre-register a forward nominal slot named `cd.name` + bind the alias