From 85c1b85f8b3c1aa96cc4e01971fc71f220fb3da0 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 17 Jun 2026 07:58:34 +0300 Subject: [PATCH] =?UTF-8?q?docs(metatype):=20comptime=20List=20growth=20?= =?UTF-8?q?=E2=80=94=20two-layer=20root=20cause=20documented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigated the last deferred enhancement. List(T).append at comptime fails in two independent layers (both reproduce with plain List(i64); List works via #run because that evaluates at emit time, after lowering): 1. null comptime allocator — defaultContextValue looks up the CAllocator->Allocator thunks by name, but they aren't lowered at scanDecls time. Fixable by forcing getOrCreateThunks before the interp runs in runComptimeTypeFunc (tried, works for this layer). 2. struct_get through a *T slot_ptr chain (the *List receiver) — the deep part; comptime pointer/struct/slot resolution, its own session. Speculative fixes reverted (no end-to-end win without layer 2). --- current/CHECKPOINT-METATYPE.md | 42 ++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/current/CHECKPOINT-METATYPE.md b/current/CHECKPOINT-METATYPE.md index 32e22189..9387e546 100644 --- a/current/CHECKPOINT-METATYPE.md +++ b/current/CHECKPOINT-METATYPE.md @@ -115,12 +115,34 @@ shifts every `.ir` snapshot. On-demand import keeps the prelude clean. The reflect/construct triad is COMPLETE — `` `enum `` (`0619`), `` `struct `` (`0622`), `` `tuple `` (`0623`) all reflect AND construct + round-trip. Remaining METATYPE work is ONE deferred enhancement, a clean diagnostic rather than a crash: -- **Comptime `List` growth** — `List(EnumVariant).append` at comptime bails - ("struct_get: base has no fields"); the allocator/List protocol path isn't fully - interp-evaluable (heap alloc + protocol dispatch + List internals at comptime — - a deeper, multi-step interp effort). Probe `.sx-tmp/probe_makeenum.sx`. Doesn't - block anything: array-literal locals already work for building variant lists - (`examples/0620`/`0624`). +- **Comptime `List` growth** — `List(T).append` at comptime bails ("struct_get: + base has no fields"). Doesn't block anything: array-literal locals already build + variant lists (`examples/0620`/`0624`). Probe `.sx-tmp/probe_makeenum.sx` / + `probe_li64.sx`. **Investigated — it's TWO layers** (both reproduce with plain + `List(i64)`, not metatype-specific; List works via `#run` because that evaluates + at EMIT time, after everything is lowered, while a metatype `::` const evaluates + at `scanDecls` time): + 1. **Null comptime allocator.** `interp.zig:defaultContextValue` builds the + comptime `context.allocator` by looking up `__thunk_CAllocator_Allocator_alloc_bytes` + by name in the module's functions — but at `scanDecls` time those protocol + thunks aren't lowered yet, so `alloc_fn`/`dealloc_fn` are `.null_val` and any + comptime allocation fails. FIX (tried, works for this layer): call + `self.getOrCreateThunks("Allocator", "CAllocator")` (guarded by the same + Context/Allocator/CAllocator-registered check `emitDefaultContextGlobal` uses) + before the interp runs in `comptime.zig:runComptimeTypeFunc`. + `createProtocolThunk` saves/restores builder state, so calling it mid-lowering + is safe. After this, `alloc_fn=func_ref` — but layer 2 still bails. + 2. **`struct_get` through a `*T` slot_ptr chain.** A `*List` struct receiver + (`vs.append(…)` → `append(self: *List, …)`) lands in the interp as a slot + whose contents are a slot_ptr to the actual value — `self.field` does + `struct_get` on `base=slot_ptr field_index=1` and bails. The auto-deref in + `interp.zig:.struct_get` does a single `loadSlot`; a chain-resolve loop did + NOT fix it (the final loaded value is a field-pointer aggregate that + `resolveFieldLoad` turns back into a slot_ptr — List's comptime representation + uses field-pointers + slot_ptrs the struct_get path doesn't fully resolve). + This is the deep part: comptime pointer/struct/slot resolution for `*T` + receivers, its own focused effort. Both speculative fixes were REVERTED (no + end-to-end testable win without layer 2). The metatype surface (declare/define/type_info/field_type + make_enum) is feature-complete for the locked design; generic type-fn body locals now work too. - ~~**Validation + loud diagnostics**~~ — COMPLETE. duplicate variant names @@ -144,9 +166,11 @@ capabilities would let the variant list be built more freely; both error cleanly an array from a `{ptr,len}` slice, folded open-ended `hi` to a fixed array's static length at lower time (no runtime/.ir change), and added `interp.zig:subsliceElements`. `examples/0621` locks it. -- **Comptime `List` growth.** Building variants via `List(EnumVariant).append` at - comptime bails ("struct_get: base has no fields") — the allocator/List protocol - path isn't fully interp-evaluable. Probe `.sx-tmp/probe_makeenum.sx`. +- **Comptime `List` growth.** `List(T).append` at comptime bails ("struct_get: + base has no fields"). Investigated — two layers (null comptime allocator at + scanDecls + `struct_get` through a `*T` slot_ptr chain); see the detailed writeup + under "Next step". Layer 1 has a known fix; layer 2 is deep. Probe + `.sx-tmp/probe_makeenum.sx`. - ~~Generic type-fn body locals~~ — DONE. A generic `($T) -> Type` now comptime-evaluates its FULL body (prelude statements + return), so a local before the return resolves. `createComptimeFunctionWithPrelude` +