parseFnDecl now calls parseOptionalExternExport() after the callconv
slot and stores the modifier on FnDecl.extern_export. For 'extern' the
body is ';' (an empty-block placeholder — the modifier carries the
linkage, no *_expr node, per the naming constraint). Both fn-decl
lookahead predicates (isFunctionDef, hasFnBodyAfterArrow) now treat
kw_extern/kw_export as fn-body markers beside kw_callconv, so
'(...) -> R extern;' is recognized as a fn def rather than a fn-type
const.
Per user feedback, decision 4 ("library separate") is REVISED: extern
carries an optional LIB + "csym" axis mirroring '#foreign LIB "csym"',
so it is a true #foreign superset (Gate A->B requirement — the Part B
migration of 466 #foreign uses across 6 libs must preserve each
symbol's library). Added FnDecl.extern_lib/extern_name and
VarDecl.extern_lib (beside is_extern/extern_name).
All unconsumed by lowering: extern parses, but a fn still errors at
sema (body produces no value). Suite green (443 unit / 633 corpus).
lock commit.
60 lines
3.5 KiB
Markdown
60 lines
3.5 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.0a** (lock, green — parse half of 1.0) — the fn-decl path now ACCEPTS
|
|
postfix `extern`/`export`. `parseFnDecl` calls `parseOptionalExternExport()` after the
|
|
callconv slot and stores it on `FnDecl.extern_export`; for `extern` the body is `;`
|
|
(an empty-block placeholder — modifier carries the linkage, no `*_expr` node). Both
|
|
lookahead predicates (`isFunctionDef` + `hasFnBodyAfterArrow`) now treat
|
|
`kw_extern`/`kw_export` as fn-body markers (beside `kw_callconv`) so `(...) -> R
|
|
extern;` is recognized as a fn def, not a fn-type const. **Per user feedback (decision
|
|
4 REVISED):** added `extern_lib`+`extern_name` to `FnDecl` and `extern_lib` to
|
|
`VarDecl` (next to `is_extern`/`extern_name`) — the optional `LIB`+`"csym"` axis
|
|
mirroring `#foreign LIB "csym"`, so `extern` is a true `#foreign` superset (Gate A→B).
|
|
All unconsumed by lowering — `extern` parses but a fn still errors at sema
|
|
(`body produces no value`). Suite green (443 unit / 633 corpus, 0 fail).
|
|
|
|
## 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.0b** (xfail — example half of 1.0) — add `examples/12xx-ffi-extern-fn.sx`
|
|
that extern-binds a libc symbol (`abs`); expected files capture the SUCCESS output, so
|
|
the example is **red** now (parses, then errors at sema since lowering is unwired).
|
|
Then **1.1** (green): in `decl.zig`, when `extern_export == .extern_`, route the fn
|
|
through `declareExtern` (`is_extern`, `.external`, `callconv(.c)`, no ctx — anchors
|
|
`decl.zig:1123,387,2110,2113`) instead of lowering the placeholder body → example
|
|
green. Then **1.2** (green): `extern LIB "csym"` rename (consume `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.
|
|
|
|
## Known issues
|
|
None yet.
|