Commit Graph

3 Commits

Author SHA1 Message Date
agra
56414407fc ffi: drop static keyword on foreign-class methods; param type discriminates
`static name :: ...` was redundant — instance methods always declare
`self: *Self` as their first param by convention. The parser now derives
`is_static` from the first param's TYPE: if it's `*Self` the method is
an instance method; anything else (including no params at all) is a
class method. Removes a token from the surface, keeps the dispatch
behavior identical.

The receiver param's NAME doesn't matter — only its type. Calling the
first param `this`, `me`, `receiver`, etc. is fine as long as the type
is `*Self`. This mirrors how the rest of sx handles receiver dispatch.

Migration of every site that used the keyword:

- `library/modules/platform/android.sx` — `SurfaceView.new(ctx)`.
- `examples/ffi-jni-class-03-static.sx` — `Math.abs(n)`.
- `examples/ffi-jni-main-03-ctor.sx` — `SurfaceView.new(ctx)` in the
  `#jni_main` body.
- `examples/ffi-objc-dsl-05-static.sx` — NSObject's `.class()` /
  `.description()`.

164/164 example tests; chess clean on macOS / iOS sim / Android via
`tools/verify-step.sh`.
2026-05-25 16:32:32 +03:00
agra
8406cc1fed ffi 3.1: Cls.static_method(args) lowers to objc_msg_send on the class object
Implementation half of the Phase 3.1 cadence step.
`lowerForeignStaticCall` for `#objc_class` / `#objc_protocol` runtimes
no longer bails; it routes through a new `lowerObjcStaticCall` helper
that loads the class object from a module-scoped cached slot (populated
once per module via `objc_getClass`) and dispatches `objc_msg_send`
with the same selector-mangling as Phase 3.0's instance dispatch.

Three pieces:

1. `Module.objc_class_cache` — parallel to `objc_selector_cache`,
   insertion-ordered list of (class_name, slot_GlobalId) so the
   constructor that calls `objc_getClass` per slot at module load
   is deterministic. `lookupObjcClass` / `appendObjcClass` accessors.
2. `internObjcClassObject` in lower.zig — get-or-create a
   `OBJC_CLASSLIST_REFERENCES_<Cls>` global pointer; matches clang's
   naming convention. `lowerObjcStaticCall` reuses
   `deriveObjcSelector` from 3.0 for the selector, loads the class
   slot, and emits `objc_msg_send(class_obj, sel, args)`.
3. `emitObjcClassInit` in emit_llvm.zig — companion to
   `emitObjcSelectorInit`. Walks `objc_class_cache`, synthesizes a
   constructor `__sx_objc_class_init` that calls `objc_getClass(name)`
   per slot, registers in `@llvm.global_ctors` for AOT (extending the
   existing array if the selector init already created it), and
   injects a direct call into main's prelude after any prior init
   calls so the ORC JIT path runs it too.

Surface form is `.` (`NSObject.class()`) matching JNI's `Alias.new(...)`
convention rather than the plan's notional `::` — avoids extending the
parser for a new postfix operator with no other use case.

Test `examples/ffi-objc-dsl-05-static.sx` exercises NSObject's
`+class` and `+description` class methods via the new syntax, asserts
both return non-null. NSObject is always available at module-load,
unlike runtime-created test classes that wouldn't exist yet when
the class-init constructor runs.

164/164 tests; chess builds + runs clean on all three platforms.
2026-05-25 16:23:24 +03:00
agra
b07ee53a39 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.
2026-05-25 16:13:05 +03:00