test(ffi-linkage): xfail example for extern fn binding (Phase 1.0b)

Add examples/1223-ffi-extern-fn.sx — binds libc 'abs' via bare 'extern'
(sx name = C symbol, no rename). Hand-authored expected/ captures the
SUCCESS output (abs(-7)=7 / abs(42)=42, exit 0).

RED: 1223 is the sole corpus failure (634 ran, 1 failed) — it parses
then errors at sema ('body produces no value') because lowering does
not yet route extern fns through declareExtern. Phase 1.1 wires the
lowering and turns this green.

xfail commit per the cadence rule (no commit both adds a test and makes
it pass).
This commit is contained in:
agra
2026-06-14 13:06:54 +03:00
parent df6b675e67
commit 78e304f552
5 changed files with 43 additions and 21 deletions

View File

@@ -5,18 +5,16 @@ 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 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).
**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)`.
@@ -30,15 +28,15 @@ identifiers in `src/` + 28 doc lines. End-state invariant: **zero `foreign`** (P
+ 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.
**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
@@ -54,6 +52,12 @@ historical carve-out — keep `issues/*.md` provenance, gate the live tree only.
- (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.