fix(ffi-linkage): Phase 5.0 prereq — map extern C-variadic tail to ... like #foreign

Two gates were keyed on the `#foreign` (foreign_expr) body shape only:
- declareFunction: the is_variadic drop (decl.zig) — a variadic extern
  kept its trailing slice param in the IR signature.
- packVariadicCallArgs: the call-site early-out (pack.zig) — extras were
  slice-packed instead of passed through the C `...` slot.

Both now also fire for `extern_export == .extern_`, so a variadic
`extern` drops the trailing `..args: []T`, sets is_variadic, and passes
extras through the C ABI with default argument promotion — byte-identical
to its `#foreign` twin. Greens example 1229.

645 corpus / 444 unit, 0 failed.
This commit is contained in:
agra
2026-06-14 21:05:40 +03:00
parent 9a2c78d6b9
commit 0fdc82154f
2 changed files with 14 additions and 5 deletions

View File

@@ -2094,7 +2094,12 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
const is_extern_decl = fd.extern_export == .extern_;
var is_variadic = false;
var effective_params = fd.params;
if (is_foreign and fd.params.len > 0 and fd.params[fd.params.len - 1].is_variadic) {
// The C-variadic `...` tail applies to BOTH lib-less C-import spellings:
// the legacy `#foreign` (foreign_expr body) and the new `extern` keyword.
// A migrated variadic `extern` must drop the trailing slice param and set
// the flag exactly as its `#foreign` twin did (mirrored at the call site
// by `packVariadicCallArgs`).
if ((is_foreign or is_extern_decl) and fd.params.len > 0 and fd.params[fd.params.len - 1].is_variadic) {
is_variadic = true;
effective_params = fd.params[0 .. fd.params.len - 1];
}