diff --git a/current/CHECKPOINT-EXTERN-EXPORT.md b/current/CHECKPOINT-EXTERN-EXPORT.md index 96d1bf2..c3c6615 100644 --- a/current/CHECKPOINT-EXTERN-EXPORT.md +++ b/current/CHECKPOINT-EXTERN-EXPORT.md @@ -5,8 +5,20 @@ 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 — visibility-gate equivalence** (xfail `717c35d` → fix -`7d8ba1a`) — the first of the two deferred fn-path prerequisites is now LOCKED. +**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`) +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 +slice-packed the extras → garbage at the C ABI (probe: `sum_ints(3,10,20,30)` → +53316585, not 60). Both gates now also fire for `extern_export == .extern_`, so a +variadic `extern` drops the `..args: []T`, sets `is_variadic`, and passes extras +through the C `...` slot with default argument promotion — byte-identical to its +`#foreign` twin. New example **1229** (`1229-ffi-extern-cvariadic`, JIT `#source`, +int-sum + double-avg). Suite green (645 corpus / 444 unit, 0 failed). + +### Prior: Phase 5.0 prereq — visibility-gate equivalence (xfail `717c35d` → fix `7d8ba1a`) — the first of the two deferred fn-path prerequisites. The non-transitive C-import visibility gate (`isVisible(.c_import_bare)`, `decl.zig:2249`) used to recognise only the legacy `#foreign` body shape; a bare `extern` fn (empty-block body + `extern_export == .extern_`) escaped the gate via @@ -99,15 +111,32 @@ 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-path variadic prerequisite** (the SECOND of the two -deferred fn-path prereqs; visibility-gate equivalence is now DONE). The -`is_variadic` drop in `declareFunction` is gated on `is_foreign` only, so a -migrated variadic `extern` (e.g. `printf`) would lose its `...` tail. Extend the -gate to cover `extern_export == .extern_`. Cadence: xfail an `extern` variadic fn -losing its `...` (locks the gap), then the next commit fixes the gate. Find the -exact site via `grep -n "is_variadic" src/ir/lower/decl.zig` (the checkpoint's old -`decl.zig:2097` line ref may have drifted). AFTER both prereqs land, the fn-decl -`#foreign` body-marker path (`parser.zig:~2062`) can migrate onto `extern`. +**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 +`#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: +- The `foreign_expr` node carries `library_ref` + `c_name`; the `extern` fn carries + `extern_export = .extern_` + `extern_lib` + `extern_name` on the FnDecl with an + empty-block body. Migration = the parser's fn-body `#foreign` arm + (`parser.zig:~2062`) builds the extern shape (set `extern_export`, map + `library_ref→extern_lib`, `c_name→extern_name`) rather than a `foreign_expr`. +- Lowering ALREADY coalesces the two at every fn site checked this stream + (`decl.zig` 2088/2124/2132/2156/2324/2531 read `is_foreign OR extern_export`), + and the two prereq gates (visibility `decl.zig:2249`, variadic `decl.zig:2097` + + `pack.zig:302`) now do too — so the migration should be behavior-preserving with + ZERO snapshot churn. VERIFY with the A→B gate test (`lower.test.zig`) + a full + `zig build test` after routing; any churn means a site still reads `foreign_expr` + structurally and must be coalesced first. +- ⚠ This ALSO migrates the **const-with-type** path implicitly IF it shares the same + `foreign_expr`→extern reshape (it builds `const_decl{value=foreign_expr}`). Decide: + reshape the const path's value node alongside, or leave the dead const path on + `foreign_expr` until Phase 8 cutover. The const path is dead (see findings below), + so leaving it is acceptable; but the parser arm is shared-ish — check whether the + fn-body arm change touches it. +- Cadence: because the migration is behavior-preserving (no churn), it's a single + `refactor`/lock commit (like the 5.0 global-path commit `e5ddfbe`), NOT an + xfail→fix pair. **Investigation findings (this session — reorder the remaining paths):** - **const-with-type** (`parser.zig:316`, `name :: type_expr #foreign …`) is a @@ -174,6 +203,16 @@ Part A ratified (bare / postfix / `⇒ callconv(.c)` / lib-separate). Part B: NOT confirm this at the Part A milestone; confirm before Phase 9. ## Log +- (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: + `sum_ints(3,10,20,30)` → 53316585; doubles → 0.0). `test`/xfail `9a2c78d`. +- (5.0 prereq variadic fix) Extended the two C-variadic gates — the `is_variadic` + drop in `declareFunction` (`decl.zig:2097`) and the early-out in + `packVariadicCallArgs` (`pack.zig:302`) — to fire for `extern_export == .extern_` + as well as a `foreign_expr` body. 1229 green (`60` / `2.000000`). Suite green + (645 corpus / 444 unit, 0 failed). `fix`/green `0fdc821`. **BOTH fn-path prereqs + DONE → fn-decl `#foreign` body-marker migration unblocked.** - (5.0 prereq vis xfail) Added cross-module example `1228-ffi-extern-c-non-transitive` (main → b → c). Main references c's lib-less `#foreign` + `extern` twins transitively; expected snapshot pins the DESIRED equivalent C-specific