docs(ffi-linkage): checkpoint — Phase 9.1a/b/c done (linkage purge started); collision analysis + scoped gate + ordered remaining plan
This commit is contained in:
@@ -5,7 +5,38 @@ 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 8 — CUTOVER: parser hard-rejects `#foreign`** (`feat!` commit `3811311`,
|
||||
**Phase 9.1 (partial) — internal linkage-identifier purge** (commits `b838f63` 9.1a,
|
||||
`b78e7dd` 9.1b, `cd14794` 9.1c). **PHASE 9 STARTED.** Decision 6 = PURGE EVERYTHING,
|
||||
scoped (user, 2026-06-15): purge `foreign` from **all `.sx` files + all documentation +
|
||||
all our Zig (`src/`)**, analyzing each grep hit — **legitimate keeps stay**
|
||||
(SQLITE_CONSTRAINT_FOREIGNKEY + other SQLite API constant names, vendored
|
||||
`library/vendors/sqlite/c/*`, `1176-diagnostics-foreign-removed.sx` [the rejection test
|
||||
MUST contain `#foreign`], the parser rejection-message string + `hash_foreign` token
|
||||
[kept so `#foreign` keeps its friendly deprecation error]).
|
||||
- **9.1a** (`b838f63`): 5 collision-free linkage renames — `callForeign`→`callExtern`,
|
||||
`marshalForeignArg`→`marshalExternArg`, `dedupeForeignSymbol`→`dedupeExternSymbol`,
|
||||
`foreign_name_map`→`extern_name_map`, `is_foreign_c_api`→`is_extern_c_api`.
|
||||
- **9.1b** (`b78e7dd`): the "foreign symbol already bound" diagnostic (decl.zig) +
|
||||
resolveFuncByName panic (call.zig) → "extern symbol". Intentional 1172 regen.
|
||||
- **9.1c** (`cd14794`): **deleted** the dead `VarDecl.is_foreign`/`foreign_lib`/
|
||||
`foreign_name` fields (the global `#foreign` path rejects → write-dead; 3 coalescing
|
||||
readers in decl.zig simplified to `vd.extern_name`/`vd.is_extern`).
|
||||
All snapshot-neutral except the intentional 1172 regen; suite green (646/444).
|
||||
|
||||
**COLLISION ANALYSIS (done — drives the rest of 9.1/9.2):**
|
||||
- `is_foreign` lives on FnDecl?(no — flipped to `extern_export`), **VarDecl (deleted in
|
||||
9.1c)**, and **ForeignClassDecl (ast.zig:903 — STILL LIVE**, distinguishes runtime-class
|
||||
reference vs define; renamed in 9.2, not 9.1).
|
||||
- `is_extern`/`extern_lib`/`extern_name` already exist (VarDecl + IR insts) — so the
|
||||
old `foreign_*` linkage names could NOT be blind-renamed onto them; 9.1c deleted the
|
||||
dead VarDecl trio instead of renaming.
|
||||
- `foreign_expr` (25) is **still BUILT by `c_import.zig` auto-synthesis** (`#import c
|
||||
{#include}` synthesizes fn bodies as `foreign_expr`). To eliminate it: migrate that
|
||||
synth path to build the extern shape (empty-block body + `extern_export = .extern_`),
|
||||
exactly the Phase 5.0 fn-body flip but for auto-synth — THEN delete the `foreign_expr`
|
||||
node + all readers. This is the last 9.1 item.
|
||||
|
||||
### Prior: Phase 8 — CUTOVER: parser hard-rejects `#foreign` (`feat!` commit `3811311`,
|
||||
preceded by the 8.0 xfail `8180faf` + 3 pre-cutover `refactor`s `2cce6a3`/`720556b`/
|
||||
`d132aab`). **PHASE 8 COMPLETE.** The prefix `#foreign` linkage directive is removed:
|
||||
all four parse sites (const-with-type 316, data global 425, fn body 2065, runtime-class
|
||||
@@ -257,44 +288,47 @@ AOT), 1227 (export fn rename, AOT), 1348 (objc extern class), 1349 (objc export
|
||||
(jni extern class), 1174/1175 (interplay diagnostics).
|
||||
|
||||
## Next step
|
||||
**PART B — Phase 9 (total `foreign` purge — the zero-`foreign` invariant).** Phases 5–8
|
||||
COMPLETE: `extern`/`export` is the sole C-linkage SURFACE; `#foreign` is hard-rejected.
|
||||
**Decision 6 RATIFIED (user, 2026-06-15): PURGE EVERYTHING** — the Phase 9.4 gate is
|
||||
absolute (`grep -rniE 'foreign' src/ library/ examples/ issues/ specs.md readme.md
|
||||
CLAUDE.md` → 0), including `issues/*.md` writeups.
|
||||
**PART B — finish Phase 9 (the scoped `foreign` purge).** Phases 5–8 + 9.1a/b/c COMPLETE.
|
||||
**Gate (scoped per user 2026-06-15):** `grep -rniIE 'foreign'` → 0 across `.sx` files,
|
||||
all docs, and our `src/` Zig — EXCLUDING the legitimate keeps listed in Last completed
|
||||
step (SQLite API names, vendored C, the rejection test/message + `hash_foreign` token).
|
||||
|
||||
Remaining `foreign` footprint (run `grep -rniIE 'foreign'` per area to scope each step):
|
||||
- **Comment-only `#foreign` in `examples/`** — the deferred provenance comments (0716,
|
||||
0729, 1216, 1223/1224/1225/1229/1230/1231, 1332, 1348, 1349, 1426 + the migrated
|
||||
feature tests' leftover comments + issues/0030.sx). ⚠ Many CONTRAST `#foreign` vs
|
||||
`extern` ("no `#foreign`, no `#library`") — a blind `s/#foreign/extern/` yields
|
||||
nonsense; rewrite each comment to stay coherent. Mechanical-ish but needs reading.
|
||||
- **`issues/*.md` prose** (~12 files) — bug writeups referencing `#foreign`. Rewrite to
|
||||
`extern`/`export` per Decision 6 (purge everything). Provenance is preserved in the
|
||||
git history + the `(Regression issue NNNN)` note, not the keyword spelling.
|
||||
- **Internal `src/` identifiers** (Phase 9.1/9.2 — the big mechanical rename):
|
||||
- 9.1 linkage: `foreign_expr`(still BUILT by `c_import.zig` auto-synthesis — migrate
|
||||
that path first, then the node folds away) · `is_foreign`→`is_extern` · `foreign_lib`/
|
||||
`foreign_name`→`extern_*` · `foreign_name_map`→`extern_name_map` · `callForeign`→
|
||||
`callExtern` · `marshalForeignArg` · `is_foreign_c_api` · `dedupeForeignSymbol` · the
|
||||
"foreign symbol already bound" diagnostic text → "extern symbol" (surfaces in 1172).
|
||||
- 9.2 runtime-class → `Runtime*Class*` (Decision 5 ratified): `ForeignClassDecl`(65) ·
|
||||
`ForeignMethodDecl` · `ForeignClassMember` · `ForeignFieldDecl` · `foreign_class_map` ·
|
||||
`current_foreign_class`/`_method` · `foreign_path` · `parse/tryParseForeignClass*` ·
|
||||
`lowerForeign{Method,Static}Call` · `findForeign*InChain` · `resolveForeign*` ·
|
||||
`register*ForeignClass*` · `*ForeignRefs` · `ForeignRuntime`.
|
||||
- 9.0 surface: the `hash_foreign` token + lexer entry + the 4 parse-path REJECTION
|
||||
stubs + the `lex hash_foreign` test. ⚠ DESIGN CHOICE: keep the token so `#foreign`
|
||||
keeps its nice migration message, or delete it (→ generic "unknown directive")?
|
||||
Recommend KEEP the token + rejection through at least one release for a good
|
||||
deprecation, deferring the token deletion. Decide before 9.0.
|
||||
- 9.3 docs: `CLAUDE.md` still references `#foreign` in several places (file-roles,
|
||||
bundling, the rejected-patterns examples) — purge those too for the gate.
|
||||
- **9.4 acceptance gate:** the absolute grep → 0 across all gated areas.
|
||||
|
||||
Suggested order: 9.3-examples (comment rewrite) + issues/*.md first (low-risk text), then
|
||||
9.1/9.2 internal renames (mechanical, snapshot-neutral — but 9.1's diagnostic-text rename
|
||||
intentionally regens 1172), then 9.0 surface decision, then the 9.4 gate.
|
||||
Remaining, in suggested (dependency-safe) order:
|
||||
1. **9.1d — eliminate `foreign_expr`** (last linkage item): migrate `c_import.zig`
|
||||
auto-synthesis to build the extern shape instead of a `foreign_expr` body (the Phase
|
||||
5.0 fn-body flip applied to auto-synth), then delete the `foreign_expr` AST node +
|
||||
`ForeignExpr` + all readers (25). Snapshot-neutral; verify full corpus (the `#import c`
|
||||
examples 1215/1216/1217 + sqlite 1624 exercise it).
|
||||
2. **9.2 — runtime-class family rename → `Runtime*` (Decision 5).** The BIG one, do as
|
||||
small per-identifier commits with `zig build` after each (snapshot-neutral). Targets
|
||||
(counts): `ForeignClassDecl`(65)→`RuntimeClassDecl` · `foreign_path`(62)→`runtime_path`
|
||||
· `foreign_class_map`(44) · `current_foreign_class`(34)/`_method` · `ForeignMethodDecl`(31)
|
||||
· `foreign_class_decl`(30) · `foreign_expr`-gone-by-now · `ForeignClassMember`(20) ·
|
||||
`ForeignFieldDecl`(15) · `ForeignClassDecl.is_foreign`(the live one)→e.g. `is_reference`
|
||||
· `parse/tryParseForeignClass*` · `lowerForeign{Method,Static}Call` ·
|
||||
`findForeign*InChain` · `resolveForeign*` · `register*ForeignClass*` · `*ForeignRefs` ·
|
||||
`ForeignRuntime` · `current_foreign_class`/`_method`. ⚠ COUPLED .sx↔.zig hook names:
|
||||
`jni_main_foreign_path_at`/`jni_main_foreign_paths`/`hookJniMainForeignPathAt`/
|
||||
`foreignPathToJavaName`/`splitForeignPath` span build.sx + bundle.sx + compiler_hooks.zig
|
||||
+ specs.md (2975/3049) — rename all four sites together.
|
||||
3. **9.x-src-comments** — the ~200 bare-`foreign` comments in `src/` (rename last, since
|
||||
many reference identifiers that 9.1d/9.2 rename; do AFTER those so the comment text
|
||||
matches the new names).
|
||||
4. **9.3-examples comments** — the deferred `.sx` provenance comments (0716, 0729, 1205/
|
||||
1207/1216/1218/1219/1220, 1223-1231, 1306/1308/1315/1318/1320/1321/1331/1332/1348/1349,
|
||||
1412/1414/1417/1418/1419/1426, 0117/0415, 1140/1141/1125, issues/0030.sx). ⚠ Many
|
||||
CONTRAST `#foreign` vs `extern` ("no `#foreign`, no `#library`") or reference renamed
|
||||
internals — rewrite each to stay coherent (NOT blind sed). ALSO: `1219-ffi-foreign.sx`
|
||||
prints `"foreign-rename: {}"` to STDOUT — changing it regens the snapshot (intentional).
|
||||
5. **9.3-issues** — `issues/*.md` writeups (~20 files) → rewrite `#foreign`/`foreign` to
|
||||
`extern`/`export`/`runtime-class` per the renames.
|
||||
6. **9.3-docs** — specs.md (12: rename "Foreign Function Interface" heading → "C Interop";
|
||||
the `#import c` "foreign declarations" prose; the jni_main_foreign_path_at refs with #2),
|
||||
readme.md (2), CLAUDE.md (2: host_ffi `#foreign("c")` ref + "foreign calls").
|
||||
7. **9.0 surface decision** — keep `hash_foreign` token + rejection (recommended: good
|
||||
deprecation) vs delete it. If kept, the token + the rejection-message string + 1176 are
|
||||
permanent legitimate keeps; the gate excludes them.
|
||||
8. **9.4 gate** — `grep -rniIE 'foreign'` over the gated set minus the keep-list → 0.
|
||||
- **6.2 verification note (carry forward):** the `platform/` runtime modules
|
||||
(uikit/android/android_jni) are NOT compiled by any marker'd host corpus test — verify
|
||||
future platform-adjacent migrations via direct `sx ir` on importers (1610/1606 compile
|
||||
@@ -423,6 +457,16 @@ Part A ratified (bare / postfix / `⇒ callconv(.c)` / lib-separate). Part B:
|
||||
deleted at cutover). Affects only diagnostic wording — IR/behavior identical either way.
|
||||
|
||||
## Log
|
||||
- (9.1c) Deleted dead `VarDecl.is_foreign`/`foreign_lib`/`foreign_name` (global `#foreign`
|
||||
rejects → write-dead); 3 decl.zig readers simplified to `vd.extern_name`/`vd.is_extern`.
|
||||
Snapshot-neutral; suite green (646/444). `refactor` `cd14794`.
|
||||
- (9.1b) "foreign symbol already bound" diagnostic + resolveFuncByName panic → "extern
|
||||
symbol"; intentional 1172 regen. Suite green. `refactor` `b78e7dd`.
|
||||
- (9.1a) **PHASE 9 STARTED.** 5 collision-free linkage renames (callForeign→callExtern,
|
||||
marshalForeignArg, dedupeForeignSymbol, foreign_name_map→extern_name_map,
|
||||
is_foreign_c_api). Snapshot-neutral; suite green. `refactor` `b838f63`. Decision 6
|
||||
scoped by user: purge `.sx` + docs + our `src/` Zig, keep legitimate hits (SQLite API
|
||||
names, vendored C, the rejection test/message + hash_foreign token).
|
||||
- (8.1 cutover) **PHASE 8 COMPLETE.** Parser hard-rejects `#foreign` at all 4 sites
|
||||
(const/global/fn-body via `self.fail`; runtime-class via `self.failAt` at the caller,
|
||||
new helper); greens xfail 1176. Deleted obsolete 1174 + 1620, the GATE A→B test +
|
||||
|
||||
Reference in New Issue
Block a user