// M5.A — `xx closure : Block` for an arbitrary closure signature. // // Pre-M5.A: the stdlib hand-rolled `Into(Block) for Closure(s32, *void) -> s32` // didn't exist — this code wouldn't compile. Only `Closure() -> void` // and `Closure(bool) -> void` shapes were supported. // // Post-M5.A: the compiler synthesises `__block_invoke_i_i_p` for this // signature on the fly. The block's invoke trampoline forwards // `(__sx_default_context, sx_env, arg0, arg1)` to the captured closure // and returns the s32 result. #import "modules/std.sx"; #import "modules/std/objc_block.sx"; // Side-effect capture so we can observe both args reached the // closure body, even though void-returning trampolines are the // well-tested shape. g_sum: s32 = 0; g_tag: *void = null; main :: () -> s32 { cl := (n: s32, tag: *void) => { g_sum = n + 1; g_tag = tag; }; b : Block = xx cl; invoke_fn : (*Block, s32, *void) -> void callconv(.c) = xx b.invoke; sentinel: s32 = 42; invoke_fn(@b, 41, xx @sentinel); if g_sum != 42 { print("FAIL: g_sum expected 42, got {}\n", g_sum); return 1; } if g_tag == null { print("FAIL: g_tag null\n"); return 1; } print("block multi-arg ok: sum={}\n", g_sum); 0; }