From a908ecf28fc890b9cf1cc9d667a10e63427865e8 Mon Sep 17 00:00:00 2001 From: agra Date: Mon, 25 May 2026 16:55:32 +0300 Subject: [PATCH] ffi 3.2 A1 (xfail): add `#selector("...")` override regression test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 3.2 xfail half. `#selector("explicit:string")` is the escape hatch for cases where the sx-side method name doesn't conveniently produce the target selector under the default mangling rule (Phase 3.0 — split on `_`, each piece becomes a keyword with a trailing `:`). Surface form mirrors `#jni_method_descriptor("(Sig)Ret")` — sits after the optional `-> ReturnType` and before the method body / terminator. Test fixture covers both lowering paths: - Static method override: `NSObject.gimme()` with override "description" — exercises lowerObjcStaticCall (Phase 3.1). - Instance method override: `NSDictionary.lookup(self, key)` with override "objectForKey:" — declared (parse + AST + lowering wiring) but not invoked at runtime (no real NSDictionary in scope). The declaration alone locks in the multi-arg-override path. Pre-3.2: parser doesn't know `#selector`; snapshot captures "expected ';'" at the override site, exit=1. Next commit (A2) wires the lexer token, AST field, parser block, and lowering integration; snapshot flips to working output. 165/165 example tests. Plan at `~/.claude/plans/lets-see-options-for-merry-dijkstra.md`. --- current/CHECKPOINT-FFI.md | 21 +++++++++- examples/ffi-objc-dsl-06-selector-override.sx | 41 +++++++++++++++++++ .../ffi-objc-dsl-06-selector-override.exit | 1 + .../ffi-objc-dsl-06-selector-override.txt | 1 + 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 examples/ffi-objc-dsl-06-selector-override.sx create mode 100644 tests/expected/ffi-objc-dsl-06-selector-override.exit create mode 100644 tests/expected/ffi-objc-dsl-06-selector-override.txt diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index 13b140e..e453c1d 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -511,8 +511,27 @@ type is the contract. Updated: `library/modules/platform/android.sx`, `examples/ffi-jni-class-03-static.sx`, `examples/ffi-jni-main-03-ctor.sx`, `examples/ffi-objc-dsl-05-static.sx`. +Phase 3 step 3.2 in flight. Plan at +`~/.claude/plans/lets-see-options-for-merry-dijkstra.md`. Three parts: +(A) `#selector("...")` override, (B) golden mangling-table fixture, +(C) uikit.sx migration to declarative `#objc_class` (5 clusters, +foreign classes only — sx-defined classes wait for Phase 3.7). + +This commit lands A1 — the xfail half of the `#selector` cadence. +`examples/ffi-objc-dsl-06-selector-override.sx` exercises the +surface form (both static `NSObject.gimme()` with override "description" +and an instance-method `NSDictionary.lookup` with override +"objectForKey:"). The parser doesn't know the `#selector` token yet, +so the snapshot captures the parser error and exit=1. Next commit +(A2) wires lexer/parser/AST/lowering and flips the snapshot. + Open work, in roughly the order they make sense: -- **Phase 3 step 3.2** — `#selector("explicit:")` override + golden +- **Phase 3 step 3.2 — A2 (make-green)** — wire the `#selector` + token and override behavior. Snapshot flips to working output. +- **Phase 3 step 3.2 — B (golden mangling table)** — locked-in IR + fixture for the default mangling rule. +- **Phase 3 step 3.2 — C1..C5** — uikit.sx migration, one cluster + per commit, chess regression after each. test for the default-mangling table. Escape hatch for selectors that don't fit the underscore-split rule (e.g. `tableView_ numberOfRowsInSection_` with an asymmetric keyword count). diff --git a/examples/ffi-objc-dsl-06-selector-override.sx b/examples/ffi-objc-dsl-06-selector-override.sx new file mode 100644 index 0000000..0617c87 --- /dev/null +++ b/examples/ffi-objc-dsl-06-selector-override.sx @@ -0,0 +1,41 @@ +// Phase 3 step 3.2 (PLAN-FFI.md): `#selector("explicit:string")` +// override on `#objc_class` members. Escape hatch for cases where the +// sx-side method name doesn't conveniently produce the target selector +// through the default mangling rule (Phase 3.0 — split on `_`, each +// piece becomes a keyword with a trailing `:`). +// +// Surface form mirrors `#jni_method_descriptor("(Sig)Ret")` — sits +// after the optional `-> ReturnType` and before the body / terminator. +// +// Pre-3.2: the parser doesn't know the `#selector` token; snapshot +// captures the parser error (exit=1). Next commit wires lexer + parser +// + AST + lowering and the snapshot flips to working output. +#import "modules/std.sx"; +#import "modules/compiler.sx"; + +NSObject :: #foreign #objc_class("NSObject") { + // Default mangling would yield selector "gimme" — NSObject has no + // such IMP. The override pins it to the real selector + // "description". Static method (no `self: *Self` first param). + gimme :: () -> *void #selector("description"); +} + +// Instance-method override exercises a different lowering path +// (`lowerObjcMethodCall` rather than `lowerObjcStaticCall`). Parse- +// only on this side — main only invokes the static path because we +// don't have a real NSDictionary in scope, but the declaration locks +// in the parser + AST + lowering wiring for the multi-arg shape. +NSDictionary :: #foreign #objc_class("NSDictionary") { + lookup :: (self: *Self, key: *void) -> *void #selector("objectForKey:"); +} + +main :: () -> s32 { + inline if OS == .macos { + d := NSObject.gimme(); + print("static override non-null: {}\n", d != null); + } + inline if OS != .macos { + print("skipped (not macos)\n"); + } + 0; +} diff --git a/tests/expected/ffi-objc-dsl-06-selector-override.exit b/tests/expected/ffi-objc-dsl-06-selector-override.exit new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/expected/ffi-objc-dsl-06-selector-override.exit @@ -0,0 +1 @@ +1 diff --git a/tests/expected/ffi-objc-dsl-06-selector-override.txt b/tests/expected/ffi-objc-dsl-06-selector-override.txt new file mode 100644 index 0000000..1a2d01b --- /dev/null +++ b/tests/expected/ffi-objc-dsl-06-selector-override.txt @@ -0,0 +1 @@ +/Users/agra/projects/sx/examples/ffi-objc-dsl-06-selector-override.sx:20:26: error: expected ';'