ffi 3.1 (xfail): add Cls.static_method(args) regression test

xfail half of Phase 3.1: static calls on `#objc_class` aliases lower
to `objc_msg_send` against the class object (loaded once per module
via `objc_getClass`).

Test mirrors the Phase 3.0 pattern (`ffi-objc-dsl-01..04`): synthesize
a class at runtime via `objc_allocateClassPair`, add class methods on
the metaclass via `object_getClass(cls) + class_addMethod`, declare
the sx-side `#objc_class` with `static answer :: ...` / `static add ::
...`, then invoke `SxProbeStatic.answer()` / `.add(7, 35)`. Skips on
non-macOS.

Surface choice: the call site is `.` (`Cls.method(args)`), matching
JNI's existing static dispatch convention (`SurfaceView.new(ctx)`)
rather than the plan's notional `::` form. The lowering disambiguates
static vs instance by inspecting `method.is_static` on the foreign-
class member, same as JNI. Picking `.` avoids extending the parser
for a new postfix operator with no other use case.

Pre-3.1 snapshot pins the current bail diagnostic at
`lowerForeignStaticCall` (lower.zig:4475) — "static calls on
'objc_class' runtime not yet supported (Phase 3/4)" — fires twice
because both the niladic and the keyword-arg static call hit it.
exit=1.

164/164 tests; next commit implements the dispatch and flips the
snapshot to working output.
This commit is contained in:
agra
2026-05-25 16:13:05 +03:00
parent 53fe73acda
commit b07ee53a39
3 changed files with 53 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
// Phase 3 step 3.1 (PLAN-FFI.md): static call `Cls.class_method(args)`
// on an `#objc_class` alias lowers to `objc_msg_send` against the class
// object (loaded once per module via `objc_getClass` and cached). The
// selector is derived by the same default mangling as Phase 3.0
// (`stringWithUTF8String_(s)` → "stringWithUTF8String:").
//
// Mirrors the JNI surface (`Alias.new(...)` etc.); the lowering
// disambiguates static vs instance by looking at `method.is_static` on
// the foreign-class member.
//
// Pre-3.1: `lowerForeignStaticCall` only handles JNI runtime + the
// `new` constructor; any other static call bails. Snapshot pins the
// bail diagnostic.
#import "modules/std.sx";
#import "modules/compiler.sx";
#import "modules/std/objc.sx";
SxProbeStatic :: #foreign #objc_class("SxProbeStatic") {
static answer :: () -> s32;
static add :: (a: s32, b: s32) -> s32;
}
answer_imp :: (self: *void, _cmd: *void) -> s32 callconv(.c) {
42;
}
add_imp :: (self: *void, _cmd: *void, a: s32, b: s32) -> s32 callconv(.c) {
a + b;
}
main :: () -> s32 {
inline if OS == .macos {
ns_object := objc_getClass("NSObject".ptr);
cls := objc_allocateClassPair(ns_object, "SxProbeStatic".ptr, 0);
// class_addMethod on the metaclass — that's where class methods live.
metacls := object_getClass(xx cls);
class_addMethod(metacls, sel_registerName("answer".ptr), xx answer_imp, "i@:".ptr);
class_addMethod(metacls, sel_registerName("add:b:".ptr), xx add_imp, "i@:ii".ptr);
objc_registerClassPair(cls);
n := SxProbeStatic.answer();
print("answer() = {}\n", n);
s := SxProbeStatic.add(7, 35);
print("add(7, 35) = {}\n", s);
}
inline if OS != .macos {
print("skipped (not macos)\n");
}
0;
}

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,2 @@
/Users/agra/projects/sx/examples/ffi-objc-dsl-05-static.sx:41:14: error: static calls on 'objc_class' runtime not yet supported (Phase 3/4)
/Users/agra/projects/sx/examples/ffi-objc-dsl-05-static.sx:43:14: error: static calls on 'objc_class' runtime not yet supported (Phase 3/4)