ffi M1.1 (first pass): id / Class / SEL / BOOL type aliases
Adds named stand-ins for the three opaque Obj-C runtime types and Apple's signed-char boolean to library/modules/std/objc.sx: id :: *void; // any Obj-C instance pointer Class :: *void; // a class object pointer SEL :: *void; // a registered selector BOOL :: s8; // Apple's signed-char boolean (NOT sx's bool) All resolve to their underlying type at the LLVM layer — no runtime cost — but make foreign-class declarations read closer to Objective-C source. The header's old caveat about lacking type aliases is gone. 141-objc-type-aliases.sx exercises the aliases against the real macOS Obj-C runtime: alloc/init an NSObject, fetch its class via objc_getClass, sel_registerName a SEL, then call 'isKindOfClass:' returning BOOL=1. Non-macOS paths print the same line to keep the snapshot stable. DEFERRED (M1.1.b, follow-up): 'Class(T)' parameterization with #extends-aware covariance, and 'instancetype' per-decl substitution. Both require compiler-level type-check support beyond plain stdlib aliases. 170 examples pass (+1).
This commit is contained in:
@@ -6,7 +6,38 @@ add a test and make it pass — that's two commits).
|
|||||||
|
|
||||||
## Last completed step
|
## Last completed step
|
||||||
|
|
||||||
**issue-0043 closed — `#foreign` C-variadic tail via `args: ..T`.**
|
**M1.0 — Expression-bodied function declarations**
|
||||||
|
(3 commits: `6c95b2a`, `4a048d3`, `86c1127`).
|
||||||
|
|
||||||
|
sx's `=>` body form (already used in lambdas) now spans every
|
||||||
|
function-declaration position: top-level, struct method, AND
|
||||||
|
`#objc_class` member method. The parser extension is a single
|
||||||
|
arm in `parseForeignClassDecl` ([src/parser.zig:1262]) that
|
||||||
|
mirrors the existing `parseFnDecl` arrow handling.
|
||||||
|
|
||||||
|
Three commits, FFI cadence:
|
||||||
|
- `6c95b2a` ffi M1.0 (1/3): lock in passing top-level + struct-method form
|
||||||
|
(`examples/139-expression-bodied-fn.sx`).
|
||||||
|
- `4a048d3` ffi M1.0 (2/3, xfail): `=>` body inside `#objc_class` member
|
||||||
|
captured as parser error (`examples/140-expression-bodied-objc-method.sx`).
|
||||||
|
- `86c1127` ffi M1.0 (3/3): parser extension, 140 flips green.
|
||||||
|
|
||||||
|
169 examples pass (+2 from M1.0). `zig build test` green.
|
||||||
|
|
||||||
|
This is the first milestone of the **6-month Obj-C FFI roadmap**
|
||||||
|
saved at `~/.claude/plans/lets-see-options-for-merry-dijkstra.md`.
|
||||||
|
The roadmap covers: M1 language precursors + typed `Class(T)` +
|
||||||
|
class-synthesis foundation; M2 declarative class sugar (properties,
|
||||||
|
class constants, `#extends` chaining); M3 retire
|
||||||
|
`uikit_register_classes`; M4 ARC + autoreleasepool; M5 closure↔block
|
||||||
|
bridge; M6 auto-import + production hardening. Resolved design
|
||||||
|
questions: per-instance allocator at `alloc()`, directive-statement
|
||||||
|
`#extends`/`#implements` syntax, refcount inherited from NSObject.
|
||||||
|
Four design questions still open (see roadmap).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Prior landing — issue-0043 closed: `#foreign` C-variadic tail via `args: ..T`.**
|
||||||
A trailing variadic param on a `#foreign` declaration now maps to the
|
A trailing variadic param on a `#foreign` declaration now maps to the
|
||||||
C calling convention's `...` instead of sx's slice-packing path. Drops
|
C calling convention's `...` instead of sx's slice-packing path. Drops
|
||||||
the existing per-arity shim pattern (`__log_2i :: (prio, tag, fmt, a:
|
the existing per-arity shim pattern (`__log_2i :: (prio, tag, fmt, a:
|
||||||
@@ -145,14 +176,30 @@ plus 2 codegen fixes surfaced along the way.**
|
|||||||
|
|
||||||
## Current state
|
## Current state
|
||||||
|
|
||||||
- 97/97 regression tests pass (was 86 at start of FFI work; +11 net,
|
- 169/169 example tests pass; `zig build test` green.
|
||||||
factoring in the `examples/issue-0036.sx` → `101-ffi-medium-struct.sx`
|
- Phase 3.0/3.1/3.2 (Obj-C DSL dispatch + selector mangling +
|
||||||
promotion).
|
selector override + uikit.sx C1-C5 cluster migrations) all
|
||||||
- `tests/cross_compile.sh` runs clean (empty tuple list).
|
landed. M1.0 (expression-bodied functions) just landed.
|
||||||
- Chess Android build + iOS-sim build both clean. emit_llvm.zig sret
|
- Chess on macOS / iOS-sim / Android all build and run.
|
||||||
+ struct↔array changes don't regress either.
|
- Active forward plan: 6-month Obj-C FFI roadmap at
|
||||||
- ABI coverage matrix locked in for all four C-ABI aggregate slots
|
`~/.claude/plans/lets-see-options-for-merry-dijkstra.md`.
|
||||||
(≤8 B int, 9..16 B int, 16 B HFA, >16 B byval+sret).
|
|
||||||
|
## Next step (M1.1 — Foreign type aliases)
|
||||||
|
|
||||||
|
Introduce sx-side typed aliases for the Obj-C primitives that
|
||||||
|
today are `*void`: `Class(T)` (phantom-parameterized), `id`, `SEL`,
|
||||||
|
`BOOL`, `instancetype`. Per the roadmap, the load-bearing piece
|
||||||
|
is `Class(T)`: a phantom-typed pointer alias enabling type-safe
|
||||||
|
factory returns like
|
||||||
|
`layerClass :: Class(CALayer) = CAEAGLLayer.class();` (the M2.1(a)
|
||||||
|
class-constant form). Plain `Class` ⇒ `Class(NSObject)` sugar.
|
||||||
|
`Cls.class()` returns `Class(Cls)`. Covariant on `#extends`.
|
||||||
|
|
||||||
|
Files to touch: [src/parser.zig](../src/parser.zig) (parser
|
||||||
|
support for `Class(T)` type syntax),
|
||||||
|
[src/ir/lower.zig](../src/ir/lower.zig) (alias resolution +
|
||||||
|
covariance check). LLVM layer unchanged — these are phantom-typed,
|
||||||
|
underlying representation stays `*void`.
|
||||||
|
|
||||||
## Phase 1B complete (1.6–1.14)
|
## Phase 1B complete (1.6–1.14)
|
||||||
|
|
||||||
|
|||||||
47
examples/141-objc-type-aliases.sx
Normal file
47
examples/141-objc-type-aliases.sx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// M1.1 — Obj-C primitive type aliases.
|
||||||
|
//
|
||||||
|
// `id`, `Class`, `SEL`, `BOOL` from `modules/std/objc.sx` stand in
|
||||||
|
// for the three opaque Obj-C runtime types and Apple's signed-char
|
||||||
|
// boolean. They resolve to `*void` / `s8` at the LLVM layer — no
|
||||||
|
// runtime cost — but make foreign-class and call-site declarations
|
||||||
|
// read closer to Objective-C source.
|
||||||
|
//
|
||||||
|
// `Class(T)` parameterization (phantom T, `#extends`-aware
|
||||||
|
// covariance) is deferred to a follow-up; for now plain `Class`
|
||||||
|
// is the only form and assignments are not checked against the
|
||||||
|
// referent's class hierarchy.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
#import "modules/compiler.sx";
|
||||||
|
#import "modules/std/objc.sx";
|
||||||
|
|
||||||
|
// Foreign-class declaration using the aliases at param/return positions.
|
||||||
|
NSObjectAlias :: #foreign #objc_class("NSObject") {
|
||||||
|
alloc :: () -> *Self;
|
||||||
|
init :: (self: *Self) -> *Self;
|
||||||
|
isKindOfClass :: (self: *Self, cls: Class) -> BOOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
inline if OS == .macos {
|
||||||
|
// id - any Obj-C instance pointer.
|
||||||
|
nsobj : id = NSObjectAlias.alloc().init();
|
||||||
|
|
||||||
|
// Class - the runtime class object.
|
||||||
|
ns_cls : Class = objc_getClass("NSObject".ptr);
|
||||||
|
|
||||||
|
// SEL - registered selector.
|
||||||
|
sel : SEL = sel_registerName("alloc".ptr);
|
||||||
|
_ = sel;
|
||||||
|
|
||||||
|
// BOOL - Apple's signed-char boolean. Cast the *Self into
|
||||||
|
// a *NSObjectAlias for the method call.
|
||||||
|
obj : *NSObjectAlias = xx nsobj;
|
||||||
|
flag : BOOL = obj.isKindOfClass(ns_cls);
|
||||||
|
print("isKindOfClass: {}\n", flag); // 1 (true)
|
||||||
|
}
|
||||||
|
inline if OS != .macos {
|
||||||
|
print("isKindOfClass: 1\n"); // skip — runtime not present
|
||||||
|
}
|
||||||
|
0;
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
// Obj-C runtime FFI primitives.
|
// Obj-C runtime FFI primitives.
|
||||||
//
|
//
|
||||||
// `*void` stands in for the Obj-C `id`/`Class`/`SEL` types. There's no
|
|
||||||
// sx-level type alias yet, so naming discipline at call sites is the only
|
|
||||||
// thing keeping them apart.
|
|
||||||
//
|
|
||||||
// objc_msgSend has the standard ARM64 calling convention (no varargs path).
|
// objc_msgSend has the standard ARM64 calling convention (no varargs path).
|
||||||
// Each call site must invoke through a function pointer of the *exact*
|
// Each call site must invoke through a function pointer of the *exact*
|
||||||
// argument and return shape. The idiom:
|
// argument and return shape. The idiom:
|
||||||
@@ -11,6 +7,28 @@
|
|||||||
// msg_fn : (recv: *void, sel: *void, arg: [*]u8) -> *void = xx objc_msgSend;
|
// msg_fn : (recv: *void, sel: *void, arg: [*]u8) -> *void = xx objc_msgSend;
|
||||||
// result := msg_fn(receiver, selector, c_string);
|
// result := msg_fn(receiver, selector, c_string);
|
||||||
|
|
||||||
|
// ─── Obj-C primitive type aliases ───────────────────────────────────────
|
||||||
|
// Named stand-ins for the three opaque Obj-C runtime types. They all
|
||||||
|
// resolve to `*void` at the LLVM layer (no runtime cost) but improve
|
||||||
|
// readability in foreign-class declarations and call sites.
|
||||||
|
//
|
||||||
|
// id — any Obj-C instance pointer
|
||||||
|
// Class — a class object pointer
|
||||||
|
// SEL — a registered selector
|
||||||
|
//
|
||||||
|
// `Class(T)` parameterization (phantom-typed, with `#extends`-aware
|
||||||
|
// covariance) is a follow-up — needs compiler-level type-check support.
|
||||||
|
// For now, `Class` alone is the only form; assignments are not checked
|
||||||
|
// against the referent's class hierarchy.
|
||||||
|
id :: *void;
|
||||||
|
Class :: *void;
|
||||||
|
SEL :: *void;
|
||||||
|
|
||||||
|
// Apple's `BOOL` is a signed char (NOT sx's built-in `bool`, which is
|
||||||
|
// LLVM `i1`). Obj-C method signatures that take or return `BOOL` cross
|
||||||
|
// the FFI boundary as `s8`.
|
||||||
|
BOOL :: s8;
|
||||||
|
|
||||||
// On macOS libobjc is auto-loaded by libSystem; on iOS it isn't, so we
|
// On macOS libobjc is auto-loaded by libSystem; on iOS it isn't, so we
|
||||||
// link it explicitly. Foundation registers NSString etc. with the runtime,
|
// link it explicitly. Foundation registers NSString etc. with the runtime,
|
||||||
// also auto-loaded on macOS and required as an explicit framework on iOS.
|
// also auto-loaded on macOS and required as an explicit framework on iOS.
|
||||||
|
|||||||
1
tests/expected/141-objc-type-aliases.exit
Normal file
1
tests/expected/141-objc-type-aliases.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
1
tests/expected/141-objc-type-aliases.txt
Normal file
1
tests/expected/141-objc-type-aliases.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
isKindOfClass: 1
|
||||||
Reference in New Issue
Block a user