ffi M5.A.next.5.1.B: build_block_convert added to stdlib — make-green

`build_block_convert(args: []Type, $ret: Type) -> string` emits
the convert-body source for the generic `Into(Block) for
Closure(..$args) -> $R` impl (step 5.2):

  1. A nested `__invoke :: (block_self: *Block, arg0: T0, ...) ->
     R callconv(.c) { ... }` trampoline matching the per-shape
     Apple Block ABI.
  2. A `return Block.{ ... };` literal whose `invoke` slot points
     at the nested trampoline via `xx @__invoke`.

Void-returning shapes emit `typed_fn(block_self.sx_env, args...)`;
non-void emits `return typed_fn(...)`. Per-position arg names
follow `arg0`, `arg1`, ... in declaration order; the typed-fn
cast reconstructs the closure's call signature so the trampoline
hands control back to `sx_fn` with the right argument layout.

`examples/176-build-block-convert.sx` flips green (216/216).
This commit is contained in:
agra
2026-05-27 21:48:45 +03:00
parent 3bd6f26c96
commit aeb950b86f

View File

@@ -107,3 +107,70 @@ impl Into(Block) for Closure(bool) -> void {
};
}
}
// Comptime builder for the generic `Into(Block) for Closure(..$args)
// -> $R` impl body. Receives the closure's per-position pack types
// (`args`, a comptime `[]Type`) and the closure's return type
// (`$ret`), emits source that:
//
// 1. Declares a nested `__invoke` `callconv(.c)` trampoline whose
// signature matches the per-shape Apple Block ABI: first arg is
// `block_self: *Block`, then the pack types verbatim. The body
// reconstructs a typed fn-pointer from `block_self.sx_fn`,
// prepends `block_self.sx_env`, and tail-calls the sx closure.
// 2. Returns a stack Block literal whose `invoke` slot points at
// the nested trampoline via `xx @__invoke`.
//
// The host impl wraps the emitted source via `#insert
// build_block_convert($args, $R);`. Per-call-shape monomorphisation
// of the impl body re-runs this builder with the concrete types
// bound, so each closure shape gets its own dedicated trampoline.
//
// Void return type emits `typed_fn(...)`; non-void emits
// `return typed_fn(...)`.
build_block_convert :: (args: []Type, $ret: Type) -> string {
ret_name := type_name(ret);
code := "__invoke :: (block_self: *Block";
i : s64 = 0;
while i < args.len {
code = concat(code, ", arg");
code = concat(code, int_to_string(i));
code = concat(code, ": ");
code = concat(code, type_name(args[i]));
i = i + 1;
}
code = concat(code, ") -> ");
code = concat(code, ret_name);
code = concat(code, " callconv(.c) { typed_fn : (*void");
i = 0;
while i < args.len {
code = concat(code, ", ");
code = concat(code, type_name(args[i]));
i = i + 1;
}
code = concat(code, ") -> ");
code = concat(code, ret_name);
code = concat(code, " = xx block_self.sx_fn; ");
if ret_name == "void" {
code = concat(code, "typed_fn(block_self.sx_env");
} else {
code = concat(code, "return typed_fn(block_self.sx_env");
}
i = 0;
while i < args.len {
code = concat(code, ", arg");
code = concat(code, int_to_string(i));
i = i + 1;
}
code = concat(code, "); } ");
code = concat(code, "return .{ ");
code = concat(code, "isa = @_NSConcreteStackBlock, ");
code = concat(code, "flags = 0, ");
code = concat(code, "reserved = 0, ");
code = concat(code, "invoke = xx @__invoke, ");
code = concat(code, "descriptor = xx @__sx_block_descriptor, ");
code = concat(code, "sx_env = self.env, ");
code = concat(code, "sx_fn = self.fn_ptr, ");
code = concat(code, "};");
return code;
}