diff --git a/current/CHECKPOINT-EXTERN-EXPORT.md b/current/CHECKPOINT-EXTERN-EXPORT.md index d19aa3a..bddf048 100644 --- a/current/CHECKPOINT-EXTERN-EXPORT.md +++ b/current/CHECKPOINT-EXTERN-EXPORT.md @@ -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. ## Last completed step -**Phase 3.0** (xfail) — `examples/1348-ffi-objc-extern-class.sx` exercises the new -postfix-`extern` aggregate spelling (`NSObject :: #objc_class("NSObject") extern { … }`, -the new way to write `#foreign #objc_class("NSObject") { … }`). RED: parser rejects -`extern` after the `#objc_class(...)` directive (`expected '{'`). Green-state snapshots -hand-authored (exit 0, stdout `extern-class dispatch ok`). Suite: 639 corpus / 443 unit, -**1 fail (1348, the intended xfail)**. Step 3.1 wires parse+lowering to green it. +**Phase 3.1** (green) — **PHASE 3 COMPLETE.** Postfix `extern`/`export` on `#objc_class`/ +`#jni_class` aggregates fully works. `parseForeignClassDecl` now parses an optional +`extern`/`export` modifier in the slot **between** the `("X")` directive args and the `{` +body (`parser.zig:~1409`): `extern`→`is_foreign_eff = true` (reference an existing runtime +class, == legacy `#foreign`); `export`→`is_foreign_eff = false` (define + register a new sx +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: 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). ## Next step -**Phase 3.1** (green) — wire the postfix `extern`/`export` aggregate path. `parseForeignClassDecl` -already consumes the directive + `("X")` + body; add an optional postfix-modifier slot -**after** the `)` and before the `{`: `extern`→reference (== `is_foreign`), `export`→ -define+register (== no `#foreign`). Map the postfix modifier onto the same downstream -`is_foreign` decision that `tryParseForeignClassPrefix` feeds, so `objc_class.zig` lowering -is unchanged. Green 1348; add a jni postfix test + an `export` (defined-class) test for the -per-runtime coverage. Then Phase 4 (interplay/diagnostics/docs + the A→B gate: unit test that +**Phase 4 — interplay, diagnostics, docs (+ the A→B gate).** (a) Diagnostics for combining +the surfaces: reject `extern`+`export` together; decide/handle prefix `#foreign` **and** +postfix `extern`/`export` on the same aggregate (today the postfix silently overrides +`is_foreign` — Phase 4 should reject the redundant/contradictory combo, mirroring the fn +path). (b) `extern`+`callconv` stacking/redundancy on fns. (c) docs — `specs.md`/`readme.md` +document the three `extern`/`export` axes (fns, globals, aggregates); `#foreign` stays +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. **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`, new spelling of `#foreign #objc_class`). RED (parser: `expected '{'` after the 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 - **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the