Files
sx/current/CHECKPOINT-EXTERN-EXPORT.md
agra df6b675e67 feat(ffi-linkage): fn-path accepts postfix extern/export + lib/name fields (Phase 1.0a)
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.
2026-06-14 13:02:42 +03:00

3.5 KiB

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.