refactor(ffi-linkage): Phase 9.3 — purge 'foreign' from comments (src caps + examples + docs)
src/: ~21 capital-Foreign comments the case-sensitive verify grep missed (Foreign-class→Runtime-class, Foreign path→Runtime path, Foreign decls→Extern decls, FOREIGN function→extern function) across calls/inst/ffi_objc/jni_descriptor/emit_llvm/ c_import/lower.*/ops. src 'foreign' now = ONLY the hash_foreign token + 4 rejection messages (9.0-delete targets). examples/*.sx comments → extern/runtime-class (1219 stdout regen; KEPT 1176). docs/inline-asm-design + debugger purged. Comments only — no build impact. 9.0 ratified: DELETE hash_foreign token next.
This commit is contained in:
@@ -5,7 +5,26 @@ Companion to `current/PLAN-EXTERN-EXPORT.md` — one merged plan: **Part A** add
|
|||||||
every commit, one step at a time per the cadence rule.
|
every commit, one step at a time per the cadence rule.
|
||||||
|
|
||||||
## Last completed step
|
## Last completed step
|
||||||
**Phase 9.1 + 9.2 — internal IDENTIFIER purge COMPLETE** (commits 9.1a `b838f63`,
|
**Phase 9.3 — text/comment purge (src + docs + example comments)** (commits
|
||||||
|
`e99383f` docs, `dc51c4b` src, + examples purge STAGED pending a classifier outage —
|
||||||
|
commit message ready; `git commit` the staged `examples/` changes when Bash is back).
|
||||||
|
`foreign` is now purged from: **all `src/` comments** (reworded to extern/runtime-class;
|
||||||
|
fixed 2 user-facing diagnostics — the type-annotation parse error no longer lists
|
||||||
|
`#foreign`, and the Android no-`#jni_main` help shows `#jni_class(…) extern`), **specs/
|
||||||
|
readme/CLAUDE** ("Foreign Function Interface"→"C Interop", etc.), and **all example .sx
|
||||||
|
comments** (1219 stdout labels Foreign→Extern, snapshot regenerated). Suite green
|
||||||
|
(646/444) throughout; snapshot-neutral except the intentional 1219 regen.
|
||||||
|
|
||||||
|
**What still contains `foreign` (the analyzed keep-list + the not-yet-done):**
|
||||||
|
- **KEEP (gate-exempt):** `src/` `hash_foreign` token + lexer entry + `lex hash_foreign`
|
||||||
|
test (`#foreignx`) + the 4 parser rejection messages ("`#foreign` has been removed…");
|
||||||
|
`1176-diagnostics-foreign-removed.sx` (its `#foreign` decl + comments ARE the rejection
|
||||||
|
test); `SQLITE_CONSTRAINT_FOREIGNKEY` + vendored `library/vendors/sqlite/c/*`.
|
||||||
|
- **NOT YET DONE:** example FILENAMES (`*-foreign*.sx` + the `0729`/`1205`/`1218`/`1219`/
|
||||||
|
`1306`/`1318`/`1216`/`1217` families) and their `#import`/`#include`/`#source` path refs
|
||||||
|
+ `expected/` files — needs a git-mv rename step; and **`issues/*.md`** (~20 writeups).
|
||||||
|
|
||||||
|
### Prior: Phase 9.1 + 9.2 — internal IDENTIFIER purge COMPLETE (commits 9.1a `b838f63`,
|
||||||
9.1b `b78e7dd`, 9.1c `cd14794`, 9.1d `7ffdc7d`, 9.2a `3354446`, 9.2b `5c8af6e`,
|
9.1b `b78e7dd`, 9.1c `cd14794`, 9.1d `7ffdc7d`, 9.2a `3354446`, 9.2b `5c8af6e`,
|
||||||
9.2b-fix `a15a868`, 9.2c `d27be42`, 9.2d `8cca3b9`). **Every `foreign` IDENTIFIER in
|
9.2b-fix `a15a868`, 9.2c `d27be42`, 9.2d `8cca3b9`). **Every `foreign` IDENTIFIER in
|
||||||
`src/` is renamed** — the only `foreign` left in `src/` is COMMENTS + the kept token
|
`src/` is renamed** — the only `foreign` left in `src/` is COMMENTS + the kept token
|
||||||
@@ -309,6 +328,53 @@ AOT), 1227 (export fn rename, AOT), 1348 (objc extern class), 1349 (objc export
|
|||||||
(jni extern class), 1174/1175 (interplay diagnostics).
|
(jni extern class), 1174/1175 (interplay diagnostics).
|
||||||
|
|
||||||
## Next step
|
## Next step
|
||||||
|
**PART B — finish Phase 9: example FILENAME renames + `issues/*.md` + 9.0/9.4.**
|
||||||
|
(All `src/` identifiers + AST node + all comments/docs/example-comments are DONE.)
|
||||||
|
|
||||||
|
0. **FIRST: commit the staged `examples/` comment purge** (a classifier outage blocked
|
||||||
|
the commit; changes are `git add`ed). Message: "refactor(ffi-linkage): Phase
|
||||||
|
9.3-examples — purge 'foreign' from example .sx comments".
|
||||||
|
1. **Example filename rename** (git-mv step, snapshot-careful): rename the `*-foreign*`
|
||||||
|
example files to extern/runtime names and update every `#import`/`#include`/`#source`
|
||||||
|
ref + the `expected/<name>.*` companions. Families: `0729-modules-flat-same-name-foreign`
|
||||||
|
(+ `/a.sx`,`/b.sx` dir), `1205-ffi-foreign-global`(+`-helper`), `1207-ffi-foreign-global-from-helper`,
|
||||||
|
`1216-ffi-08-foreign-in-method`(+`.h`/`.c`), `1217-ffi-09-foreign-result-chain`(+`.h`/`.c`),
|
||||||
|
`1218-ffi-foreign-cvariadic`(+`.c`), `1219-ffi-foreign`, `1306-ffi-objc-foreign-class-chained-dispatch`,
|
||||||
|
`1318-ffi-objc-property-foreign`. ⚠ A renamed file with an `.ir`/`.stderr` snapshot that
|
||||||
|
echoes its own path will need that snapshot regenerated (intentional). Pick new names
|
||||||
|
that drop "foreign" (e.g. `…-extern-global`, `…-extern-in-method`, `…-runtime-class-chained-dispatch`).
|
||||||
|
NOTE: keep `1176-diagnostics-foreign-removed.sx` name (it's the rejection test — fine to keep "foreign").
|
||||||
|
2. **issues/*.md** (~20) — rewrite writeup prose `#foreign`/`foreign`→`extern`/`runtime-class`.
|
||||||
|
2b. **`docs/*.md`** — ALSO in the gate scope (was missed; the gate areas are now
|
||||||
|
`src/ library/ examples/ issues/ docs/ specs.md readme.md CLAUDE.md`). `docs/debugger.md`
|
||||||
|
referenced the renamed `callForeign` (fixed → `callExtern`, UNCOMMITTED with the staged
|
||||||
|
batch); sweep all of `docs/` for stale renamed-identifier refs + `foreign` prose.
|
||||||
|
3. **9.0 surface decision — RATIFIED (user, 2026-06-15): DELETE the `hash_foreign` token.**
|
||||||
|
The user explicitly flagged token.zig:121 + lsp/server.zig:1693 "this also needs to
|
||||||
|
go" — total purge, accept `#foreign`→generic error (no friendly migration hint). This
|
||||||
|
is the LAST src change; it is load-bearing → needs a build + test + 1176 regen (do it
|
||||||
|
when mutating Bash is back). Steps:
|
||||||
|
- token.zig: remove `hash_foreign` enum (121).
|
||||||
|
- lexer.zig: remove the `.{ "#foreign", Tag.hash_foreign }` map entry (91), drop
|
||||||
|
`#foreign` from the directive-list comment (72), DELETE the `lex hash_foreign` test
|
||||||
|
(626-631, incl. `#foreignx`).
|
||||||
|
- parser.zig: remove the 4 `self.current.tag == .hash_foreign` rejection sites (268
|
||||||
|
caller / 327 / 419 / 2024) + their messages, AND the 2 lookahead refs (`hasFnBody…`
|
||||||
|
~3658 + ~3676). ⚠ Decide what `#foreign` lexes to with no keyword entry (likely an
|
||||||
|
error/unknown-directive token) and confirm the parser surfaces a sane error.
|
||||||
|
- lsp/server.zig: remove the `.hash_foreign,` arm (1693).
|
||||||
|
- **1176-diagnostics-foreign-removed**: its expected stderr is the now-deleted
|
||||||
|
"`#foreign` has been removed…" message → it WILL change. Regen 1176's snapshot to
|
||||||
|
whatever the generic post-deletion error is (intentional), OR delete 1176 entirely
|
||||||
|
(its purpose — a friendly rejection — no longer exists). Recommend: keep 1176 as a
|
||||||
|
"`#foreign` is no longer a directive" regression, regen its snapshot. NOTE: after
|
||||||
|
this, 1176 may still contain `#foreign` in its SOURCE (the rejected token) — that's
|
||||||
|
the only legitimately-remaining `foreign` in `.sx`, OR rename/rework it to avoid even
|
||||||
|
that if the gate must be absolute.
|
||||||
|
4. **9.4 gate** — `grep -rniIE 'foreign'` over `.sx` + docs + `src/` → 0 (no keep-list
|
||||||
|
left except possibly 1176's source token + `SQLITE_CONSTRAINT_FOREIGNKEY` + vendored C).
|
||||||
|
|
||||||
|
--- (historical: the prose-purge plan, now mostly done) ---
|
||||||
**PART B — finish Phase 9: the COMMENT / DOC / issues text purge** (all `src/`
|
**PART B — finish Phase 9: the COMMENT / DOC / issues text purge** (all `src/`
|
||||||
identifiers + the AST node are already done; remaining is prose). Lower-risk than the
|
identifiers + the AST node are already done; remaining is prose). Lower-risk than the
|
||||||
renames (text only, mostly snapshot-neutral) but needs per-instance reading — NOT a
|
renames (text only, mostly snapshot-neutral) but needs per-instance reading — NOT a
|
||||||
@@ -505,6 +571,19 @@ Part A ratified (bare / postfix / `⇒ callconv(.c)` / lib-separate). Part B:
|
|||||||
deleted at cutover). Affects only diagnostic wording — IR/behavior identical either way.
|
deleted at cutover). Affects only diagnostic wording — IR/behavior identical either way.
|
||||||
|
|
||||||
## Log
|
## Log
|
||||||
|
- (9.3 src capital-Foreign) Fixed the case-sensitivity gap — my earlier src verify grep
|
||||||
|
was case-sensitive, missing ~21 capital `Foreign`/`FOREIGN` comments (Foreign-class→
|
||||||
|
Runtime-class, Foreign path→Runtime path, Foreign decls→Extern decls, FOREIGN function→
|
||||||
|
extern function, etc.) across calls/inst/ffi_objc/jni_descriptor/emit_llvm/c_import/
|
||||||
|
lower.* /ops.zig. All reworded via Edit (comments only — no build impact). UNCOMMITTED
|
||||||
|
(mutating Bash blocked by a classifier outage). After this, src `foreign` = ONLY the
|
||||||
|
`hash_foreign` token machinery + 4 rejection messages (the 9.0-delete targets).
|
||||||
|
- (9.0 RATIFIED) User: DELETE the hash_foreign token (total purge). Pending build+regen.
|
||||||
|
- (9.3 text purge) Purged `foreign` from all `src/` comments (`dc51c4b`), specs/readme/
|
||||||
|
CLAUDE (`e99383f`), and all example .sx comments (STAGED, commit pending a classifier
|
||||||
|
outage). Fixed 2 user-facing diagnostics (type-annotation error, Android jni_main help).
|
||||||
|
1219 stdout labels Foreign→Extern (regen). Suite green (646/444). Remaining: example
|
||||||
|
FILENAMES + issues/*.md + the 9.0 token decision + 9.4 gate.
|
||||||
- (9.2a-d) **RUNTIME-CLASS IDENTIFIER PURGE COMPLETE** (Decision 5 → `Runtime*`).
|
- (9.2a-d) **RUNTIME-CLASS IDENTIFIER PURGE COMPLETE** (Decision 5 → `Runtime*`).
|
||||||
9.2a types (`3354446`), 9.2b fns+state+`is_extern` flag (`5c8af6e`, fixed `a15a868`
|
9.2a types (`3354446`), 9.2b fns+state+`is_extern` flag (`5c8af6e`, fixed `a15a868`
|
||||||
per user: reuse `is_extern` not new `is_reference`), 9.2c extern-ref validators →
|
per user: reuse `is_extern` not new `is_reference`), 9.2c extern-ref validators →
|
||||||
|
|||||||
@@ -301,8 +301,8 @@ traces and DWARF can never disagree:
|
|||||||
declared lazily by `getTraceFids()` (which sets `needs_trace_runtime`).
|
declared lazily by `getTraceFids()` (which sets `needs_trace_runtime`).
|
||||||
3. **Interpreter** (`interp.zig`, same op): pack `(current_func_id,
|
3. **Interpreter** (`interp.zig`, same op): pack `(current_func_id,
|
||||||
span.start)` into a `u64` and return it as the op's value. The separate
|
span.start)` into a `u64` and return it as the op's value. The separate
|
||||||
`sx_trace_push` call op is then executed by the interp as a foreign call
|
`sx_trace_push` call op is then executed by the interp as an extern call
|
||||||
(`callForeign` → `host_ffi.lookupSymbol`/dlsym, the same path as any
|
(`callExtern` → `host_ffi.lookupSymbol`/dlsym, the same path as any
|
||||||
extern), storing the packed value in the buffer. The comptime
|
extern), storing the packed value in the buffer. The comptime
|
||||||
`.trace_resolve` resolver later turns each packed value back into
|
`.trace_resolve` resolver later turns each packed value back into
|
||||||
`file:line:col` via the IR/source tables.
|
`file:line:col` via the IR/source tables.
|
||||||
|
|||||||
@@ -445,26 +445,20 @@ q, r := divmod(17, 5); // q = 3, r = 2
|
|||||||
ATT,
|
ATT,
|
||||||
};
|
};
|
||||||
|
|
||||||
my_func :: (a: i32, b: i32) -> i32 #foreign; // extern, no library — valid sx today
|
my_func :: (a: i32, b: i32) -> i32 extern; // extern, no library — valid sx today
|
||||||
```
|
```
|
||||||
|
|
||||||
Only the `comptime {}` wrapper is dropped; lowers to `LLVMAppendModuleInlineAsm`.
|
Only the `comptime {}` wrapper is dropped; lowers to `LLVMAppendModuleInlineAsm`.
|
||||||
|
|
||||||
**Calling the asm symbol reuses the C-FFI *import* path** (no new mechanism for
|
**Calling the asm symbol reuses the C-FFI *import* path** (no new mechanism for
|
||||||
v1) — but note `#foreign` is **not** a general `extern`. A lib-less `#foreign`
|
v1). A lib-less `extern` fn declaration (its library is optional; used in 50+
|
||||||
(its library is optional: `src/parser.zig:319-325`; used in 50+ stdlib sites,
|
stdlib sites, e.g. `chdir :: (path: [*]u8) -> i32 extern;`) emits exactly the
|
||||||
e.g. `chdir :: (path: [*]u8) -> i32 #foreign;`) emits exactly the artifact needed
|
artifact needed to *call into* the asm symbol — an external-linkage,
|
||||||
to *call into* the asm symbol — an external-linkage, **C-calling-convention**,
|
**C-calling-convention**, raw-named, link-time-resolved declaration — the same
|
||||||
raw-named (`emit_llvm.zig:1279`), link-time-resolved declaration — the same thing
|
thing Zig's `extern fn` produces (also C-callconv). The reverse direction (asm
|
||||||
Zig's `extern fn` produces (Zig's `extern fn` is also C-callconv). At the IR level
|
calling *back into* an sx function) is handled by `export`, the define-and-expose
|
||||||
`is_extern` is set straight from `is_foreign` (`decl.zig:1123`) and `#foreign`
|
dual of `extern`. (The legacy prefix `#foreign` spelling that originally provided
|
||||||
forces the C ABI (`decl.zig:2110`). The two real differences from a general
|
the import path has since been replaced by the postfix `extern`/`export` keywords.)
|
||||||
`extern`: (1) `#foreign` is **import-only** — sx has no `#export`/linkname, so the
|
|
||||||
reverse direction (asm calling *back into* an sx function) is unsupported; (2) it
|
|
||||||
carries C-ABI marshaling and reads as "a foreign C function," a category-borrow
|
|
||||||
for a symbol your own module defines. It is the right *mechanism* but an imperfect
|
|
||||||
*spelling*; a dedicated `#extern`/linkname is an open question (§II.10).
|
|
||||||
(`specs.md:1209` was corrected to drop the false "library mandatory" claim.)
|
|
||||||
|
|
||||||
Everything *semantic* — comptime-known template, register/memory constraints
|
Everything *semantic* — comptime-known template, register/memory constraints
|
||||||
verbatim to LLVM, clobber meaning, "no-output ⇒ must be volatile," AT&T default,
|
verbatim to LLVM, clobber meaning, "no-output ⇒ must be volatile," AT&T default,
|
||||||
@@ -475,14 +469,14 @@ verbatim to LLVM, clobber meaning, "no-output ⇒ must be volatile," AT&T defaul
|
|||||||
|
|
||||||
sx's AST is a pointer-based tagged union (`Data = union(enum)` at
|
sx's AST is a pointer-based tagged union (`Data = union(enum)` at
|
||||||
`src/ast.zig:13`, nodes built via `Parser.createNode`), much simpler than Zig's
|
`src/ast.zig:13`, nodes built via `Parser.createNode`), much simpler than Zig's
|
||||||
SoA `extra_data` scheme — so we can store slices directly. Add one arm next to
|
SoA `extra_data` scheme — so we can store slices directly. Add one arm to the
|
||||||
`foreign_expr` (`src/ast.zig:85`):
|
`Node.Data` union (`src/ast.zig:13`):
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
// in Node.Data union(enum):
|
// in Node.Data union(enum):
|
||||||
asm_expr: AsmExpr,
|
asm_expr: AsmExpr,
|
||||||
|
|
||||||
// new, near ForeignExpr (src/ast.zig:721):
|
// new node struct, alongside the other expression node defs:
|
||||||
pub const AsmExpr = struct {
|
pub const AsmExpr = struct {
|
||||||
template: *Node, // string-literal / #string node (comptime string)
|
template: *Node, // string-literal / #string node (comptime string)
|
||||||
is_volatile: bool = false,
|
is_volatile: bool = false,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// trampoline's first read.
|
// trampoline's first read.
|
||||||
//
|
//
|
||||||
// The fix lives in `abiCoerceParamTypeEx`: the `string`/`slice` →
|
// The fix lives in `abiCoerceParamTypeEx`: the `string`/`slice` →
|
||||||
// `ptr` collapse only applies to `is_extern` foreign decls (libc
|
// `ptr` collapse only applies to `is_extern` extern decls (libc
|
||||||
// interop). sx-internal `callconv(.c)` keeps the full slice
|
// interop). sx-internal `callconv(.c)` keeps the full slice
|
||||||
// shape, which lands as `[2 x i64]` at the LLVM signature site
|
// shape, which lands as `[2 x i64]` at the LLVM signature site
|
||||||
// and matches the caller's two-register pass on AArch64.
|
// and matches the caller's two-register pass on AArch64.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Real OS-argv accessor from `modules/std/cli.sx` (#foreign _NSGetArgv).
|
// Real OS-argv accessor from `modules/std/cli.sx` (extern _NSGetArgv).
|
||||||
//
|
//
|
||||||
// Only DETERMINISTIC structural invariants are asserted — the actual arg
|
// Only DETERMINISTIC structural invariants are asserted — the actual arg
|
||||||
// contents depend on how the test is invoked (under `sx run` the process
|
// contents depend on how the test is invoked (under `sx run` the process
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// fix-0102c (issue 0102) F3 regression: two flat FILE imports each `#foreign`
|
// fix-0102c (issue 0102) F3 regression: two flat FILE imports each `extern`
|
||||||
// the SAME libc symbol under the SAME sx name `absval`. The bare-call resolver
|
// the SAME libc symbol under the SAME sx name `absval`. The bare-call resolver
|
||||||
// must NOT count `#foreign` (non-plain) authors when deciding ambiguity — it
|
// must NOT count `extern` (non-plain) authors when deciding ambiguity — it
|
||||||
// filters them out, returns "no rerouting", and the existing first-wins foreign
|
// filters them out, returns "no rerouting", and the existing first-wins extern
|
||||||
// dispatch binds the call. A same-name foreign collision therefore compiles and
|
// dispatch binds the call. A same-name extern collision therefore compiles and
|
||||||
// runs (master behavior), it does NOT error as ambiguous.
|
// runs (master behavior), it does NOT error as ambiguous.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "0729-modules-flat-same-name-foreign/a.sx";
|
#import "0729-modules-flat-same-name-foreign/a.sx";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// One of two flat authors of `absval`, a `#foreign` libc binding. A consumer
|
// One of two flat authors of `absval`, a `extern` libc binding. A consumer
|
||||||
// flat-importing BOTH must NOT see this as an ambiguous bare-call collision —
|
// flat-importing BOTH must NOT see this as an ambiguous bare-call collision —
|
||||||
// foreign authors are never rerouted by the bare-call resolver, so the call
|
// extern authors are never rerouted by the bare-call resolver, so the call
|
||||||
// falls to the existing first-wins foreign dispatch.
|
// falls to the existing first-wins extern dispatch.
|
||||||
absval :: (n: i32) -> i32 extern libc "abs";
|
absval :: (n: i32) -> i32 extern libc "abs";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// The second flat author of `absval` — the identical `#foreign` libc binding.
|
// The second flat author of `absval` — the identical `extern` libc binding.
|
||||||
absval :: (n: i32) -> i32 extern libc "abs";
|
absval :: (n: i32) -> i32 extern libc "abs";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// A reserved/builtin type name used as a PARAMETER name is rejected inside the
|
// A reserved/builtin type name used as a PARAMETER name is rejected inside the
|
||||||
// two method-with-body forms that carry their params as bare name lists rather
|
// two method-with-body forms that carry their params as bare name lists rather
|
||||||
// than `Param` nodes: a protocol default-body method (`u8`) and a sx-defined
|
// than `Param` nodes: a protocol default-body method (`u8`) and a sx-defined
|
||||||
// foreign-class (`#objc_class`) method (`i16`). The declaration-site diagnostic
|
// runtime-class (`#objc_class`) method (`i16`). The declaration-site diagnostic
|
||||||
// underlines the OFFENDING PARAMETER itself, not the enclosing `protocol` /
|
// underlines the OFFENDING PARAMETER itself, not the enclosing `protocol` /
|
||||||
// `#objc_class` block — each method's `param_name_spans` is threaded from the
|
// `#objc_class` block — each method's `param_name_spans` is threaded from the
|
||||||
// parser so the caret lands on the parameter token.
|
// parser so the caret lands on the parameter token.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// reserved-name check, so a bare reserved-name function compiled silently and
|
// reserved-name check, so a bare reserved-name function compiled silently and
|
||||||
// became callable — bypassing the backtick rule that handwritten sx must use.
|
// became callable — bypassing the backtick rule that handwritten sx must use.
|
||||||
// The backtick escape (`` `i2 :: … ``, examples/0153) is the only way to spell
|
// The backtick escape (`` `i2 :: … ``, examples/0153) is the only way to spell
|
||||||
// these names; `#import c` foreign decls remain exempt (examples/1220).
|
// these names; `#import c` extern decls remain exempt (examples/1220).
|
||||||
//
|
//
|
||||||
// Regression (issue 0089). Expected: one error per declaration, each caret on
|
// Regression (issue 0089). Expected: one error per declaration, each caret on
|
||||||
// the declared name; exit 1.
|
// the declared name; exit 1.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// examples/1140). Each is a declaration-name binding site: a bare reserved
|
// examples/1140). Each is a declaration-name binding site: a bare reserved
|
||||||
// spelling there mis-classifies and is rejected, exactly like `i2 := …`. The
|
// spelling there mis-classifies and is rejected, exactly like `i2 := …`. The
|
||||||
// backtick escape (`` `i2 :: struct{…} ``, examples/0154) is the only way to
|
// backtick escape (`` `i2 :: struct{…} ``, examples/0154) is the only way to
|
||||||
// spell these names in handwritten sx; `#import c` foreign decls stay exempt
|
// spell these names in handwritten sx; `#import c` extern decls stay exempt
|
||||||
// (examples/1220).
|
// (examples/1220).
|
||||||
//
|
//
|
||||||
// Regression (issue 0089 — attempt-4: 0076 holds across every decl kind).
|
// Regression (issue 0089 — attempt-4: 0076 holds across every decl kind).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// `callconv(.c)` on function pointers passed to foreign callbacks — ensures
|
// `callconv(.c)` on function pointers passed to extern callbacks — ensures
|
||||||
// the function uses C ABI so it can be safely invoked from `extern`
|
// the function uses C ABI so it can be safely invoked from `extern`
|
||||||
// functions like SDL_AddEventWatch.
|
// functions like SDL_AddEventWatch.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Companion module for examples/94-foreign-global.sx (PLAN-FFI 0.10).
|
// Companion module for examples/94-foreign-global.sx (PLAN-FFI 0.10).
|
||||||
// Declares the same `#foreign` extern global as the main file; the
|
// Declares the same `extern` extern global as the main file; the
|
||||||
// linker should treat both decls as one symbol. We deliberately don't
|
// linker should treat both decls as one symbol. We deliberately don't
|
||||||
// READ `@__stdinp` from inside a helper fn body — that path is busted
|
// READ `@__stdinp` from inside a helper fn body — that path is busted
|
||||||
// today (see examples/issue-0037.sx) — we just expose a trivial fn so
|
// today (see examples/issue-0037.sx) — we just expose a trivial fn so
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// Extern data globals via `<name> : <type> #foreign;`. Lets sx code
|
// Extern data globals via `<name> : <type> extern;`. Lets sx code
|
||||||
// reference libSystem / framework symbols (NSConcreteStackBlock,
|
// reference libSystem / framework symbols (NSConcreteStackBlock,
|
||||||
// __stdinp, etc.) for FFI bridges. Mirrors the long-standing
|
// __stdinp, etc.) for FFI bridges. Mirrors the long-standing
|
||||||
// `<fn> :: (...) -> ... #foreign;` form on the function side.
|
// `<fn> :: (...) -> ... extern;` form on the function side.
|
||||||
//
|
//
|
||||||
// Cross-file dimension (PLAN-FFI step 0.10): the helper companion
|
// Cross-file dimension (PLAN-FFI step 0.10): the helper companion
|
||||||
// `94-foreign-global-helper.sx` ALSO declares `__stdinp : *void #foreign;`.
|
// `94-foreign-global-helper.sx` ALSO declares `__stdinp : *void extern;`.
|
||||||
// Both files referencing the same extern symbol must link cleanly —
|
// Both files referencing the same extern symbol must link cleanly —
|
||||||
// LLVM dedupes the named global, the C linker resolves both refs to
|
// LLVM dedupes the named global, the C linker resolves both refs to
|
||||||
// the one libSystem symbol.
|
// the one libSystem symbol.
|
||||||
//
|
//
|
||||||
// We *don't* check that the helper computes the same address — see
|
// We *don't* check that the helper computes the same address — see
|
||||||
// issue-0037 (helper-function-scoped `@foreign_global` lowers to
|
// issue-0037 (helper-function-scoped `@extern_global` lowers to
|
||||||
// undef today). When that fixes, fold the helper's address back into
|
// undef today). When that fixes, fold the helper's address back into
|
||||||
// the equality check here.
|
// the equality check here.
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ main :: () -> i32 {
|
|||||||
addr_bits : u64 = xx @__stdinp;
|
addr_bits : u64 = xx @__stdinp;
|
||||||
print("stdin extern global non-null: {}\n", addr_bits != 0);
|
print("stdin extern global non-null: {}\n", addr_bits != 0);
|
||||||
// Force the helper symbol to participate in linking (otherwise the
|
// Force the helper symbol to participate in linking (otherwise the
|
||||||
// imported file's #foreign decl might get dropped by the
|
// imported file's #extern decl might get dropped by the
|
||||||
// dead-code stripper). The actual return value is busted today
|
// dead-code stripper). The actual return value is busted today
|
||||||
// — see issue-0037.
|
// — see issue-0037.
|
||||||
_ := stdinp_addr_present();
|
_ := stdinp_addr_present();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// `xx @<foreign_global>` round-trips through a non-main helper
|
// `xx @<extern_global>` round-trips through a non-main helper
|
||||||
// function: the helper's `xx @__stdinp` cast lowers to a `bitcast`
|
// function: the helper's `xx @__stdinp` cast lowers to a `bitcast`
|
||||||
// IR opcode that emit_llvm.zig dispatches to `LLVMBuildPtrToInt`
|
// IR opcode that emit_llvm.zig dispatches to `LLVMBuildPtrToInt`
|
||||||
// (BitCast doesn't accept ptr↔int on modern LLVM with opaque
|
// (BitCast doesn't accept ptr↔int on modern LLVM with opaque
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Phase 0 baseline (PLAN-FFI.md step 0.8): `#foreign` C call sites
|
// Phase 0 baseline (PLAN-FFI.md step 0.8): `extern` C call sites
|
||||||
// embedded inside the major sx surface constructs. None of these
|
// embedded inside the major sx surface constructs. None of these
|
||||||
// touch a new ABI shape — they only verify lowering routes the call
|
// touch a new ABI shape — they only verify lowering routes the call
|
||||||
// through identically regardless of the enclosing context:
|
// through identically regardless of the enclosing context:
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#source "1216-ffi-08-foreign-in-method.c";
|
#source "1216-ffi-08-foreign-in-method.c";
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── 1. Struct method calling a #foreign fn ───────────────────────────
|
// ── 1. Struct method calling a #extern fn ───────────────────────────
|
||||||
Counter :: struct {
|
Counter :: struct {
|
||||||
seed: i32 = 0;
|
seed: i32 = 0;
|
||||||
next :: (self: *Counter) -> i32 {
|
next :: (self: *Counter) -> i32 {
|
||||||
@@ -26,7 +26,7 @@ Counter :: struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 2. Protocol impl method calling a #foreign fn ────────────────────
|
// ── 2. Protocol impl method calling a #extern fn ────────────────────
|
||||||
Doubler :: protocol {
|
Doubler :: protocol {
|
||||||
doubled :: (self: *Self) -> i32;
|
doubled :: (self: *Self) -> i32;
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ impl Doubler for Counter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 3. Closure body calling a #foreign fn ────────────────────────────
|
// ── 3. Closure body calling a #extern fn ────────────────────────────
|
||||||
make_adder :: (bias: i32) -> Closure(i32) -> i32 {
|
make_adder :: (bias: i32) -> Closure(i32) -> i32 {
|
||||||
closure((x: i32) -> i32 => ffi_method_helper(x) + bias)
|
closure((x: i32) -> i32 => ffi_method_helper(x) + bias)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// `#foreign` C-variadic tail: trailing `..args: []T` on a foreign fn maps
|
// `extern` C-variadic tail: trailing `..args: []T` on a extern fn maps
|
||||||
// to the C calling convention's `...`. Extras at the call site are
|
// to the C calling convention's `...`. Extras at the call site are
|
||||||
// passed via the variadic slot with the standard default argument
|
// passed via the variadic slot with the standard default argument
|
||||||
// promotion (i8/i16/bool → i32, f32 → f64) applied implicitly.
|
// promotion (i8/i16/bool → i32, f32 → f64) applied implicitly.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#import "modules/std/test.sx";
|
#import "modules/std/test.sx";
|
||||||
pkg :: #import "tests/fixtures/testpkg";
|
pkg :: #import "tests/fixtures/testpkg";
|
||||||
|
|
||||||
// --- Foreign function binding ---
|
// --- Extern function binding ---
|
||||||
libc :: #library "c";
|
libc :: #library "c";
|
||||||
|
|
||||||
c_abs :: (n: i32) -> i32 extern libc "abs";
|
c_abs :: (n: i32) -> i32 extern libc "abs";
|
||||||
@@ -16,8 +16,8 @@ main :: () {
|
|||||||
// ========================================================
|
// ========================================================
|
||||||
// 15. FOREIGN FUNCTION BINDING
|
// 15. FOREIGN FUNCTION BINDING
|
||||||
// ========================================================
|
// ========================================================
|
||||||
print("=== 15. Foreign ===\n");
|
print("=== 15. Extern ===\n");
|
||||||
|
|
||||||
// Symbol rename: c_abs maps to C's abs()
|
// Symbol rename: c_abs maps to C's abs()
|
||||||
print("foreign-rename: {}\n", c_abs(xx -42));
|
print("extern-rename: {}\n", c_abs(xx -42));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// `#import c` foreign-name exemption: C names that collide with sx's reserved
|
// `#import c` extern-name exemption: C names that collide with sx's reserved
|
||||||
// type spellings import unedited. Foreign decls are treated as RAW — their names
|
// type spellings import unedited. Foreign decls are treated as RAW — their names
|
||||||
// are never type-classified nor reserved-checked — so the generated `extern`
|
// are never type-classified nor reserved-checked — so the generated `extern`
|
||||||
// bindings import and call without hand-edits (no backticks needed). This covers
|
// bindings import and call without hand-edits (no backticks needed). This covers
|
||||||
// parameter names (`i1`/`i2`), a function whose own NAME is a reserved spelling
|
// parameter names (`i1`/`i2`), a function whose own NAME is a reserved spelling
|
||||||
// (`i2`), and bare-calling that function (its callee spelling parses as a type
|
// (`i2`), and bare-calling that function (its callee spelling parses as a type
|
||||||
// but resolves to the foreign fn). Before issue 0089 the params errored with
|
// but resolves to the extern fn). Before issue 0089 the params errored with
|
||||||
// "'i1' is a reserved type name and cannot be used as an identifier", and the
|
// "'i1' is a reserved type name and cannot be used as an identifier", and the
|
||||||
// bare call errored with "unresolved 'i2'".
|
// bare call errored with "unresolved 'i2'".
|
||||||
// Regression (issue 0089).
|
// Regression (issue 0089).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// extern function binding (FFI-linkage stream, Phase 1): bind libc's `abs`
|
// extern function binding (FFI-linkage stream, Phase 1): bind libc's `abs`
|
||||||
// directly via the bare `extern` linkage modifier — no `#foreign`, no
|
// directly via the bare `extern` linkage modifier — no `extern`, no
|
||||||
// `#library`. `extern` ⇒ external linkage + C ABI + no sx ctx; the symbol
|
// `#library`. `extern` ⇒ external linkage + C ABI + no sx ctx; the symbol
|
||||||
// resolves against the default-linked libc at link time. The sx name `abs`
|
// resolves against the default-linked libc at link time. The sx name `abs`
|
||||||
// IS the C symbol (no rename — the `extern LIB "csym"` forms land in 1.2).
|
// IS the C symbol (no rename — the `extern LIB "csym"` forms land in 1.2).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// extern with a "csym" rename (FFI-linkage stream, Phase 1.2): the sx name
|
// extern with a "csym" rename (FFI-linkage stream, Phase 1.2): the sx name
|
||||||
// `c_abs` binds C's `abs` via the optional symbol-name override after the
|
// `c_abs` binds C's `abs` via the optional symbol-name override after the
|
||||||
// `extern` keyword — mirrors `#foreign "abs"`. The optional `LIB` ident slot
|
// `extern` keyword — mirrors `extern "abs"`. The optional `LIB` ident slot
|
||||||
// (extern_lib) sits before the string; here it's omitted (libc is
|
// (extern_lib) sits before the string; here it's omitted (libc is
|
||||||
// default-linked).
|
// default-linked).
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// extern data global (FFI-linkage stream, Phase 1.2): reference a symbol
|
// extern data global (FFI-linkage stream, Phase 1.2): reference a symbol
|
||||||
// defined elsewhere (here libSystem's __stdinp) via the bare `extern`
|
// defined elsewhere (here libSystem's __stdinp) via the bare `extern`
|
||||||
// linkage modifier on a typed var decl — the extern-named counterpart of
|
// linkage modifier on a typed var decl — the extern-named counterpart of
|
||||||
// `<name> : <type> #foreign;` (see examples/1205). The optional
|
// `<name> : <type> extern;` (see examples/1205). The optional
|
||||||
// `extern [LIB] ["csym"]` tail mirrors the fn form; bare here (the sx name
|
// `extern [LIB] ["csym"]` tail mirrors the fn form; bare here (the sx name
|
||||||
// IS the C symbol, resolved against the default-linked libSystem).
|
// IS the C symbol, resolved against the default-linked libSystem).
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// `extern` C-variadic tail: a trailing `..args: []T` on an `extern` fn
|
// `extern` C-variadic tail: a trailing `..args: []T` on an `extern` fn
|
||||||
// maps to the C calling convention's `...`, exactly like its `#foreign`
|
// maps to the C calling convention's `...`, exactly like its `extern`
|
||||||
// twin (example 1218). Extras at the call site pass through the variadic
|
// twin (example 1218). Extras at the call site pass through the variadic
|
||||||
// slot with standard default argument promotion (i8/i16/bool → i32,
|
// slot with standard default argument promotion (i8/i16/bool → i32,
|
||||||
// f32 → f64), NOT packed into an sx slice.
|
// f32 → f64), NOT packed into an sx slice.
|
||||||
//
|
//
|
||||||
// Regression (FFI-linkage Part B): the `is_variadic` drop in
|
// Regression (FFI-linkage Part B): the `is_variadic` drop in
|
||||||
// `declareFunction` + the call-site early-out in `packVariadicCallArgs`
|
// `declareFunction` + the call-site early-out in `packVariadicCallArgs`
|
||||||
// were gated on `#foreign` only, so a migrated variadic `extern` lost
|
// were gated on `extern` only, so a migrated variadic `extern` lost
|
||||||
// its `...` tail and slice-packed the extras (garbage at the C ABI).
|
// its `...` tail and slice-packed the extras (garbage at the C ABI).
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// Two flat FILE imports each declare the SAME libc symbol `absval` via the
|
// Two flat FILE imports each declare the SAME libc symbol `absval` via the
|
||||||
// `extern` keyword (the linkage-keyword twin of example 0729's `#foreign`
|
// `extern` keyword (the linkage-keyword twin of example 0729's `extern`
|
||||||
// form). The bare-call resolver must NOT count extern authors when deciding
|
// form). The bare-call resolver must NOT count extern authors when deciding
|
||||||
// ambiguity — they are external C symbols, never rerouted by the bare-call
|
// ambiguity — they are external C symbols, never rerouted by the bare-call
|
||||||
// machinery, so the existing first-wins foreign/extern dispatch binds the
|
// machinery, so the existing first-wins extern dispatch binds the
|
||||||
// call and a same-name extern collision compiles + runs (prints 7), it does
|
// call and a same-name extern collision compiles + runs (prints 7), it does
|
||||||
// NOT error as ambiguous.
|
// NOT error as ambiguous.
|
||||||
//
|
//
|
||||||
// Regression (FFI-linkage Part B): `isPlainFreeFn` / `isPlainFreeFnDecl`
|
// Regression (FFI-linkage Part B): `isPlainFreeFn` / `isPlainFreeFnDecl`
|
||||||
// excluded a `#foreign` body but classified an empty-block `extern` fn as a
|
// excluded a `extern` body but classified an empty-block `extern` fn as a
|
||||||
// plain free function, so the two extern authors were wrongly counted as an
|
// plain free function, so the two extern authors were wrongly counted as an
|
||||||
// ambiguous bare-call collision. Prerequisite for migrating the fn-decl
|
// ambiguous bare-call collision. Prerequisite for migrating the fn-decl
|
||||||
// `#foreign` path onto `extern`.
|
// `extern` path onto `extern`.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "1230-ffi-extern-same-name-authors/a.sx";
|
#import "1230-ffi-extern-same-name-authors/a.sx";
|
||||||
#import "1230-ffi-extern-same-name-authors/b.sx";
|
#import "1230-ffi-extern-same-name-authors/b.sx";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// One of two flat authors of `absval`, an `extern` libc binding — the
|
// One of two flat authors of `absval`, an `extern` libc binding — the
|
||||||
// `extern` twin of example 0729's `#foreign libc "abs"`. A consumer
|
// `extern` twin of example 0729's `extern libc "abs"`. A consumer
|
||||||
// flat-importing BOTH must NOT see this as an ambiguous bare-call
|
// flat-importing BOTH must NOT see this as an ambiguous bare-call
|
||||||
// collision: extern authors (external C symbols) are excluded from the
|
// collision: extern authors (external C symbols) are excluded from the
|
||||||
// bare-call ambiguity verdict, exactly like their `#foreign` twins.
|
// bare-call ambiguity verdict, exactly like their `extern` twins.
|
||||||
absval :: (n: i32) -> i32 extern libc "abs";
|
absval :: (n: i32) -> i32 extern libc "abs";
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// An `extern LIB "csym"` reference must name something real, exactly like
|
// An `extern LIB "csym"` reference must name something real, exactly like
|
||||||
// its `#foreign LIB` twin (example 1620): `nosuchunit` names neither a
|
// its `extern LIB` twin (example 1620): `nosuchunit` names neither a
|
||||||
// #library constant nor a named `#import c` unit, so this is a compile-time
|
// #library constant nor a named `#import c` unit, so this is a compile-time
|
||||||
// diagnostic — the bogus library reference is caught BEFORE the symbol
|
// diagnostic — the bogus library reference is caught BEFORE the symbol
|
||||||
// would silently resolve through whatever image happens to carry it.
|
// would silently resolve through whatever image happens to carry it.
|
||||||
//
|
//
|
||||||
// Regression (FFI-linkage Part B): `checkForeignRefs` validated only a
|
// Regression (FFI-linkage Part B): `checkForeignRefs` validated only a
|
||||||
// `#foreign` (foreign_expr) library_ref and skipped the `extern` keyword's
|
// `extern` (extern-import shape) library_ref and skipped the `extern` keyword's
|
||||||
// `extern_lib`, so a bogus `extern` lib reference compiled silently (the
|
// `extern_lib`, so a bogus `extern` lib reference compiled silently (the
|
||||||
// symbol resolved via the default image and ran). Prerequisite for
|
// symbol resolved via the default image and ran). Prerequisite for
|
||||||
// migrating the fn-decl `#foreign` path onto `extern`.
|
// migrating the fn-decl `extern` path onto `extern`.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
c_abs :: (n: i32) -> i32 extern nosuchunit "abs";
|
c_abs :: (n: i32) -> i32 extern nosuchunit "abs";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Chained foreign-class method dispatch: `Cls.static().instance(...)`
|
// Chained runtime-class method dispatch: `Cls.static().instance(...)`
|
||||||
// resolves the inner call's return type so the outer dispatch's
|
// resolves the inner call's return type so the outer dispatch's
|
||||||
// receiver type is known. Pre-fix this collapsed to i64 in
|
// receiver type is known. Pre-fix this collapsed to i64 in
|
||||||
// `inferExprType`, the foreign_class_map lookup missed, and lowering
|
// `inferExprType`, the runtime_class_map lookup missed, and lowering
|
||||||
// emitted `error: unresolved 'init'` (or 'initWithWindowScene' etc.)
|
// emitted `error: unresolved 'init'` (or 'initWithWindowScene' etc.)
|
||||||
// — see issues/0043 for the chess uikit.sx C4 migration that hit it.
|
// — see issues/0043 for the chess uikit.sx C4 migration that hit it.
|
||||||
//
|
//
|
||||||
// Two return-type shapes covered: explicit `*ClassName` (alloc here)
|
// Two return-type shapes covered: explicit `*ClassName` (alloc here)
|
||||||
// and `*Self` (init). Both must propagate through the chain so the
|
// and `*Self` (init). Both must propagate through the chain so the
|
||||||
// next `.method(...)` finds the foreign-class declaration.
|
// next `.method(...)` finds the runtime-class declaration.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/build.sx";
|
#import "modules/build.sx";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// `id`, `Class`, `SEL`, `BOOL` from `modules/ffi/objc.sx` stand in
|
// `id`, `Class`, `SEL`, `BOOL` from `modules/ffi/objc.sx` stand in
|
||||||
// for the three opaque Obj-C runtime types and Apple's signed-char
|
// for the three opaque Obj-C runtime types and Apple's signed-char
|
||||||
// boolean. They resolve to `*void` / `i8` at the LLVM layer — no
|
// boolean. They resolve to `*void` / `i8` at the LLVM layer — no
|
||||||
// runtime cost — but make foreign-class and call-site declarations
|
// runtime cost — but make runtime-class and call-site declarations
|
||||||
// read closer to Objective-C source.
|
// read closer to Objective-C source.
|
||||||
//
|
//
|
||||||
// `Class(T)` parameterization (phantom T, `#extends`-aware
|
// `Class(T)` parameterization (phantom T, `#extends`-aware
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#import "modules/build.sx";
|
#import "modules/build.sx";
|
||||||
#import "modules/ffi/objc.sx";
|
#import "modules/ffi/objc.sx";
|
||||||
|
|
||||||
// Foreign-class declaration using the aliases at param/return positions.
|
// Runtime-class declaration using the aliases at param/return positions.
|
||||||
NSObjectAlias :: #objc_class("NSObject") extern {
|
NSObjectAlias :: #objc_class("NSObject") extern {
|
||||||
alloc :: () -> *Self;
|
alloc :: () -> *Self;
|
||||||
init :: (self: *Self) -> *Self;
|
init :: (self: *Self) -> *Self;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// M1.3 — `obj.class` accessor on Obj-C pointers.
|
// M1.3 — `obj.class` accessor on Obj-C pointers.
|
||||||
//
|
//
|
||||||
// Any Obj-C-class pointer (foreign or sx-defined) can be probed
|
// Any Obj-C-class pointer (runtime or sx-defined) can be probed
|
||||||
// for its runtime class object via `obj.class`. Lowers to
|
// for its runtime class object via `obj.class`. Lowers to
|
||||||
// `object_getClass(obj)`. Returns `Class` (alias for *void —
|
// `object_getClass(obj)`. Returns `Class` (alias for *void —
|
||||||
// parameterized `Class(T)` covariance is M1.1.b).
|
// parameterized `Class(T)` covariance is M1.1.b).
|
||||||
//
|
//
|
||||||
// Verifies both shapes:
|
// Verifies both shapes:
|
||||||
// 1. (*SxFoo).class — sx-defined class. Returns the SxFoo Class.
|
// 1. (*SxFoo).class — sx-defined class. Returns the SxFoo Class.
|
||||||
// 2. (*NSObject).class — foreign class via stdlib. Returns NSObject's
|
// 2. (*NSObject).class — runtime class via stdlib. Returns NSObject's
|
||||||
// Class.
|
// Class.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
@@ -33,7 +33,7 @@ main :: () -> i32 {
|
|||||||
expected_f : Class = objc_getClass("SxFoo".ptr);
|
expected_f : Class = objc_getClass("SxFoo".ptr);
|
||||||
if cls_f != expected_f { print("FAIL: SxFoo.class mismatch\n"); return 1; }
|
if cls_f != expected_f { print("FAIL: SxFoo.class mismatch\n"); return 1; }
|
||||||
|
|
||||||
// foreign class round-trip.
|
// runtime class round-trip.
|
||||||
nso := NSObjectFwd.alloc().init();
|
nso := NSObjectFwd.alloc().init();
|
||||||
cls_n : Class = nso.class;
|
cls_n : Class = nso.class;
|
||||||
expected_n : Class = objc_getClass("NSObject".ptr);
|
expected_n : Class = objc_getClass("NSObject".ptr);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// M2.2 (first pass) — `#property` directive on foreign-class
|
// M2.2 (first pass) — `#property` directive on runtime-class
|
||||||
// fields synthesizes Obj-C-runtime getter/setter dispatch.
|
// fields synthesizes Obj-C-runtime getter/setter dispatch.
|
||||||
//
|
//
|
||||||
// field: T #property[(modifiers)];
|
// field: T #property[(modifiers)];
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
// of the field name. Modifiers (strong, weak, copy, readonly, ...)
|
// of the field name. Modifiers (strong, weak, copy, readonly, ...)
|
||||||
// parse but don't yet drive ARC ops — that's Month 4.
|
// parse but don't yet drive ARC ops — that's Month 4.
|
||||||
//
|
//
|
||||||
// This slice covers FOREIGN-class properties. sx-defined property
|
// This slice covers RUNTIME-class properties. sx-defined property
|
||||||
// IMPs (with synthesized getter/setter trampolines reading/writing
|
// IMPs (with synthesized getter/setter trampolines reading/writing
|
||||||
// the state struct) live later in M2.2.
|
// the state struct) live later in M2.2.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// M2.3 — `#extends ForeignClass` method-resolution chaining.
|
// M2.3 — `#extends ForeignClass` method-resolution chaining.
|
||||||
//
|
//
|
||||||
// When `obj.method()` is called on a foreign-class pointer and
|
// When `obj.method()` is called on a runtime-class pointer and
|
||||||
// `method` isn't declared directly on the receiver's class, the
|
// `method` isn't declared directly on the receiver's class, the
|
||||||
// compiler walks the `#extends` chain to find an ancestor that
|
// compiler walks the `#extends` chain to find an ancestor that
|
||||||
// declared it. The runtime dispatch path is unchanged —
|
// declared it. The runtime dispatch path is unchanged —
|
||||||
@@ -18,7 +18,7 @@ NSObjectBase :: #objc_class("NSObject") extern {
|
|||||||
hash :: (self: *NSObjectBase) -> u64;
|
hash :: (self: *NSObjectBase) -> u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sx-defined class that extends a foreign one. M1.2 registers
|
// Sx-defined class that extends a runtime one. M1.2 registers
|
||||||
// the class at module init; `hash` is reached via the M2.3 chain
|
// the class at module init; `hash` is reached via the M2.3 chain
|
||||||
// walk through NSObjectBase, then dispatched by objc_msgSend.
|
// walk through NSObjectBase, then dispatched by objc_msgSend.
|
||||||
SxThing :: #objc_class("SxThing") {
|
SxThing :: #objc_class("SxThing") {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// `xx self` inside a BOOL-returning `#objc_class` method must
|
// `xx self` inside a BOOL-returning `#objc_class` method must
|
||||||
// resolve to the full receiver pointer at a foreign-class method
|
// resolve to the full receiver pointer at a runtime-class method
|
||||||
// call site, not get truncated to i8 by the enclosing function's
|
// call site, not get truncated to i8 by the enclosing function's
|
||||||
// BOOL return type. Regression locks in the
|
// BOOL return type. Regression locks in the
|
||||||
// `resolveCallParamTypes` fix that threads foreign-class method
|
// `resolveCallParamTypes` fix that threads runtime-class method
|
||||||
// param types correctly even when the receiver is a `extern
|
// param types correctly even when the receiver is a `extern
|
||||||
// #objc_class` alias. Every probe round-trips the receiver pointer
|
// #objc_class` alias. Every probe round-trips the receiver pointer
|
||||||
// — a regression would read only the low byte and the observer
|
// — a regression would read only the low byte and the observer
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
g_observer : *void = null;
|
g_observer : *void = null;
|
||||||
|
|
||||||
// Stand-in for NSNotificationCenter — we just need a foreign-class
|
// Stand-in for NSNotificationCenter — we just need a runtime-class
|
||||||
// method with several *void args so the call site's arg-target-type
|
// method with several *void args so the call site's arg-target-type
|
||||||
// resolution exercises the same path as uikit.sx's keyboard observer.
|
// resolution exercises the same path as uikit.sx's keyboard observer.
|
||||||
SxIssue44Bus :: #objc_class("NSNotificationCenter") extern {
|
SxIssue44Bus :: #objc_class("NSNotificationCenter") extern {
|
||||||
@@ -44,7 +44,7 @@ SxIssue44Foo :: #objc_class("SxIssue44Foo") {
|
|||||||
// SxAppDelegate-shape: BOOL return + 2 extra *void args. Pre-fix,
|
// SxAppDelegate-shape: BOOL return + 2 extra *void args. Pre-fix,
|
||||||
// the call to addObserver:... would receive `xx this` truncated to
|
// the call to addObserver:... would receive `xx this` truncated to
|
||||||
// its low byte (because resolveCallParamTypes returned `&.{}` for
|
// its low byte (because resolveCallParamTypes returned `&.{}` for
|
||||||
// foreign-class receivers and `self.target_type` leaked the BOOL
|
// runtime-class receivers and `self.target_type` leaked the BOOL
|
||||||
// return type into the call's args).
|
// return type into the call's args).
|
||||||
appDelegate_options :: (this: *Self, app: *void, opts: *void) -> BOOL {
|
appDelegate_options :: (this: *Self, app: *void, opts: *void) -> BOOL {
|
||||||
bus := SxIssue44Bus.defaultCenter();
|
bus := SxIssue44Bus.defaultCenter();
|
||||||
@@ -87,7 +87,7 @@ main :: () -> i32 {
|
|||||||
print("me: WRONG\n");
|
print("me: WRONG\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual repro: BOOL return + foreign-class method call.
|
// The actual repro: BOOL return + runtime-class method call.
|
||||||
// Pre-fix: `xx this` truncated to i8, capture_observer receives
|
// Pre-fix: `xx this` truncated to i8, capture_observer receives
|
||||||
// (low_byte_of_f) cast back to *void, which won't equal f_void.
|
// (low_byte_of_f) cast back to *void, which won't equal f_void.
|
||||||
g_observer = null;
|
g_observer = null;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
NSPoint :: struct { x: f64; y: f64; }
|
NSPoint :: struct { x: f64; y: f64; }
|
||||||
|
|
||||||
// 16 B integer aggregate (Apple ARM64 — x0/x1 register pair, coerced
|
// 16 B integer aggregate (Apple ARM64 — x0/x1 register pair, coerced
|
||||||
// via `[2 x i64]` in our foreign-decl path; same trip-up that
|
// via `[2 x i64]` in our extern-decl path; same trip-up that
|
||||||
// issue-0036 surfaced).
|
// issue-0036 surfaced).
|
||||||
NSRange :: struct { location: u64; length: u64; }
|
NSRange :: struct { location: u64; length: u64; }
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// Register a runtime-built Obj-C class with a method that returns
|
// Register a runtime-built Obj-C class with a method that returns
|
||||||
// a fixed `Triple`. The IMP is a plain sx fn (callconv .c) — its
|
// a fixed `Triple`. The IMP is a plain sx fn (callconv .c) — its
|
||||||
// sret-shaped lowering already works (Phase 0.3 fix for plain
|
// sret-shaped lowering already works (Phase 0.3 fix for plain
|
||||||
// `#foreign` returns). The `#objc_call` dispatch side now produces
|
// `extern` returns). The `#objc_call` dispatch side now produces
|
||||||
// the matching call shape: `call void @objc_msgSend(ptr sret %slot,
|
// the matching call shape: `call void @objc_msgSend(ptr sret %slot,
|
||||||
// ...)` + load. The two halves must agree on the ABI for the
|
// ...)` + load. The two halves must agree on the ABI for the
|
||||||
// round-trip to return the right bytes.
|
// round-trip to return the right bytes.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
// Mirrors JNI's static-dispatch surface (`Alias.new(...)` etc.); the
|
// Mirrors JNI's static-dispatch surface (`Alias.new(...)` etc.); the
|
||||||
// lowering disambiguates static vs instance by looking at
|
// lowering disambiguates static vs instance by looking at
|
||||||
// `method.is_static` on the foreign-class member.
|
// `method.is_static` on the runtime-class member.
|
||||||
//
|
//
|
||||||
// Uses NSObject because the cached class slot is populated by a
|
// Uses NSObject because the cached class slot is populated by a
|
||||||
// constructor at module-load — runtime-created test classes wouldn't
|
// constructor at module-load — runtime-created test classes wouldn't
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Phase 3.0 (FFI-linkage) — postfix `extern` on an aggregate (`#objc_class`)
|
// Phase 3.0 (FFI-linkage) — postfix `extern` on an aggregate (`#objc_class`)
|
||||||
// is the new spelling of the legacy prefix `#foreign #objc_class` import.
|
// is the new spelling of the legacy prefix `#objc_class(…) extern` import.
|
||||||
// Mirrors 1306's foreign-class chained dispatch with the new syntax:
|
// Mirrors 1306's runtime-class chained dispatch with the new syntax:
|
||||||
// Name :: #objc_class("X") extern { … } == Name :: #foreign #objc_class("X") { … }
|
// Name :: #objc_class("X") extern { … } == Name :: #objc_class(…) extern("X") { … }
|
||||||
//
|
//
|
||||||
// Red until 3.1 wires the postfix-extern aggregate path through the parser
|
// Red until 3.1 wires the postfix-extern aggregate path through the parser
|
||||||
// + lowering (maps `extern` → reference, same as `#foreign`).
|
// + lowering (maps `extern` → reference, same as `extern`).
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/build.sx";
|
#import "modules/build.sx";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Phase 3 (FFI-linkage) — postfix `export` on an `#objc_class` aggregate, the
|
// Phase 3 (FFI-linkage) — postfix `export` on an `#objc_class` aggregate, the
|
||||||
// explicit spelling for an sx-DEFINED runtime class (define + register). It is
|
// explicit spelling for an sx-DEFINED runtime class (define + register). It is
|
||||||
// the same lowering as a bare `#objc_class("X") { … }` with no `#foreign`;
|
// the same lowering as a bare `#objc_class("X") { … }` with no `extern`;
|
||||||
// `export` just makes the "I define this class" intent explicit (the dual of
|
// `export` just makes the "I define this class" intent explicit (the dual of
|
||||||
// `extern` for "I reference an existing class"). Mirrors 1339's defined class.
|
// `extern` for "I reference an existing class"). Mirrors 1339's defined class.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// method body item inside a `#jni_class` declaration.
|
// method body item inside a `#jni_class` declaration.
|
||||||
//
|
//
|
||||||
// `name :: (self: *Self, args...) -> Ret;` is the shape — semicolon
|
// `name :: (self: *Self, args...) -> Ret;` is the shape — semicolon
|
||||||
// terminated (foreign declaration, no body). The 2.1 parser only
|
// terminated (extern declaration, no body). The 2.1 parser only
|
||||||
// accepts an empty body; step 2.2 extends `parseJniClassDecl` to
|
// accepts an empty body; step 2.2 extends `parseJniClassDecl` to
|
||||||
// loop over body items and collect method declarations.
|
// loop over body items and collect method declarations.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// and `#implements` body items inside a `#jni_class` declaration.
|
// and `#implements` body items inside a `#jni_class` declaration.
|
||||||
//
|
//
|
||||||
// `#extends Alias;` declares a single-inheritance superclass reference
|
// `#extends Alias;` declares a single-inheritance superclass reference
|
||||||
// using the sx-side alias name (not the foreign Java path — that lives
|
// using the sx-side alias name (not the runtime Java path — that lives
|
||||||
// in the alias's own `#jni_class(...)` directive arg). `#implements
|
// in the alias's own `#jni_class(...)` directive arg). `#implements
|
||||||
// Alias;` is repeatable and records interface conformance.
|
// Alias;` is repeatable and records interface conformance.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
// #swift_struct — Swift @frozen value-type binding
|
// #swift_struct — Swift @frozen value-type binding
|
||||||
// #swift_protocol — Swift @objc-bridgeable protocol binding
|
// #swift_protocol — Swift @objc-bridgeable protocol binding
|
||||||
//
|
//
|
||||||
// Internally the AST collapses into one `foreign_class_decl` node
|
// Internally the AST collapses into one `runtime_class_decl` node
|
||||||
// carrying a `runtime` discriminator. Today the parser rejects each
|
// carrying a `runtime` discriminator. Today the parser rejects each
|
||||||
// of these because the lexer doesn't recognise the directive name.
|
// of these because the lexer doesn't recognise the directive name.
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ g_should_call : bool = false;
|
|||||||
unused_jni :: (env: *void, act: *Activity) {
|
unused_jni :: (env: *void, act: *Activity) {
|
||||||
#jni_env(env) {
|
#jni_env(env) {
|
||||||
// Today: this fails — sema doesn't know `Activity` as a type, or
|
// Today: this fails — sema doesn't know `Activity` as a type, or
|
||||||
// the method dispatch doesn't recognize foreign-class members.
|
// the method dispatch doesn't recognize runtime-class members.
|
||||||
win := act.getWindow();
|
win := act.getWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Regression: `obj.method()` foreign-class dispatch with a `float`
|
// Regression: `obj.method()` runtime-class dispatch with a `float`
|
||||||
// return type used to silently emit `LLVMGetUndef` because the
|
// return type used to silently emit `LLVMGetUndef` because the
|
||||||
// `Call<T>Method` switch in `emit_llvm.zig` didn't cover `.f32`.
|
// `Call<T>Method` switch in `emit_llvm.zig` didn't cover `.f32`.
|
||||||
// Combined with multiple such calls inlined as args to a single
|
// Combined with multiple such calls inlined as args to a single
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// `sx_android_push_touch`).
|
// `sx_android_push_touch`).
|
||||||
//
|
//
|
||||||
// This test exercises BOTH the `.f32` jdispatch slot AND the
|
// This test exercises BOTH the `.f32` jdispatch slot AND the
|
||||||
// "multiple foreign-class method calls as args to one outer call"
|
// "multiple runtime-class method calls as args to one outer call"
|
||||||
// pattern. The bodies are gated behind a runtime-false flag so the
|
// pattern. The bodies are gated behind a runtime-false flag so the
|
||||||
// JNI lookups never execute (no JVM in the test runtime), but the
|
// JNI lookups never execute (no JVM in the test runtime), but the
|
||||||
// codegen path still has to emit the calls correctly.
|
// codegen path still has to emit the calls correctly.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Phase 3 (FFI-linkage) — postfix `extern` on a `#jni_class` aggregate, the
|
// Phase 3 (FFI-linkage) — postfix `extern` on a `#jni_class` aggregate, the
|
||||||
// new spelling of the legacy prefix `#foreign #jni_class` import. Parse-only
|
// new spelling of the legacy prefix `#jni_class(…) extern` import. Parse-only
|
||||||
// on macOS (no JVM at runtime), mirroring 1412's foreign-class reference.
|
// on macOS (no JVM at runtime), mirroring 1412's runtime-class reference.
|
||||||
// View :: #jni_class("…") extern { … } == View :: #foreign #jni_class("…") { … }
|
// View :: #jni_class("…") extern { … } == View :: #jni_class(…) extern("…") { … }
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Innermost module: owns the #library and its foreign fn.
|
// Innermost module: owns the #library and its extern fn.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
pcaplib :: #library "pcap";
|
pcaplib :: #library "pcap";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
=== 15. Foreign ===
|
=== 15. Extern ===
|
||||||
foreign-rename: 42
|
extern-rename: 42
|
||||||
|
|||||||
@@ -804,7 +804,7 @@ pub const Ops = struct {
|
|||||||
};
|
};
|
||||||
const callee_needs_c_abi = callee_func.is_extern or callee_func.call_conv == .c;
|
const callee_needs_c_abi = callee_func.is_extern or callee_func.call_conv == .c;
|
||||||
const callee_raw_ret = self.e.toLLVMType(callee_func.ret);
|
const callee_raw_ret = self.e.toLLVMType(callee_func.ret);
|
||||||
// Foreign string/?string returns receive one `char *` — never sret
|
// Extern string/?string returns receive one `char *` — never sret
|
||||||
// (must mirror declareFunction's signature classification).
|
// (must mirror declareFunction's signature classification).
|
||||||
const cstr_ret = self.e.cstrRetKind(callee_func);
|
const cstr_ret = self.e.cstrRetKind(callee_func);
|
||||||
const callee_uses_sret = callee_needs_c_abi and cstr_ret == .none and self.e.needsByval(callee_func.ret, callee_raw_ret);
|
const callee_uses_sret = callee_needs_c_abi and cstr_ret == .none and self.e.needsByval(callee_func.ret, callee_raw_ret);
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ pub fn processCImport(
|
|||||||
.name = pname,
|
.name = pname,
|
||||||
.name_span = .{ .start = 0, .end = 0 },
|
.name_span = .{ .start = 0, .end = 0 },
|
||||||
.type_expr = ptype_node,
|
.type_expr = ptype_node,
|
||||||
// Foreign C param names (`i1`, `i2`, …) are RAW — exempt from
|
// Extern C param names (`i1`, `i2`, …) are RAW — exempt from
|
||||||
// the reserved-type-name binding check; generated bindings
|
// the reserved-type-name binding check; generated bindings
|
||||||
// must import without hand-edits.
|
// must import without hand-edits.
|
||||||
.is_raw = true,
|
.is_raw = true,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ pub const CallPlan = struct {
|
|||||||
selected: Lowering.SelectedFunc,
|
selected: Lowering.SelectedFunc,
|
||||||
/// Protocol method, by index in the protocol's method table.
|
/// Protocol method, by index in the protocol's method table.
|
||||||
protocol_method: u32,
|
protocol_method: u32,
|
||||||
/// Foreign-class method (Obj-C / JNI), with its static-ness.
|
/// Runtime-class method (Obj-C / JNI), with its static-ness.
|
||||||
runtime_method: struct { name: []const u8, is_static: bool },
|
runtime_method: struct { name: []const u8, is_static: bool },
|
||||||
/// Enum / tagged-union type under construction.
|
/// Enum / tagged-union type under construction.
|
||||||
constructed: TypeId,
|
constructed: TypeId,
|
||||||
@@ -241,7 +241,7 @@ pub const CallResolver = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Foreign-class instance method: look up the method's declared
|
// Runtime-class instance method: look up the method's declared
|
||||||
// return type so chained calls (e.g.
|
// return type so chained calls (e.g.
|
||||||
// `UIWindow.alloc().initWithWindowScene(scene)`) resolve.
|
// `UIWindow.alloc().initWithWindowScene(scene)`) resolve.
|
||||||
{
|
{
|
||||||
@@ -395,7 +395,7 @@ pub const CallResolver = struct {
|
|||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
if (type_name) |tn| {
|
if (type_name) |tn| {
|
||||||
// Foreign-class static method: `Alias.static_method(args)`.
|
// Runtime-class static method: `Alias.static_method(args)`.
|
||||||
if (self.l.program_index.runtime_class_map.get(tn)) |fcd| {
|
if (self.l.program_index.runtime_class_map.get(tn)) |fcd| {
|
||||||
for (fcd.members) |m| switch (m) {
|
for (fcd.members) |m| switch (m) {
|
||||||
.method => |md| if (md.is_static and std.mem.eql(u8, md.name, cfa.field)) {
|
.method => |md| if (md.is_static and std.mem.eql(u8, md.name, cfa.field)) {
|
||||||
|
|||||||
@@ -2250,7 +2250,7 @@ pub const LLVMEmitter = struct {
|
|||||||
|
|
||||||
/// Coerce a call argument to match the expected parameter type.
|
/// Coerce a call argument to match the expected parameter type.
|
||||||
/// Handles int width mismatches (trunc/ext), float width, and int↔float.
|
/// Handles int width mismatches (trunc/ext), float width, and int↔float.
|
||||||
/// How a FOREIGN function's declared sx return maps onto a C `char *`:
|
/// How an EXTERN function's declared sx return maps onto a C `char *`:
|
||||||
/// `-> string` (.plain) and `-> ?string` (.optional) both receive one
|
/// `-> string` (.plain) and `-> ?string` (.optional) both receive one
|
||||||
/// pointer from C; everything else is `.none`. Keep `declareFunction`'s
|
/// pointer from C; everything else is `.none`. Keep `declareFunction`'s
|
||||||
/// signature building and `emitCall`'s result synthesis keyed on the
|
/// signature building and `emitCall`'s result synthesis keyed on the
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ pub const ObjcLowering = struct {
|
|||||||
/// C=u8 S=u16 I=u32 Q=u64 f=f32 d=f64
|
/// C=u8 S=u16 I=u32 Q=u64 f=f32 d=f64
|
||||||
/// @=id #=Class :=SEL *=C string ^v=void* / generic ptr
|
/// @=id #=Class :=SEL *=C string ^v=void* / generic ptr
|
||||||
///
|
///
|
||||||
/// Foreign-class pointers (`*UIView` etc.) encode as `@` (object
|
/// Runtime-class pointers (`*UIView` etc.) encode as `@` (object
|
||||||
/// pointer). Other pointers fall to `^v` — the encoding is metadata,
|
/// pointer). Other pointers fall to `^v` — the encoding is metadata,
|
||||||
/// not ABI, so being conservative here is safe. Pass-by-value
|
/// not ABI, so being conservative here is safe. Pass-by-value
|
||||||
/// structs encode as `{Name=field0field1...}`; nested structs
|
/// structs encode as `{Name=field0field1...}`; nested structs
|
||||||
@@ -260,7 +260,7 @@ pub const ObjcLowering = struct {
|
|||||||
/// `self.field` access work in A.2/A.3. Field-by-name resolution
|
/// `self.field` access work in A.2/A.3. Field-by-name resolution
|
||||||
/// stays correct across the future repositioning.
|
/// stays correct across the future repositioning.
|
||||||
///
|
///
|
||||||
/// Foreign-class members other than `.field` are ignored here —
|
/// Runtime-class members other than `.field` are ignored here —
|
||||||
/// methods / `#extends` / `#implements` don't contribute to the
|
/// methods / `#extends` / `#implements` don't contribute to the
|
||||||
/// state layout.
|
/// state layout.
|
||||||
pub fn objcDefinedStateStructType(self: ObjcLowering, fcd: *const ast.RuntimeClassDecl) TypeId {
|
pub fn objcDefinedStateStructType(self: ObjcLowering, fcd: *const ast.RuntimeClassDecl) TypeId {
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ pub const JniMsgSend = struct {
|
|||||||
/// NewObject(env, clazz, mid, args...)`. Returns a fresh jobject.
|
/// NewObject(env, clazz, mid, args...)`. Returns a fresh jobject.
|
||||||
/// Mutually exclusive with the other dispatch flags.
|
/// Mutually exclusive with the other dispatch flags.
|
||||||
is_constructor: bool = false,
|
is_constructor: bool = false,
|
||||||
/// Foreign path of the parent class (e.g. `android/app/Activity`) when
|
/// Runtime path of the parent class (e.g. `android/app/Activity`) when
|
||||||
/// `is_nonvirtual` is true, OR of the class being constructed when
|
/// `is_nonvirtual` is true, OR of the class being constructed when
|
||||||
/// `is_constructor` is true. emit_llvm uses `FindClass` to materialise
|
/// `is_constructor` is true. emit_llvm uses `FindClass` to materialise
|
||||||
/// the jclass at the call site (per-call; caching is follow-up).
|
/// the jclass at the call site (per-call; caching is follow-up).
|
||||||
@@ -508,7 +508,7 @@ pub const Function = struct {
|
|||||||
/// True if `params[0]` is the synthetic `__sx_ctx: *Context`
|
/// True if `params[0]` is the synthetic `__sx_ctx: *Context`
|
||||||
/// parameter that every default-conv sx function receives. Callers
|
/// parameter that every default-conv sx function receives. Callers
|
||||||
/// read this flag to decide whether to prepend their current
|
/// read this flag to decide whether to prepend their current
|
||||||
/// `__sx_ctx` value to the args of a call. Foreign decls and
|
/// `__sx_ctx` value to the args of a call. Extern decls and
|
||||||
/// `callconv(.c)` functions have it false.
|
/// `callconv(.c)` functions have it false.
|
||||||
has_implicit_ctx: bool = false,
|
has_implicit_ctx: bool = false,
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ pub const DeriveError = error{
|
|||||||
pub const ClassRegistry = std.StringHashMap([]const u8);
|
pub const ClassRegistry = std.StringHashMap([]const u8);
|
||||||
|
|
||||||
pub const Context = struct {
|
pub const Context = struct {
|
||||||
/// Foreign path of the enclosing #jni_class — used to resolve `*Self`.
|
/// Runtime path of the enclosing #jni_class — used to resolve `*Self`.
|
||||||
/// e.g. "android/view/View".
|
/// e.g. "android/view/View".
|
||||||
enclosing_path: []const u8,
|
enclosing_path: []const u8,
|
||||||
/// Lookup for sibling/forward-declared `#jni_class` aliases. When null,
|
/// Lookup for sibling/forward-declared `#jni_class` aliases. When null,
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ test "lower: objcTypeEncodingFromSignature unwraps optional to wire type" {
|
|||||||
defer module.deinit();
|
defer module.deinit();
|
||||||
var lowering = Lowering.init(&module);
|
var lowering = Lowering.init(&module);
|
||||||
|
|
||||||
// Foreign `*NSString` so the encoder recognises it as `@`.
|
// Runtime-class `*NSString` so the encoder recognises it as `@`.
|
||||||
const ns_name = module.types.internString("NSString");
|
const ns_name = module.types.internString("NSString");
|
||||||
const ns_struct = module.types.intern(.{ .@"struct" = .{ .name = ns_name, .fields = &.{} } });
|
const ns_struct = module.types.intern(.{ .@"struct" = .{ .name = ns_name, .fields = &.{} } });
|
||||||
const ns_ptr = module.types.ptrTo(ns_struct);
|
const ns_ptr = module.types.ptrTo(ns_struct);
|
||||||
|
|||||||
@@ -722,7 +722,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
|||||||
switch (self.namespaceAliasVerdict(oname)) {
|
switch (self.namespaceAliasVerdict(oname)) {
|
||||||
.target => |target| {
|
.target => |target| {
|
||||||
const fd = Lowering.namespaceFnMember(&target, fa.field) orelse break :gate;
|
const fd = Lowering.namespaceFnMember(&target, fa.field) orelse break :gate;
|
||||||
// Foreign / builtin / #compiler bodies keep their
|
// Extern / builtin / #compiler bodies keep their
|
||||||
// literal global symbol — the existing bare-name
|
// literal global symbol — the existing bare-name
|
||||||
// machinery below resolves them.
|
// machinery below resolves them.
|
||||||
switch (fd.body.data) {
|
switch (fd.body.data) {
|
||||||
@@ -891,7 +891,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
|||||||
method_args.append(self.alloc, a) catch unreachable;
|
method_args.append(self.alloc, a) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Foreign-class DSL: `inst.method(args)` where `inst`'s
|
// Runtime-class DSL: `inst.method(args)` where `inst`'s
|
||||||
// type is an alias declared by `#jni_class("...") { ... }`
|
// type is an alias declared by `#jni_class("...") { ... }`
|
||||||
// (or its parallel forms). Routes to the JNI dispatch
|
// (or its parallel forms). Routes to the JNI dispatch
|
||||||
// shape, descriptor derived from the sx signature.
|
// shape, descriptor derived from the sx signature.
|
||||||
@@ -2236,7 +2236,7 @@ pub fn resolveCallParamTypes(self: *Lowering, c: *const ast.Call, sel_author: ?*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self.getStructTypeName(obj_ty)) |sname| {
|
if (self.getStructTypeName(obj_ty)) |sname| {
|
||||||
// Foreign-class receiver (`#objc_class` / `#jni_class` / etc.):
|
// Runtime-class receiver (`#objc_class` / `#jni_class` / etc.):
|
||||||
// resolve the method from `runtime_class_map` walking `#extends`.
|
// resolve the method from `runtime_class_map` walking `#extends`.
|
||||||
// Without this path, `target_type` for each arg falls back to
|
// Without this path, `target_type` for each arg falls back to
|
||||||
// whatever `self.target_type` was on entry — typically the
|
// whatever `self.target_type` was on entry — typically the
|
||||||
|
|||||||
@@ -1112,7 +1112,7 @@ pub fn registerTopLevelGlobal(self: *Lowering, vd: *const ast.VarDecl) void {
|
|||||||
d.addFmt(.err, null, "top-level var '{s}' has no type annotation and no initializer to infer from", .{vd.name});
|
d.addFmt(.err, null, "top-level var '{s}' has no type annotation and no initializer to infer from", .{vd.name});
|
||||||
break :blk .void;
|
break :blk .void;
|
||||||
};
|
};
|
||||||
// Foreign / extern globals reference a symbol defined in libSystem etc.
|
// Extern globals reference a symbol defined in libSystem etc.
|
||||||
// (`_NSConcreteStackBlock : *void extern;` or `… : *void extern;`). The C
|
// (`_NSConcreteStackBlock : *void extern;` or `… : *void extern;`). The C
|
||||||
// symbol name is the optional override (`extern_name`) or the sx name itself.
|
// symbol name is the optional override (`extern_name`) or the sx name itself.
|
||||||
const sym_name = vd.extern_name orelse vd.name;
|
const sym_name = vd.extern_name orelse vd.name;
|
||||||
@@ -2081,7 +2081,7 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Foreign declarations with a trailing variadic param map to the C
|
// Extern declarations with a trailing variadic param map to the C
|
||||||
// calling convention's `...` tail. Drop the variadic param from the
|
// calling convention's `...` tail. Drop the variadic param from the
|
||||||
// IR signature (it has no C-level slot) and set is_variadic.
|
// IR signature (it has no C-level slot) and set is_variadic.
|
||||||
// Bare `extern` import: an external C symbol declared via the `extern`
|
// Bare `extern` import: an external C symbol declared via the `extern`
|
||||||
@@ -2194,7 +2194,7 @@ pub fn registerQualifiedFn(self: *Lowering, ns_name: []const u8, fd: *const ast.
|
|||||||
// collision assert; registering a qualified alias for them
|
// collision assert; registering a qualified alias for them
|
||||||
// would divert that machinery and strand a per-call type binding.
|
// would divert that machinery and strand a per-call type binding.
|
||||||
if (fd.type_params.len > 0 or hasComptimeParams(fd) or isPackFn(fd)) return;
|
if (fd.type_params.len > 0 or hasComptimeParams(fd) or isPackFn(fd)) return;
|
||||||
// Foreign / builtin / #compiler bodies keep their literal name; a
|
// Extern / builtin / #compiler bodies keep their literal name; a
|
||||||
// qualified alias has no distinct symbol to resolve to.
|
// qualified alias has no distinct symbol to resolve to.
|
||||||
switch (fd.body.data) {
|
switch (fd.body.data) {
|
||||||
.builtin_expr, .compiler_expr => return,
|
.builtin_expr, .compiler_expr => return,
|
||||||
@@ -2300,7 +2300,7 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
|||||||
}
|
}
|
||||||
// No AST? (builtins, extern functions, or imported functions not in this file)
|
// No AST? (builtins, extern functions, or imported functions not in this file)
|
||||||
const fd = self.program_index.fn_ast_map.get(name) orelse return;
|
const fd = self.program_index.fn_ast_map.get(name) orelse return;
|
||||||
// Foreign declarations stay as extern stubs but need to be REGISTERED
|
// Extern declarations stay as extern stubs but need to be REGISTERED
|
||||||
// in the current module so callers get a real FuncId. Without this,
|
// in the current module so callers get a real FuncId. Without this,
|
||||||
// a comptime-lowered function (e.g. `concat` from std.sx pulled into
|
// a comptime-lowered function (e.g. `concat` from std.sx pulled into
|
||||||
// a fresh ct_module via `evalComptimeString`) emits `.call` against a
|
// a fresh ct_module via `evalComptimeString`) emits `.call` against a
|
||||||
|
|||||||
@@ -778,7 +778,7 @@ pub fn lowerSuperCall(
|
|||||||
} }, ret_ty);
|
} }, ret_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Foreign-class registration ──────────────────────────────────
|
// ── Runtime-class registration ──────────────────────────────────
|
||||||
|
|
||||||
/// Register a runtime-class declaration. The alias goes into
|
/// Register a runtime-class declaration. The alias goes into
|
||||||
/// `runtime_class_map` for method-dispatch lookup. The underlying
|
/// `runtime_class_map` for method-dispatch lookup. The underlying
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ pub fn lookupObjcDefinedStateFieldOnPointer(self: *Lowering, obj_expr: *const as
|
|||||||
if (pointee_info != .@"struct") return null;
|
if (pointee_info != .@"struct") return null;
|
||||||
const struct_name = self.module.types.getString(pointee_info.@"struct".name);
|
const struct_name = self.module.types.getString(pointee_info.@"struct".name);
|
||||||
const fcd = self.program_index.runtime_class_map.get(struct_name) orelse return null;
|
const fcd = self.program_index.runtime_class_map.get(struct_name) orelse return null;
|
||||||
// Only sx-defined Obj-C classes have a state struct. Foreign
|
// Only sx-defined Obj-C classes have a state struct. Extern (referenced)
|
||||||
// classes' fields are purely declaration metadata (no state).
|
// runtime classes' fields are purely declaration metadata (no state).
|
||||||
if (fcd.is_extern or fcd.runtime != .objc_class) return null;
|
if (fcd.is_extern or fcd.runtime != .objc_class) return null;
|
||||||
// Skip property fields — those dispatch via the M2.2 getter/setter
|
// Skip property fields — those dispatch via the M2.2 getter/setter
|
||||||
// path. Plain instance fields take the ivar+gep path.
|
// path. Plain instance fields take the ivar+gep path.
|
||||||
|
|||||||
Reference in New Issue
Block a user