From 35ef32ffdb670e0c79ecc43d7e6fa163626bc2d2 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 12:58:57 +0300 Subject: [PATCH] =?UTF-8?q?ffi=20M5.A.next:=20checkpoint=20=E2=80=94=20pac?= =?UTF-8?q?k=20feature=20step=201=20done=20(1c.A=20=E2=86=92=201d.B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logs the four commits that closed out step 1 of the variadic heterogeneous type packs feature: - 1c.A: parser-rejection lock-in for `..$args` inside `Closure(...)`. - 1c.B: parser + AST + types.zig `pack_start` representation. - 1d.A: impl-matching concrete-only miss lock-in. - 1d.B: pack-aware impl matching with $args + $R binding through `param_impl_pack_map` and `pack_bindings`. Next step is plan step 2 — runtime `args[$i]` indexing + per-mono mangling — opening the door to body-side pack reflection that step 5 needs to retire the hand-rolled `Into(Block)` impls. Also catches up the 1b entry which the prior session left uncommitted in the working tree. --- current/CHECKPOINT-FFI.md | 142 +++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index 97540a1..4b18b0e 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -6,8 +6,148 @@ add a test and make it pass — that's two commits). ## Last completed step +**M5.A.next.1d.B — variadic heterogeneous type packs: pack-aware impl +matching** (commit `08feb60`). + +Pack-shaped impls (`impl P(...) for Closure(..$args) -> $R`) now +match concrete closure sources at xx resolution time. Concrete +impls retain priority — pack matching only fires when +`param_impl_map` misses on the concrete key. + +New plumbing in [src/ir/lower.zig](../src/ir/lower.zig): + +- `PackParamImplEntry` carries the pack-shaped source TypeId plus + the pack-var and ret-var names extracted from the impl AST's + `target_type_expr`. `registerParamImpl` detects pack-shaped + sources via `pack_start != null` on the resolved closure type + and additionally registers in a new `param_impl_pack_map` + keyed by `"Proto\x00"` (no source suffix). +- `tryUserConversion` re-shapes the lookup so the pack path runs + on miss. `tryPackImplMatch` walks the pack entries, verifies + the source's fixed prefix matches the impl's prefix, binds + the pack-var to the source's tail param TypeIds, binds the + ret-var (when the impl's return is generic) to the source + return, and monomorphises the convert method. Mangled name + stays keyed on the concrete source so distinct call shapes + monomorphise separately. +- `pack_bindings: ?StringHashMap([]const TypeId)` is saved / + restored around monomorphisation, mirroring `type_bindings`. +- `resolveClosureTypeWithBindings` handles the `closure_type_expr` + node during type resolution: when the closure carries a + `pack_name` AND `pack_bindings` has a binding for it, the + bound TypeIds are appended after the fixed prefix and the + result is a concrete (non-pack) closure type — so the impl + body's `self: Closure(..$args) -> $R` substitutes to the + concrete source closure during monomorphisation. + +`examples/155-pack-impl-match.sx` flips from the +"no Into(Block) for cl_s32_bool__bool" lock-in diagnostic to +"pack impl match ok": one user-declared +`impl Into(Block) for Closure(..$args) -> $R` covers a +`Closure(s32, bool) -> bool` source that stdlib has no +hand-rolled impl for. The constructed Block isn't invoked +(invoke=null) — the test exercises matching + monomorphisation, +not the trampoline (step 5 of the plan). + +Same-file duplicate pack impls diagnose at registration; +cross-module pack-impl visibility and multi-pack-impl +specificity are deferred (matching the concrete path's existing +TODOs). + +193/193 example tests + `zig build test` green. Step 1 of the +pack-feature plan ("Parser + type rep + impl matching") is now +done. + +**Next step** — Step 2 of the plan: runtime indexing +(`args[$i]`) lowers to positional access; per-mono mangling +extends with a stable pack-shape hash. Builder fns receive +`$args` (a comptime `[]Type`) as a regular value parameter. +Replaces a hand-rolled Into impl in stdlib once step 2 + step +3 (type-reflection intrinsics) land. + +--- + +**M5.A.next.1d.A — pack impl matching: lock in concrete-only miss** +(commit `ce3c2fe`). + +Pinned today's matching behaviour ahead of 1d.B. A user-declared +`impl Into(Block) for Closure(..$args) -> $R` registers under a +pack-shaped source key in `param_impl_map`; the xx site mangles +the concrete `Closure(s32, bool) -> bool` source and finds +nothing → the existing focused diagnostic fires ("no `Into(Block) +for cl_s32_bool__bool` impl — add a per-signature +`__block_invoke_` trampoline + Into impl..."). The pack impl +is reachable in the file but never considered. + +`examples/155-pack-impl-match.sx` captures the rejection at line +43 column 21 (the `xx cl : *Block` site). 193/193 example tests ++ `zig build test` green. + +--- + +**M5.A.next.1c.B — pack type rep: Closure(..$args) parses + interns** +(commit `6582449`). + +`parseTypeExpr`'s `Closure(...)` arm now accepts a trailing +`..$name` (sigil optional) as a variadic-pack marker. Pack must +be terminal — `)` is the only token accepted after the name. +`ClosureTypeExpr` AST gains `pack_name: ?[]const u8` carrying +the identifier so later slices can name the binding. + +`FunctionInfo` / `ClosureInfo` in `src/ir/types.zig` grow a +`pack_start: ?u32 = null` field. `Closure(..$args) -> R` interns +as `params = []`, `pack_start = Some(0)` — distinct from any +concrete `Closure(...) -> R` shape thanks to updated hash/eql +arms. New constructor pair `closureTypePack` / +`functionTypePack` keeps the existing single-shape constructors +unchanged. + +`type_bridge.resolveClosureType` calls `closureTypePack` when +`pack_name != null`. The pack starts after the fixed prefix, +so `Closure(Prefix, ..$args)` resolves with `params = [Prefix]`, +`pack_start = Some(1)`. + +`examples/154-pack-type-rep.sx` flips from rejecting-with-error +to positive parse smoke. 192/192 example tests + `zig build +test` green. + +--- + +**M5.A.next.1c.A — pack type rep: lock in parser rejection** +(commit `bb6eca6`). + +Locked in today's `parseTypeExpr` Closure-arm rejection of `..$args`. +`examples/154-pack-type-rep.sx` uses `..$args` inside a +`Closure(...)` type expression — the pack-shape spelling used by +impl headers like `impl Into(Block) for Closure(..$args) -> $R`. +Today's parser recognized `..$args` only at parameter-list sites +(1b); the Closure type arm called `parseTypeExpr` per position +and hit "expected type name" at line 18 column 26. + +--- + +**M5.A.next.1b — variadic heterogeneous type packs: parser accepts +`..$args`** (commit `a51fe26`). + +`parseParams()` in `src/parser.zig:1558` accepts a leading `..` +before the optional `$` sigil and the parameter name. The old +`args: ..T` form (variadic marker after the colon) still works — +both paths set the same `is_variadic` flag. A pack declaration +`..$args` parses as: +- `is_variadic = true` (leading `..`) +- `is_comptime = true` (the `$` sigil) +- `type_expr = inferred_type` (no `:` annotation) + +`examples/150-pack-parse.sx` flipped from rejecting-with-error to +positive parse smoke. The no-colon branch of `parseParams` +propagates `is_variadic` and `is_comptime` onto the Param +struct, so later slices can read both flags from the parsed AST. +191/191 example tests + `zig build test` green. + +--- + **M5.A.next.1a — variadic heterogeneous type packs: parse lockin** -(this commit). +(`ad82847`). First slice of the `..$args` (variadic heterogeneous type pack) feature per the plan saved at