docs(ffi-linkage): checkpoint — prereqs 3 & 4 done (4/4); fn-body flip de-risked, Decision 7 open
Plain-free classification + extern lib-ref validation closed (the 3rd and 4th extern/#foreign divergences). All four fn-path prereqs now done. The fn-decl #foreign->extern flip is scoped: IR zero-churn, only example 1620's lib-ref wording churns. Records Decision 7 (interim diagnostic wording) as the one gate before executing the flip.
This commit is contained in:
@@ -5,9 +5,30 @@ Companion to `current/PLAN-EXTERN-EXPORT.md` — one merged plan: **Part A** add
|
|||||||
every commit, one step at a time per the cadence rule.
|
every commit, one step at a time per the cadence rule.
|
||||||
|
|
||||||
## Last completed step
|
## Last completed step
|
||||||
**Phase 5.0 prereq — extern C-variadic tail** (xfail `9a2c78d` → fix `0fdc821`) —
|
**Phase 5.0 prereqs 3 & 4 — plain-free classification + extern lib-ref validation**
|
||||||
the SECOND (and last) deferred fn-path prerequisite. **BOTH fn-path prereqs now
|
(plain-free: xfail `2706521` → fix `3c94c14`; lib-ref: xfail `38c3240` → fix
|
||||||
done.** The C-variadic `...` handling was keyed on the `#foreign` (`foreign_expr`)
|
`ad6aed3`). Two MORE extern/#foreign divergences found while de-risking the fn-path
|
||||||
|
flip, both now closed. **FOUR prereqs total now done — the fn-decl `#foreign`→`extern`
|
||||||
|
flip is fully de-risked.**
|
||||||
|
- **Prereq 3 (plain-free):** `isPlainFreeFn`/`isPlainFreeFnDecl` (resolver.zig:178,
|
||||||
|
generic.zig:815) excluded a `#foreign` body but classified an empty-block `extern`
|
||||||
|
fn as a plain free fn — so existing extern fns were wrongly counted in the bare-call
|
||||||
|
ambiguity verdict (example: two same-name `extern libc "abs"` authors errored
|
||||||
|
ambiguous, while the `#foreign` twin 0729 compiles). Both predicates now also
|
||||||
|
exclude `extern_export == .extern_`; `export` (real body) stays plain-free. Example
|
||||||
|
**1230**.
|
||||||
|
- **Prereq 4 (lib-ref validation):** `checkForeignRefs` (c_import.zig) validated only
|
||||||
|
`foreign_expr.library_ref`, so a bogus `extern nosuchunit "abs"` compiled silently
|
||||||
|
while `#foreign nosuchunit` errors (1620). Now reads the lib ref from EITHER spelling
|
||||||
|
and names the surface keyword in the diagnostic (so 1620 stays byte-unchanged).
|
||||||
|
Example **1231**.
|
||||||
|
- Two OTHER classifying sites probed and found BENIGN for extern (no flip prereq):
|
||||||
|
namespace/qualified dispatch (`registerQualifiedFn` decl.zig:2208, namespace gate
|
||||||
|
call.zig:729) — a namespaced `extern` fn resolves identically to its `#foreign` twin
|
||||||
|
(probe: `cm.c_abs(-9)` → 9 both ways; the registered qualified alias resolves to the
|
||||||
|
same extern symbol).
|
||||||
|
|
||||||
|
### Prior: Phase 5.0 prereq — extern C-variadic tail (xfail `9a2c78d` → fix `0fdc821`) — the SECOND deferred fn-path prerequisite. **BOTH original fn-path prereqs done.** The C-variadic `...` handling was keyed on the `#foreign` (`foreign_expr`)
|
||||||
body shape at two sites — the `is_variadic` drop in `declareFunction`
|
body shape at two sites — the `is_variadic` drop in `declareFunction`
|
||||||
(`decl.zig:2097`) and the call-site early-out in `packVariadicCallArgs`
|
(`decl.zig:2097`) and the call-site early-out in `packVariadicCallArgs`
|
||||||
(`pack.zig:302`). A variadic `extern` therefore kept its trailing slice param and
|
(`pack.zig:302`). A variadic `extern` therefore kept its trailing slice param and
|
||||||
@@ -111,8 +132,29 @@ AOT), 1227 (export fn rename, AOT), 1348 (objc extern class), 1349 (objc export
|
|||||||
(jni extern class), 1174/1175 (interplay diagnostics).
|
(jni extern class), 1174/1175 (interplay diagnostics).
|
||||||
|
|
||||||
## Next step
|
## Next step
|
||||||
**PART B — Phase 5.0, fn-decl `#foreign` body-marker migration** (BOTH prereqs —
|
**PART B — Phase 5.0, fn-decl `#foreign` body-marker flip** — fully de-risked (ALL
|
||||||
visibility-gate + variadic — are now DONE). Route the fn-decl `#foreign` path so a
|
FOUR prereqs done: visibility, variadic, plain-free classification, lib-ref
|
||||||
|
validation). The flip itself is now small. **AWAITING ONE DESIGN DECISION** (see
|
||||||
|
"Open decisions / Decision 7" below) before executing — it determines interim
|
||||||
|
diagnostic wording + whether one snapshot churns.
|
||||||
|
|
||||||
|
Mechanics of the flip (when greenlit):
|
||||||
|
1. `parser.zig:~2081` fn-body `#foreign` arm: build the extern shape
|
||||||
|
(`extern_export = .extern_`, `library_ref→extern_lib`, `c_name→extern_name`,
|
||||||
|
empty-block body) instead of a `foreign_expr` body.
|
||||||
|
2. Update parser unit test `parser.zig:4266-4267` (asserts the fn-body `#foreign`
|
||||||
|
builds a `foreign_expr` body with `library_ref "rl"`) to assert the extern shape.
|
||||||
|
3. Run the A→B gate (`lower.test.zig`) + full `zig build test`.
|
||||||
|
|
||||||
|
**Verified churn surface (NARROW):** IR is zero-churn (all lowering sites coalesce
|
||||||
|
`is_foreign`/`extern_export` — confirmed across this stream). The ONLY corpus churn
|
||||||
|
is example **1620**'s lib-ref message: a `#foreign`-spelled decl becomes the extern
|
||||||
|
AST, so `checkForeignRefs` would emit "extern library 'X'…" instead of "#foreign
|
||||||
|
library 'X'…". Parser-surface diagnostics (`parser.zig:484/1429`) and runtime-class
|
||||||
|
messages are unaffected (they fire on the literal keyword pre-AST). `c_import.zig`
|
||||||
|
auto-synthesis STILL builds `foreign_expr` bodies (not migrated this step), so
|
||||||
|
`foreign_expr` does not disappear — both shapes coexist, which is why every reader
|
||||||
|
had to coalesce. Route the fn-decl `#foreign` path so a
|
||||||
`#foreign` fn builds the SAME extern AST that postfix `extern` already produces,
|
`#foreign` fn builds the SAME extern AST that postfix `extern` already produces,
|
||||||
instead of a `foreign_expr` body. This is the highest-value path (the bulk of
|
instead of a `foreign_expr` body. This is the highest-value path (the bulk of
|
||||||
`#foreign` usage). Key sub-questions to resolve before/while routing:
|
`#foreign` usage). Key sub-questions to resolve before/while routing:
|
||||||
@@ -201,8 +243,33 @@ Part A ratified (bare / postfix / `⇒ callconv(.c)` / lib-separate). Part B:
|
|||||||
- **Decision 6 STILL OPEN**: historical carve-out — keep `issues/*.md` (+ design-doc prose)
|
- **Decision 6 STILL OPEN**: historical carve-out — keep `issues/*.md` (+ design-doc prose)
|
||||||
as provenance & gate only the live tree (recommended) vs purge everything. The user did
|
as provenance & gate only the live tree (recommended) vs purge everything. The user did
|
||||||
NOT confirm this at the Part A milestone; confirm before Phase 9.
|
NOT confirm this at the Part A milestone; confirm before Phase 9.
|
||||||
|
- **Decision 7 OPEN — interim diagnostic wording for `#foreign`-spelled decls** (gates the
|
||||||
|
fn-body flip). Once the flip lands, a `#foreign`-spelled fn builds the extern AST, so any
|
||||||
|
diagnostic that reads the unified AST can no longer tell the user wrote `#foreign` vs
|
||||||
|
`extern`. Concretely, example 1620's lib-ref error flips "#foreign library…" →
|
||||||
|
"extern library…". Options: **(A, recommended)** accept the narrow churn — regen 1620 as
|
||||||
|
intentional; it aligns with Part B's `extern`-only end state and the interim oddity
|
||||||
|
(`#foreign` source → "extern" message) is cosmetic and short-lived (Phase 8 cutover
|
||||||
|
removes `#foreign`). **(B)** retain a one-bit surface marker on `FnDecl` (`wrote_foreign`)
|
||||||
|
so interim diagnostics stay keyword-accurate (zero churn, small extra plumbing, marker
|
||||||
|
deleted at cutover). Affects only diagnostic wording — IR/behavior identical either way.
|
||||||
|
|
||||||
## Log
|
## Log
|
||||||
|
- (5.0 prereq plain-free xfail) Added `1230-ffi-extern-same-name-authors` (two flat
|
||||||
|
authors of `absval` via `extern libc "abs"`; the `extern` twin of `#foreign` 0729).
|
||||||
|
RED — extern authors wrongly counted as ambiguous (646/1 fail). `test`/xfail `2706521`.
|
||||||
|
- (5.0 prereq plain-free fix) `isPlainFreeFn`/`isPlainFreeFnDecl` now also exclude
|
||||||
|
`extern_export == .extern_` (external C symbol, no sx body; name-keyed first-wins like
|
||||||
|
`#foreign`); `export` stays plain-free. 1230 green (`absval = 7`). Suite green (646/444).
|
||||||
|
`fix`/green `3c94c14`.
|
||||||
|
- (5.0 prereq lib-ref xfail) Added `1231-ffi-extern-undeclared-lib` (`extern nosuchunit
|
||||||
|
"abs"` — bogus lib ref). RED — compiles silently (extern lib ref unvalidated).
|
||||||
|
`test`/xfail `38c3240`.
|
||||||
|
- (5.0 prereq lib-ref fix) `checkForeignRefs` (c_import.zig) now reads the lib ref from
|
||||||
|
either spelling (foreign_expr.library_ref OR extern_lib) and names the surface keyword,
|
||||||
|
so 1620 (#foreign) is byte-unchanged and 1231 (extern) gets "extern library … not
|
||||||
|
declared". 1231 green. Suite green (647/444). `fix`/green `ad6aed3`. **ALL FOUR fn-path
|
||||||
|
prereqs DONE → fn-body flip de-risked; awaiting Decision 7 (interim wording).**
|
||||||
- (5.0 prereq variadic xfail) Added `1229-ffi-extern-cvariadic` (JIT `#source`,
|
- (5.0 prereq variadic xfail) Added `1229-ffi-extern-cvariadic` (JIT `#source`,
|
||||||
int-sum + double-avg, `extern` C-variadic). Expected snapshot pins the DESIRED
|
int-sum + double-avg, `extern` C-variadic). Expected snapshot pins the DESIRED
|
||||||
correct output. RED (variadic `extern` slice-packs extras → garbage:
|
correct output. RED (variadic `extern` slice-packs extras → garbage:
|
||||||
|
|||||||
Reference in New Issue
Block a user