diff --git a/current/CHECKPOINT-FFI.md b/current/CHECKPOINT-FFI.md index 2e9810f..13b140e 100644 --- a/current/CHECKPOINT-FFI.md +++ b/current/CHECKPOINT-FFI.md @@ -501,6 +501,16 @@ plan's notional `::` — avoids a new postfix operator. Test: and `+description` class methods (NSObject is always available at module-load, unlike test classes created in main's body). +Class-method declarations no longer need an explicit `static` keyword. +The parser 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. Surface examples now write +`new :: (ctx: *JContext) -> *Self;` instead of +`static new :: (...)`. The receiver param NAME doesn't matter — the +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`. + Open work, in roughly the order they make sense: - **Phase 3 step 3.2** — `#selector("explicit:")` override + golden test for the default-mangling table. Escape hatch for selectors diff --git a/examples/ffi-jni-class-03-static.sx b/examples/ffi-jni-class-03-static.sx index d8f62d2..5e67477 100644 --- a/examples/ffi-jni-class-03-static.sx +++ b/examples/ffi-jni-class-03-static.sx @@ -1,16 +1,16 @@ -// Phase 2 step 2.3 (PLAN-FFI.md): xfail then green for the `static` -// method body item inside a `#jni_class` declaration. +// Phase 2 step 2.3 (PLAN-FFI.md): xfail then green for class/static +// method declarations inside a `#jni_class` body. // -// `static name :: (args...) -> Ret;` declares a class/static method — -// no implicit `self`, dispatched via `GetStaticMethodID` / `CallStatic*` -// at lowering time (Phase 2.12). Step 2.3 extends `parseJniClassDecl` -// to recognise the `static` prefix and mark the resulting -// `JniMethodDecl` with `is_static = true`. +// Instance vs class method is determined by the first param's TYPE: +// `(self: *Self, ...)` ⇒ instance, anything else (here, `(n: s32)`) +// ⇒ class method, dispatched via `GetStaticMethodID` / +// `CallStatic*` at lowering time (Phase 2.12). No explicit `static` +// keyword; the param shape carries the signal. #import "modules/std.sx"; Math :: #foreign #jni_class("java/lang/Math") { - static abs :: (n: s32) -> s32; + abs :: (n: s32) -> s32; // no `self: *Self` → class method } main :: () -> s32 { diff --git a/examples/ffi-jni-main-03-ctor.sx b/examples/ffi-jni-main-03-ctor.sx index d922971..71aaad4 100644 --- a/examples/ffi-jni-main-03-ctor.sx +++ b/examples/ffi-jni-main-03-ctor.sx @@ -13,7 +13,7 @@ Bundle :: #foreign #jni_class("android/os/Bundle") { } JContext :: #foreign #jni_class("android/content/Context") { } SurfaceView :: #foreign #jni_class("android/view/SurfaceView") { - static new :: (ctx: *JContext) -> *Self; + new :: (ctx: *JContext) -> *Self; // no `self: *Self` → class method } g_held_view : *void = null; diff --git a/examples/ffi-objc-dsl-05-static.sx b/examples/ffi-objc-dsl-05-static.sx index be1f5bd..2a9d2a7 100644 --- a/examples/ffi-objc-dsl-05-static.sx +++ b/examples/ffi-objc-dsl-05-static.sx @@ -17,11 +17,12 @@ NSObject :: #foreign #objc_class("NSObject") { // `+(Class)class` — niladic, name verbatim, selector = "class". - // Returns the class object itself. - static class :: () -> *void; + // Returns the class object itself. No `self: *Self` first param ⇒ + // class method (sx parser keys on the param TYPE). + class :: () -> *void; // `+(NSString *)description` on the class returns a description // string. Niladic, selector = "description". - static description :: () -> *void; + description :: () -> *void; } main :: () -> s32 { diff --git a/library/modules/platform/android.sx b/library/modules/platform/android.sx index 8baf424..86481a3 100644 --- a/library/modules/platform/android.sx +++ b/library/modules/platform/android.sx @@ -50,7 +50,7 @@ SurfaceHolder :: #foreign #jni_class("android/view/SurfaceHolder") { addCallback :: (self: *Self, cb: *SurfaceHolderCallback); } SurfaceView :: #foreign #jni_class("android/view/SurfaceView") { - static new :: (ctx: *JContext) -> *Self; + new :: (ctx: *JContext) -> *Self; // no `self: *Self` → class method getHolder :: (self: *Self) -> *SurfaceHolder; } SurfaceHolderCallback :: #foreign #jni_class("android/view/SurfaceHolder$Callback") { } diff --git a/src/parser.zig b/src/parser.zig index 737baa6..d94e626 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -1163,17 +1163,6 @@ pub const Parser = struct { continue; } - // Optional `static` prefix → class method (no implicit `self`). - // Context-sensitive — `static` is a plain identifier elsewhere. - var is_static = false; - if (self.current.tag == .identifier and - std.mem.eql(u8, self.tokenSlice(self.current), "static") and - self.peekNext() == .identifier) - { - is_static = true; - self.advance(); // consume `static` - } - // Field: name: Type; (instance field — JNI Get/SetField) // Method: name :: (args...) -> Ret; if (self.current.tag != .identifier) { @@ -1183,9 +1172,6 @@ pub const Parser = struct { self.advance(); if (self.current.tag == .colon) { - if (is_static) { - return self.fail("static fields not yet supported in '#jni_class' body"); - } self.advance(); // consume `:` const field_type = try self.parseTypeExpr(); try self.expect(.semicolon); @@ -1218,6 +1204,21 @@ pub const Parser = struct { } try self.expect(.r_paren); + // Instance vs class method is determined by the first param's + // TYPE: `*Self` (pointer-to-Self) ⇒ instance method, anything + // else (including a method with no params at all) ⇒ class + // method. Keying on the type, not the param name, means the + // user can call the receiver whatever they like — `this`, + // `me`, etc. — without changing the dispatch shape. + const is_static = blk: { + if (param_types.items.len == 0) break :blk true; + const first = param_types.items[0]; + if (first.data != .pointer_type_expr) break :blk true; + const pointee = first.data.pointer_type_expr.pointee_type; + if (pointee.data != .type_expr) break :blk true; + break :blk !std.mem.eql(u8, pointee.data.type_expr.name, "Self"); + }; + var return_type: ?*Node = null; if (self.current.tag == .arrow) { self.advance();