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.
|
||||
|
||||
## Last completed step
|
||||
**Phase 5.0 prereq — extern C-variadic tail** (xfail `9a2c78d` → fix `0fdc821`) —
|
||||
the SECOND (and last) deferred fn-path prerequisite. **BOTH fn-path prereqs now
|
||||
done.** The C-variadic `...` handling was keyed on the `#foreign` (`foreign_expr`)
|
||||
**Phase 5.0 prereqs 3 & 4 — plain-free classification + extern lib-ref validation**
|
||||
(plain-free: xfail `2706521` → fix `3c94c14`; lib-ref: xfail `38c3240` → fix
|
||||
`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`
|
||||
(`decl.zig:2097`) and the call-site early-out in `packVariadicCallArgs`
|
||||
(`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).
|
||||
|
||||
## Next step
|
||||
**PART B — Phase 5.0, fn-decl `#foreign` body-marker migration** (BOTH prereqs —
|
||||
visibility-gate + variadic — are now DONE). Route the fn-decl `#foreign` path so a
|
||||
**PART B — Phase 5.0, fn-decl `#foreign` body-marker flip** — fully de-risked (ALL
|
||||
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,
|
||||
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:
|
||||
@@ -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)
|
||||
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.
|
||||
- **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
|
||||
- (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`,
|
||||
int-sum + double-avg, `extern` C-variadic). Expected snapshot pins the DESIRED
|
||||
correct output. RED (variadic `extern` slice-packs extras → garbage:
|
||||
|
||||
Reference in New Issue
Block a user