ffi checkpoint: step 5 done — generic Into(Block) impl + 3 unblocking bug fixes
This commit is contained in:
@@ -6,6 +6,82 @@ add a test and make it pass — that's two commits).
|
|||||||
|
|
||||||
## Last completed step
|
## Last completed step
|
||||||
|
|
||||||
|
**M5.A.next.5 — generic `Into(Block) for Closure(..$args) -> $R`**
|
||||||
|
(commits `3bd6f26` → `2eaf932`, plus 3 unblocking compiler-bug
|
||||||
|
fixes — issues 0048 / 0049 / 0050).
|
||||||
|
|
||||||
|
The visible end-user payoff for the whole variadic-heterogeneous-
|
||||||
|
type-packs feature. One stdlib impl replaces every per-signature
|
||||||
|
hand-rolled `Into(Block)` pair; the compiler monomorphises the
|
||||||
|
impl body per call shape and emits a dedicated `__invoke`
|
||||||
|
`callconv(.c)` trampoline + Block literal via a single `#insert
|
||||||
|
build_block_convert($args, $R)`.
|
||||||
|
|
||||||
|
| Slice | Commit | What |
|
||||||
|
|---|---|---|
|
||||||
|
| 5.0 probe | (no commit) | Confirmed nested `() -> Ret callconv(.c) { body }` parses + `@inner` address-of binds + indirect call works. The trampoline emission pattern is unblocked at the language-surface level. |
|
||||||
|
| issue-0048 xfail | `8fcf352` | Bare `$args` slice loses `.len` across a function-call boundary — pin via `examples/173-pack-bare-args-cross-call.sx`. |
|
||||||
|
| issue-0048 fix | `0ede097` | `lazyLowerFunction` saves/nulls `pack_arg_nodes` / `pack_param_count` / `pack_arg_types` / `inline_return_target` before lowering the callee body. Without it, a lazily lowered regular fn called from inside a pack-fn mono inherited the outer pack maps and `lowerFieldAccess`'s `<pack_name>.len` intercept folded the callee's same-named param to the outer mono's arity. |
|
||||||
|
| issue-0049 xfail | `64dcbca` | New-form variadic `..parts: []string` defined in stdlib + called from another module crashes LLVM emit (`LLVMBuildExtractValue` inside `emitStrCmp`). Pinned via the path_join migration. |
|
||||||
|
| issue-0049 fix | `b5301c4` | `resolveParamType` + `packVariadicCallArgs` now detect when a variadic param's declared type is already a slice (`..name: []T`) and use it as the element-shape container rather than wrapping `[]T` to `[][]T`. |
|
||||||
|
| variadic migration | `5b3d864` | Stdlib (`format` / `print` / `open`) and example fixtures (`19` / `20` / `50` / `120` / `ffi-foreign-cvariadic`) move to new `..name: []T` syntax. |
|
||||||
|
| variadic cutover | `952dc0e` | Parser hard-rejects the legacy `name: ..T` form. `specs.md` documents `..name: []T` as the surface syntax. |
|
||||||
|
| issue-0050 xfail | `ec2a99a` | Generic-mono path (`monomorphizeFunction`) leaks the outer pack-fn's `pack_arg_types` into the generic's body lowering — `args.len` constant-folds to the wrong arity per `examples/175-generic-fn-pack-state-leak.sx`. |
|
||||||
|
| issue-0050 fix | `5316bf7` | Same isolation pattern as 0048 applied to `monomorphizeFunction`. |
|
||||||
|
| 5.1.A xfail | `3bd6f26` | `build_block_convert(args: []Type, $ret: Type) -> string` undefined — pin output format via `examples/176-build-block-convert.sx` across 3 void shapes + 1 non-void shape. |
|
||||||
|
| 5.1.B fix | `aeb950b` | Builder added to `library/modules/std/objc_block.sx`. Emits nested `callconv(.c)` trampoline + Block literal source. |
|
||||||
|
| 5.2.A xfail | `f5342e9` | Generic `Into(Block)` impl absent — `Closure(s64, s64) -> void` (uncovered by hand-rolled impls) emits the "no Into(Block) for cl_s64_s64__void" diagnostic per `examples/177-generic-into-block.sx`. |
|
||||||
|
| 5.2.B fix | `165b621` | Generic impl `Closure(..$args) -> $R` added with `#insert build_block_convert($args, $R)`. `lowerExpr`'s `.comptime_pack_ref` + `resolveTypeArg` + `type_bridge.isTypeShapedAstNode` extended so impl-mono `$args` (pack_bindings) and `$R` (type_bindings) resolve in both expr and type positions. |
|
||||||
|
| 5.3 | `2eaf932` | Delete hand-rolled `__block_invoke_void` + `__block_invoke_bool` + the two per-shape impls. The generic impl covers both at runtime. |
|
||||||
|
|
||||||
|
What's now possible end-to-end (from
|
||||||
|
`examples/177-generic-into-block.sx`):
|
||||||
|
|
||||||
|
```sx
|
||||||
|
#import "modules/std/objc_block.sx";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
cl := (a: s64, b: s64) => { g_a = a; g_b = b; };
|
||||||
|
blk : Block = xx cl; // generic impl mono'd for
|
||||||
|
// Closure(s64, s64) -> void
|
||||||
|
invoke_fn : (*Block, s64, s64) -> void callconv(.c) = xx blk.invoke;
|
||||||
|
invoke_fn(@blk, 10, 20);
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `xx cl : Block` site monomorphises the generic
|
||||||
|
`Into(Block) for Closure(..$args) -> $R` impl. Inside the impl
|
||||||
|
mono, `#insert build_block_convert($args, $R)` evaluates the
|
||||||
|
builder at comptime with `$args = [s64, s64]` and `$R = void`,
|
||||||
|
and substitutes the resulting source — a nested
|
||||||
|
`__invoke :: (block_self: *Block, arg0: s64, arg1: s64) -> void
|
||||||
|
callconv(.c) { ... }` trampoline plus the Block literal that
|
||||||
|
points its `invoke` slot at `@__invoke`. Stack-local block layout
|
||||||
|
matches Apple's published spec; UIKit / Foundation consumers can
|
||||||
|
take this directly.
|
||||||
|
|
||||||
|
Adding a new closure shape to stdlib used to mean writing a
|
||||||
|
per-signature `__block_invoke_<sig>` trampoline + a focused
|
||||||
|
`Into(Block) for Closure(<sig>)` impl. Now: no stdlib edit
|
||||||
|
needed. The generic impl emits per-call-shape on demand.
|
||||||
|
|
||||||
|
217/217 example tests + `zig build test` green.
|
||||||
|
|
||||||
|
Known follow-ups (out of scope for step 5):
|
||||||
|
- `string`-typed arg in a generic block trampoline segfaults at
|
||||||
|
runtime — the 16-byte `{ptr, len}` slice doesn't round-trip
|
||||||
|
through the `callconv(.c)` ABI cleanly in the generated
|
||||||
|
trampoline. Hand-rolled impls didn't hit this because they
|
||||||
|
pre-dated string-arg shapes. Real closures of shape `Closure(
|
||||||
|
string, ...) -> ...` are uncommon in Apple block APIs; revisit
|
||||||
|
when a UIKit caller needs it.
|
||||||
|
- Step 6 of the pack feature (rewriting `print` / `format` to
|
||||||
|
use `..$args: []$T` for compile-time arity + type checking
|
||||||
|
instead of `..args: []Any` runtime boxing).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**M5.A.next.4A.bare — bare `$args` + dynamic reflection intrinsics**
|
**M5.A.next.4A.bare — bare `$args` + dynamic reflection intrinsics**
|
||||||
(commits `c792642` → `2162662`, 5 slices in order).
|
(commits `c792642` → `2162662`, 5 slices in order).
|
||||||
|
|
||||||
@@ -852,19 +928,22 @@ plus 2 codegen fixes surfaced along the way.**
|
|||||||
|
|
||||||
## Current state
|
## Current state
|
||||||
|
|
||||||
- 213/213 example tests pass; `zig build test` green.
|
- 217/217 example tests pass; `zig build test` green.
|
||||||
- issue-0048 (bare `$args` slice loses `.len` across function-
|
- **Step 5 of the variadic heterogeneous type packs feature done
|
||||||
call boundary — root cause: `lazyLowerFunction` did not save/
|
end-to-end.** Generic `Into(Block) for Closure(..$args) -> $R`
|
||||||
restore `pack_arg_nodes` / `pack_param_count` / `pack_arg_types`
|
impl in stdlib emits per-call-shape `__invoke` trampoline +
|
||||||
/ `inline_return_target`, so a lazily lowered callee inside a
|
Block literal via `#insert build_block_convert($args, $R)`.
|
||||||
pack-fn mono inherited the outer pack maps and `lowerFieldAccess`'s
|
Hand-rolled `__block_invoke_void` / `__block_invoke_bool`
|
||||||
`<pack_name>.len` intercept folded the callee's same-named param
|
deleted; `examples/95` / `96` route through the generic
|
||||||
to the outer mono's arity). FIXED — defer-restore block added to
|
unchanged.
|
||||||
`lazyLowerFunction`; regression locked in at
|
- Three compiler-bug fixes landed alongside step 5 — issues
|
||||||
`examples/173-pack-bare-args-cross-call.sx`.
|
0048 (`lazyLowerFunction` pack-state leak), 0049 (new-form
|
||||||
- Step 5.0 probe done — nested `() -> Ret callconv(.c) { body }`
|
variadic `..name: []T` cross-module emit crash), 0050
|
||||||
parses, `@inner` address-of binds to a fn-pointer, indirect call
|
(`monomorphizeFunction` pack-state leak). Each is captured by
|
||||||
works. The trampoline emission pattern is unblocked.
|
a focused regression test (`examples/173` / `174` / `175`).
|
||||||
|
- Legacy variadic syntax `name: ..T` rejected at parse time;
|
||||||
|
stdlib (`path_join` / `format` / `print` / `open`) and example
|
||||||
|
fixtures migrated to `..name: []T`. `specs.md` updated.
|
||||||
- Phase 3.0/3.1/3.2 + M1.0–M1.3 + M2.1–M2.3 + M3 + M4.0 + M4.A all landed.
|
- Phase 3.0/3.1/3.2 + M1.0–M1.3 + M2.1–M2.3 + M3 + M4.0 + M4.A all landed.
|
||||||
- Pack feature step 1 done (1c.A → 1d.B; commits bb6eca6 → 08feb60).
|
- Pack feature step 1 done (1c.A → 1d.B; commits bb6eca6 → 08feb60).
|
||||||
- Pack feature step 2 done — typed `args[$i]` at literal indices
|
- Pack feature step 2 done — typed `args[$i]` at literal indices
|
||||||
|
|||||||
Reference in New Issue
Block a user