diff --git a/examples/ffi-objc-call-09-in-construct.sx b/examples/ffi-objc-call-09-in-construct.sx new file mode 100644 index 0000000..f77b1c3 --- /dev/null +++ b/examples/ffi-objc-call-09-in-construct.sx @@ -0,0 +1,85 @@ +// Phase 1 steps 1.11–1.13 (PLAN-FFI.md): `#objc_call` call sites +// embedded inside the sx surface constructs. None touch a new ABI +// path — the lowering routes the call identically regardless of +// the enclosing scope, and this test pins that lemma. +// +// 1. Struct method body Probe.fetch +// 2. Protocol impl method body impl Hashable for Probe +// 3. Closure value body closure that calls hash +// +// 1.14 (separate test): `inline if OS == { case }` gating across +// targets — verified by `tests/cross_compile.sh`. + +#import "modules/std.sx"; +#import "modules/compiler.sx"; +#import "modules/std/objc.sx"; + +// ── 1. Struct method calling #objc_call ───────────────────────────── +Probe :: struct { + receiver: *void = null; + fetch :: (self: *Probe) -> s64 { + #objc_call(s64)(self.receiver, "hash"); + } +} + +// ── 2. Protocol impl method ──────────────────────────────────────── +Hashable :: protocol { + sx_hash :: (self: *Self) -> s64; +} + +impl Hashable for Probe { + sx_hash :: (self: *Probe) -> s64 { + #objc_call(s64)(self.receiver, "hash") * 2; + } +} + +// ── 3. Closure body invoking #objc_call ───────────────────────────── +// Closure-captured `recv` isn't traced through the `#objc_call` AST +// node by sema today, so we reach the receiver via a module-level +// global. The lemma we lock here is that lowering routes the call +// the same way inside a closure body as it does at top level. +g_hasher_recv : *void = null; + +make_hasher :: () -> Closure(s32) -> s64 { + closure((dummy: s32) -> s64 => #objc_call(s64)(g_hasher_recv, "hash")); +} + +// ── 4. Generic function body — instantiated per call site ─────────── +hash_through :: (recv: $T) -> s64 { + p : *void = xx recv; + #objc_call(s64)(p, "hash"); +} + +main :: () -> s32 { + inline if OS == .macos { + ns_object := objc_getClass("NSObject".ptr); + + p : Probe = .{ receiver = ns_object }; + + // 1. struct method + h1 := p.fetch(); + print("fetch != 0 = {}\n", h1 != 0); + + // 2. protocol method (doubles the raw hash; mostly checking + // dispatch / arg threading, not the math) + h2 := p.sx_hash(); + print("protocol h2 = {}\n", h2 == h1 * 2); + + // 3. closure (receives a dummy arg to keep the `Closure(T) -> R` + // arity matching 35-closures.sx; recv comes via a global — + // closure capture through `#objc_call` AST nodes isn't + // traced by sema today and would error "unresolved"). + g_hasher_recv = ns_object; + hasher := make_hasher(); + h3 := hasher(0); + print("closure h3 = {}\n", h3 == h1); + + // 4. generic function — instantiates with T = *void here + h4 := hash_through(ns_object); + print("generic h4 = {}\n", h4 == h1); + } + inline if OS != .macos { + print("skipped (not macos)\n"); + } + 0; +} diff --git a/tests/expected/ffi-objc-call-09-in-construct.exit b/tests/expected/ffi-objc-call-09-in-construct.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/ffi-objc-call-09-in-construct.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/ffi-objc-call-09-in-construct.txt b/tests/expected/ffi-objc-call-09-in-construct.txt new file mode 100644 index 0000000..cfe23c2 --- /dev/null +++ b/tests/expected/ffi-objc-call-09-in-construct.txt @@ -0,0 +1,4 @@ +fetch != 0 = true +protocol h2 = true +closure h3 = true +generic h4 = true