Commit Graph

2 Commits

Author SHA1 Message Date
agra
08feb6040b ffi M5.A.next.1d.B: pack impl matching — bind $args + $R per call
Pack-shaped impls (`impl P(...) for Closure(..$args) -> $R`) now
match concrete closure sources at xx resolution time. Concrete
impls keep their priority — pack matching only fires on a
concrete-key miss in `param_impl_map`.

New plumbing in 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<arg_mangled>"` (no source suffix).

- `tryUserConversion` re-shapes the concrete 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. Without an
  active binding, the pack shape is preserved.

`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. Constructed Block isn't invoked
(invoke=null) — the test exercises only the matching +
monomorphisation, not the trampoline (step 5 of the plan).

Existing concrete-impl paths unchanged: 95-objc-block-noop,
96-objc-block-multi-arg, and stdlib's hand-rolled
`Into(Block) for Closure(bool) -> void` continue to pass through
the concrete map first. Same-file duplicate pack impls
diagnose at registration; cross-module visibility and
multi-pack-impl specificity stay TODOs (matching the deferred
Phase 5 work on the concrete path).

193/193 example tests + `zig build test` green.
2026-05-27 12:57:45 +03:00
agra
ce3c2fe7bd ffi M5.A.next.1d.A: pack impl matching — lock in concrete-only miss
Step 1d lock-in test pinning today's matching behaviour.
`registerParamImpl` records every impl in `param_impl_map` keyed
by `"Proto\x00<arg_mangled>\x00<src_mangled>"`. For a pack impl
`Into(Block) for Closure(..$args) -> $R` the key contains the
pack-shaped closure's mangle (interns with `pack_start = Some(0)`
after 1c.B). At the `xx cl : *Block` site the lookup 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_<sig>` trampoline + Into impl alongside the
  existing ones in modules/std/objc_block.sx, or declare it in
  your own code

The pack impl is reachable in the file but never considered.

Next commit (1d.B):
- New `param_impl_pack_map` keyed by `"Proto\x00<arg_mangled>"`
  (no src) — populated by `registerParamImpl` when the source
  is pack-shaped.
- `tryUserConversion` walks the pack map on concrete-key miss.
  Pack shape matches when the impl's fixed prefix equals the
  source's matching prefix; the remainder binds to `$args` and
  the source's return type binds to `$R`. Concrete impls win
  over pack impls (specificity).
- `resolveTypeWithBindings` learns the closure_type_expr path
  so the impl body's `self: Closure(..$args) -> $R` substitutes
  to the concrete source closure during monomorphisation.

The `Closure(s32, bool) -> bool` shape is not covered by stdlib
or 96-block-multi-arg's hand-rolled impls, so the pack impl is
the only candidate post-1d.B.

193/193 example tests + `zig build test` green.
2026-05-27 12:50:23 +03:00