docs(ffi-linkage): checkpoint — Phase 3 complete (aggregate extern/export)

This commit is contained in:
agra
2026-06-14 15:14:09 +03:00
parent 91d70bd864
commit d4f683f525

View File

@@ -5,12 +5,18 @@ 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 3.0** (xfail) — `examples/1348-ffi-objc-extern-class.sx` exercises the new **Phase 3.1** (green) — **PHASE 3 COMPLETE.** Postfix `extern`/`export` on `#objc_class`/
postfix-`extern` aggregate spelling (`NSObject :: #objc_class("NSObject") extern { … }`, `#jni_class` aggregates fully works. `parseForeignClassDecl` now parses an optional
the new way to write `#foreign #objc_class("NSObject") { … }`). RED: parser rejects `extern`/`export` modifier in the slot **between** the `("X")` directive args and the `{`
`extern` after the `#objc_class(...)` directive (`expected '{'`). Green-state snapshots body (`parser.zig:~1409`): `extern``is_foreign_eff = true` (reference an existing runtime
hand-authored (exit 0, stdout `extern-class dispatch ok`). Suite: 639 corpus / 443 unit, class, == legacy `#foreign`); `export``is_foreign_eff = false` (define + register a new sx
**1 fail (1348, the intended xfail)**. Step 3.1 wires parse+lowering to green it. class, == bare `#objc_class` with no `#foreign`). The modifier maps straight onto the same
`is_foreign` decision the prefix `#foreign` already fed the node, so **no `objc_class.zig`/
lowering change was needed** — the new surface reuses the existing reference-vs-define path.
Examples: **1348** (objc `extern` import, dispatches `NSObject.alloc().init()` → green via
JIT), **1349** (objc `export` defined class, `SxBar.alloc()`/`bump`/`get``counter: 2`),
**1426** (jni `extern` import, parse-only `parse-only ok`). Suite green (641 corpus / 443
unit, 0 fail).
### Prior: Phase 2.2 (green) — **PHASE 2 COMPLETE.** `export` (define + expose) fully works: ### Prior: Phase 2.2 (green) — **PHASE 2 COMPLETE.** `export` (define + expose) fully works:
external linkage + C ABI + no sx ctx + force-lowered root + optional `"csym"` rename. external linkage + C ABI + no sx ctx + force-lowered root + optional `"csym"` rename.
@@ -47,13 +53,15 @@ gate). Examples: 1223 (extern bare fn), 1224 (extern fn rename), 1225 (extern ba
global), 1226 (export bare fn, AOT), 1227 (export fn rename, AOT). global), 1226 (export bare fn, AOT), 1227 (export fn rename, AOT).
## Next step ## Next step
**Phase 3.1** (green) — wire the postfix `extern`/`export` aggregate path. `parseForeignClassDecl` **Phase 4 — interplay, diagnostics, docs (+ the A→B gate).** (a) Diagnostics for combining
already consumes the directive + `("X")` + body; add an optional postfix-modifier slot the surfaces: reject `extern`+`export` together; decide/handle prefix `#foreign` **and**
**after** the `)` and before the `{`: `extern`→reference (== `is_foreign`), `export` postfix `extern`/`export` on the same aggregate (today the postfix silently overrides
define+register (== no `#foreign`). Map the postfix modifier onto the same downstream `is_foreign` — Phase 4 should reject the redundant/contradictory combo, mirroring the fn
`is_foreign` decision that `tryParseForeignClassPrefix` feeds, so `objc_class.zig` lowering path). (b) `extern`+`callconv` stacking/redundancy on fns. (c) docs — `specs.md`/`readme.md`
is unchanged. Green 1348; add a jni postfix test + an `export` (defined-class) test for the document the three `extern`/`export` axes (fns, globals, aggregates); `#foreign` stays
per-runtime coverage. Then Phase 4 (interplay/diagnostics/docs + the A→B gate: unit test that documented until the Part B cutover. (d) **GATE A→B (hard):** unit test asserting `#foreign`
and `extern`/`export` lower to identical IR for a sample fn / global / **class** — lock
before any Part B migration. Also pick up the two **Deferred** items below at this gate. (interplay/diagnostics/docs + the A→B gate: unit test that
`#foreign` and `extern`/`export` lower to identical IR) before Part B migration. `#foreign` and `extern`/`export` lower to identical IR) before Part B migration.
**FUTURE MILESTONE — C→sx-by-name in JIT (`sx run`).** Investigated this session **FUTURE MILESTONE — C→sx-by-name in JIT (`sx run`).** Investigated this session
@@ -143,6 +151,17 @@ historical carve-out — keep `issues/*.md` provenance, gate the live tree only.
- (3.0) Added `examples/1348-ffi-objc-extern-class.sx` (postfix `extern` on `#objc_class`, - (3.0) Added `examples/1348-ffi-objc-extern-class.sx` (postfix `extern` on `#objc_class`,
new spelling of `#foreign #objc_class`). RED (parser: `expected '{'` after the new spelling of `#foreign #objc_class`). RED (parser: `expected '{'` after the
directive). Hand-authored green snapshots. `xfail` commit; 3.1 greens it. directive). Hand-authored green snapshots. `xfail` commit; 3.1 greens it.
- (3.1a) Wired the postfix `extern`/`export` aggregate slot in `parseForeignClassDecl`
(optional modifier between `("X")` and `{`; `var is_foreign_eff` overrides the passed
`is_foreign`, threaded into the `foreign_class_decl` node). No lowering change — reuses
the existing `is_foreign` reference-vs-define path. 1348 green. Suite green (639/443).
`green` commit. **PHASE 3 COMPLETE.**
- (3.1b) Behavior-lock: added `examples/1426-ffi-jni-extern-class.sx` (jni `extern`,
parse-only) + `examples/1349-ffi-objc-export-class.sx` (objc `export` defined class,
`counter: 2`). Both pass against the 3.1a parser change (locked in their own commit per
the cadence rule). Suite green (641/443). `lock` commit. (Note: `-Dupdate-goldens`
newline-normalizes empty stderr → reverted unrelated 1226/1227 churn, kept new stderr
0-byte per repo convention; runner normalizes both.)
## Known issues ## Known issues
- **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the - **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the