Files
sx/current/CHECKPOINT-EXTERN-EXPORT.md
agra 5777ff62ad feat(ffi-linkage): consume extern LIB "csym" rename for fns (Phase 1.2b)
parseFnDecl parses the optional [LIB] ["csym"] tail after the
extern/export keyword into FnDecl.extern_lib/extern_name (mirrors
'#foreign LIB "csym"'). declareFunction unifies the symbol-name
override: rename_c_name = foreign_expr.c_name (for #foreign) OR
fd.extern_name (for extern) -> declare under the C name and map sx->C
in foreign_name_map; the dedupe guard now covers extern too.

examples/1224 green: 'c_abs :: (n) -> i32 extern "abs";' resolves
c_abs to libc abs -> c_abs(-42) = 42. 1223 (bare extern) unregressed.
Suite green (635 corpus / 443 unit).

extern_lib is parsed + stored but not a linking driver — like
'#foreign libc', it references a lib; the #library decl + build flags
remain the separate linking axis (decision 4). green commit.
2026-06-14 13:30:59 +03:00

91 lines
6.0 KiB
Markdown

# 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.1** (green) — wired extern fn lowering in `decl.zig`; example **1223 now
green**, full suite green (634 corpus / 443 unit, 0 fail). A bare `extern` fn lowers
exactly like a lib-less `#foreign` import: `declare i32 @abs(i32) #0` — external
linkage, C ABI, NO `__sx_ctx` param; calls emit `call i32 @abs(i32 -7)` and resolve
against the default-linked libc. Six edits, all routing `extern` declare-only
(mirroring the `foreign_expr` guards): (1) `funcWantsImplicitCtx` suppresses ctx for
`.extern_`; (2) `declareFunction` adds `is_extern_decl` and (3) includes it in the
C-ABI promotion; (4) `lazyLowerFunction` routes `.extern_`→declare-only; (5)
`lowerFunction` declare-only guard; (6) `lowerFunctionBodyInto` never promotes/lowers
an extern stub. Hand-authored 1.0b snapshot matched byte-exact — no regen needed. No
`.ir` snapshot added (the trivial `declare i32 @abs(i32)` doesn't warrant a 1000-line
full-prelude dump; behavioral `.stdout` already catches ctx/linkage regressions).
## 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. **`extern` FUNCTIONS WORK** (import; bare form, no rename) —
parse + lower complete, behavior-equivalent to a lib-less `#foreign` fn. Still TODO in
Phase 1: the `extern LIB "csym"` lib/rename axis (fields exist, unconsumed) and the
extern-global form. `export` not started (Phase 2). 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**: 0.0 tokens, 0.1 AST/parser plumbing,
1.0a fn-path parsing + lib/name fields, 1.0b xfail example, 1.1 fn lowering (green).
## Next step
**Phase 1.2** (green) — two parts, each its own xfail→green or behavior-lock:
1. **`extern LIB "csym"` rename for fns** — extend `parseOptionalExternExport()` (or
`parseFnDecl`) to parse the optional `LIB` ident + `"csym"` string after the
keyword into `FnDecl.extern_lib`/`extern_name`; consume them in `declareFunction`
(mirror the `#foreign` c_name block at `decl.zig:~2119`: declare under the C name,
map sx→C in `foreign_name_map`/dedupe). New example renaming a libc symbol (e.g.
`c_abs :: (n: i32) -> i32 extern "abs";`).
2. **extern-global `g : T extern [LIB] ["csym"];`** — parse path at `parser.zig:425`
(the var-decl with type annotation): accept postfix `extern` → set
`VarDecl.is_extern`/`extern_lib`/`extern_name`; lower like the `#foreign` global
(`decl.zig:~1115-1137`, `.is_extern = vd.is_foreign`). New example mirroring
`examples/1205-ffi-foreign-global` with `extern`.
Stop at end of Phase 1 (do NOT start Phase 2 `export` or Part B migration). Then the
A→B gate: a unit test that `#foreign` and `extern` lower to identical IR.
## 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.
- (1.1) Wired extern fn lowering (6 edits in `decl.zig`, all declare-only routing
mirroring `foreign_expr`): `funcWantsImplicitCtx` + `declareFunction` cc +
`lazyLowerFunction`/`lowerFunction`/`lowerFunctionBodyInto` guards. 1223 green;
`declare i32 @abs(i32)` (C ABI, no ctx). Suite green (634/443). `green` commit.
- (1.2a) Added `examples/1224-ffi-extern-fn-rename.sx` (`c_abs :: … extern "abs";`) +
hand-authored success snapshot (`c_abs(-42) = 42`). RED (635 ran, 1 failed — parse
error: `"abs"` after `extern` not yet accepted). `xfail`; 1.2b greens it. (Also
recovered a formatter-clobbered `parser.zig` — see Known issues.)
- (1.2b) `parseFnDecl` parses the optional `[LIB] ["csym"]` tail into
`extern_lib`/`extern_name`; `declareFunction` unifies the rename (foreign c_name OR
extern_name → declare under C name, map sx→C) and extends the dedupe guard to
extern. 1224 green (`c_abs``abs`); 1223 unregressed. Suite green (635/443).
`green` commit. extern_lib parsed+stored (lib linking stays the `#library` axis).
## Known issues
- **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the
working-tree `src/parser.zig` between commits — it reformatted one-liners AND
silently dropped my `hasFnBodyAfterArrow` extern edit, reverting 1223 to a parse
error. Recovered with `git checkout src/parser.zig` (HEAD had the correct,
committed version). **After any Edit-tool change to a file the IDE may have open,
rebuild + run the affected example before trusting the edit.**