Files
sx/examples/issue-0038.sx
agra 35359b88f8 issue-0038: xfail repro — recv capture inside #objc_call
Uncomments the second passthrough case in `examples/issue-0038.sx`
that captures `recv` from the enclosing function into a closure body
that uses it inside `#objc_call(s64)(recv, "hash")`. Current behavior
is a hard error from the name-resolution pass:

    examples/issue-0038.sx:28:48: error: unresolved: 'recv'

Snapshot locks the failure in (exit 1 + that error message) so the
next commit can flip it to passing without ambiguity. Per the FFI
cadence rule this is a test-add (xfail); the make-green follow-up
adds the missing recursion arm in `lower.zig`'s `collectCaptures` for
`.ffi_intrinsic_call` nodes.
2026-05-19 21:10:58 +03:00

48 lines
1.7 KiB
Plaintext

// Closure capture analysis doesn't trace into the `FfiIntrinsicCall`
// AST node — identifiers used inside `#objc_call` / `#jni_call` /
// `#jni_static_call` from a closure body aren't recognized as
// captured variables. Surfaced when writing `ffi-objc-call-09-in-
// construct.sx`.
//
// Reduced repro: capture in a closure body works fine for
// "normal" expressions (see `passthrough_works`), but the same
// capture inside `#objc_call`'s arg list trips
// "unresolved: 'recv'" (see `passthrough_via_objc_call` — would
// fail at parse time, so it's commented out).
//
// Likely fix: in the closure free-variable analyzer (sema.zig /
// lower.zig), add a recursive arm for `ffi_intrinsic_call` that
// visits `return_type` + every `args[i]` the same way the `.call`
// arm walks `callee` + `args`.
#import "modules/std.sx";
#import "modules/compiler.sx";
#import "modules/std/objc.sx";
passthrough_works :: (recv: *void) -> Closure(s32) -> *void {
closure((d: s32) -> *void => recv); // captures `recv` — fine
}
passthrough_via_objc_call :: (recv: *void) -> Closure(s32) -> s64 {
// Same `recv` capture, but inside `#objc_call(...)`.
closure((d: s32) -> s64 => #objc_call(s64)(recv, "hash"));
}
main :: () -> s32 {
inline if OS == .macos {
f := passthrough_works(null);
p := f(0);
print("ok (passthrough works) = {}\n", p == null);
// After the fix, capture in an FfiIntrinsicCall arg list works.
ns_object := objc_getClass("NSObject".ptr);
g := passthrough_via_objc_call(ns_object);
h := g(0);
print("ok (passthrough via #objc_call) = {}\n", h != 0);
}
inline if OS != .macos {
print("skipped (not macos)\n");
}
0;
}