# sx `extern`/`export` + `#foreign` retirement — Checkpoint (FFI-linkage stream) Companion to `current/PLAN-EXTERN-EXPORT.md` — one merged plan: **Part A** adds `extern`/`export`, **Part B** migrates `#foreign` and purges `foreign`. Update after every commit, one step at a time per the cadence rule. ## Last completed step **Phase 1.0b** (xfail — example half of 1.0) — added `examples/1223-ffi-extern-fn.sx` (extern-binds libc `abs` via bare `extern`; sx name = C symbol, no rename) + hand-authored `expected/` capturing the SUCCESS output (`abs(-7) = 7` / `abs(42) = 42`, exit 0). **RED**: 1223 is the only corpus failure (634 ran, 1 failed) — it parses then errors at sema (`body produces no value`) because lowering doesn't route extern yet. Phase 1.1 turns it green. (Prior: 1.0a (lock, green) wired fn-path extern parsing — `parseFnDecl` → `parseOptionalExternExport()` → `FnDecl.extern_export`, `;` body = empty-block placeholder; both lookahead predicates accept `kw_extern`/`kw_export`; per user feedback added `FnDecl.extern_lib`/`extern_name` + `VarDecl.extern_lib`, decision 4 REVISED.) ## Current state Syntax: bare `extern`/`export`, postfix after `callconv(.c)`, `extern ⇒ callconv(.c)`. **Decision 4 revised** (user 2026-06-14): `extern` carries an optional `LIB`+`"csym"` axis (`extern_lib`/`extern_name`) like `#foreign`; the `#library` decl + build-flag linking stays separate. Touch-points: token `token.zig:45,282`; parser `1950,3669,316,425,1305`; lowering `decl.zig:1123,387,2110,2382,2514`; IR/emit already capable (no codegen change). Part B `foreign` footprint to purge: 643 lines / ~57 identifiers in `src/` + 28 doc lines. End-state invariant: **zero `foreign`** (Phase 9.4 gate). **Done**: tokens (0.0) + AST/parser plumbing (0.1) + fn-path extern parsing + lib/name fields (1.0a), all unconsumed. Lowering not yet wired. ## Next step **Phase 1.1** (green, turns 1223 green) — in `decl.zig`, when `fn_decl.extern_export == .extern_`, route the fn through `declareExtern` (`is_extern`, `.external` linkage, `callconv(.c)`, no implicit ctx — anchors `decl.zig:1123,387, 2110,2113`) instead of lowering the empty-block placeholder body. Treat the bare `extern` (no `extern_lib`/`extern_name` yet) like a lib-less `#foreign` import — the sx name IS the C symbol, resolves against the default-linked libc. Run, then `-Dupdate-goldens` to finalize 1223's snapshot byte-exact; review diff. Then **1.2** (green): consume `extern LIB "csym"` rename (`extern_lib`/`extern_name`) + extern-global `g : T extern;` (`parser.zig:425`). Stop at end of Phase 1. ## Open decisions Part A ratified (bare / postfix / `⇒ callconv(.c)` / lib-separate). Part B (confirm before Phase 9): runtime-class rename target — `Runtime*Class*` (recommended); historical carve-out — keep `issues/*.md` provenance, gate the live tree only. ## Log - (init) Plan written; FFI-linkage stream opened. - (merge) Folded FOREIGN-MIGRATION in as Part B; deleted the split plan + checkpoint. - (0.0) Added `kw_extern`/`kw_export` tokens + keyword-map entries + LSP keyword classification + `lex linkage keywords` test. Suite green; no identifier collisions in the corpus. `lock` commit. - (0.1) Added `ast.ExternExportModifier` + `FnDecl.extern_export` + `VarDecl.is_extern`/`extern_name` + `parseOptionalExternExport()` (unconsumed) + 2 parser unit tests. Suite green (443/633). `lock` commit. - (1.0a) Wired fn-path extern parsing (`parseFnDecl` + both lookahead predicates) + added `FnDecl.extern_lib`/`extern_name` + `VarDecl.extern_lib` per user feedback (decision 4 revised: extern carries an optional lib axis). Unconsumed by lowering. Suite green (443/633). `lock` commit. - (1.0b) Added `examples/1223-ffi-extern-fn.sx` + hand-authored success snapshots. RED (634 ran, 1 failed — sema `body produces no value`). `xfail` commit; 1.1 greens it. ## Known issues None yet.