From f5342e9fccf640a37ab558438c5e4f1518f14b6f Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 21:57:47 +0300 Subject: [PATCH] =?UTF-8?q?ffi=20M5.A.next.5.2.A:=20generic=20Into(Block)?= =?UTF-8?q?=20impl=20=E2=80=94=20xfail=20lock-in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `examples/177-generic-into-block.sx` exercises a closure shape (`Closure(s64, s64) -> void`) that stdlib's hand-rolled `Into(Block)` impls don't cover. Today: the focused diagnostic "no `Into(Block) for cl_s64_s64__void` impl — add a per-signature `__block_invoke_` trampoline + Into impl alongside the existing ones in modules/std/objc_block.sx, or declare it in your own code" fires at the `xx cl : Block` site. The next commit adds the generic `impl Into(Block) for Closure(..$args) -> $R` to `library/modules/std/objc_block.sx` (wiring `#insert build_block_convert($args, $R)` from step 5.1.B) plus the lowering plumbing needed to make pack + single-type `$` refs work inside the impl's monomorphisation. The test then flips green — the per-shape trampoline emitted by build_block_convert ferries (10, 20) through to the sx closure and the side-effect stores land in g_a / g_b. --- examples/177-generic-into-block.sx | 37 ++++++++++++++++++++++ tests/expected/177-generic-into-block.exit | 1 + tests/expected/177-generic-into-block.txt | 1 + 3 files changed, 39 insertions(+) create mode 100644 examples/177-generic-into-block.sx create mode 100644 tests/expected/177-generic-into-block.exit create mode 100644 tests/expected/177-generic-into-block.txt diff --git a/examples/177-generic-into-block.sx b/examples/177-generic-into-block.sx new file mode 100644 index 0000000..8344a29 --- /dev/null +++ b/examples/177-generic-into-block.sx @@ -0,0 +1,37 @@ +// FFI plan step 5.2 — generic `Into(Block) for Closure(..$args) -> +// $R` impl. One impl in stdlib covers every closure shape; the +// compiler monomorphises the impl body per call shape and emits a +// dedicated `__invoke` `callconv(.c)` trampoline + Block literal +// (via `#insert build_block_convert($args, $R);`). +// +// This test exercises a closure shape (`Closure(s64, s64) -> void`) +// that has NO hand-rolled `Into(Block)` impl in +// `library/modules/std/objc_block.sx`. Before step 5.2 lands, +// `xx cl : Block` errors out with the "no Into(Block) for +// cl_s64_s64__void" focused diagnostic. After the generic impl +// lands, the same call resolves through the pack-shaped impl and +// the per-shape trampoline ferries control back to the sx closure. +// +// The block is invoked directly through `b.invoke` (a typed +// `callconv(.c)` fn-pointer) — the same shape the Apple Block +// runtime calls when a UIKit/Foundation API hands the block back +// to its registered invoke. + +#import "modules/std.sx"; +#import "modules/std/objc_block.sx"; + +g_a: s64 = 0; +g_b: s64 = 0; + +main :: () -> s32 { + cl := (a: s64, b: s64) => { g_a = a; g_b = b; }; + blk : Block = xx cl; + + invoke_fn : (*Block, s64, s64) -> void callconv(.c) = xx blk.invoke; + invoke_fn(@blk, 10, 20); + + if g_a != 10 { print("FAIL: g_a={}\n", g_a); return 1; } + if g_b != 20 { print("FAIL: g_b={}\n", g_b); return 1; } + print("generic-into-block ok: a={} b={}\n", g_a, g_b); + 0; +} diff --git a/tests/expected/177-generic-into-block.exit b/tests/expected/177-generic-into-block.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/177-generic-into-block.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/177-generic-into-block.txt b/tests/expected/177-generic-into-block.txt new file mode 100644 index 0000000..7658721 --- /dev/null +++ b/tests/expected/177-generic-into-block.txt @@ -0,0 +1 @@ +generic-into-block ok: a=10 b=20