fix(ffi-linkage): Phase 5.0 prereq — exclude extern imports from plain-free-fn classification

isPlainFreeFn / isPlainFreeFnDecl excluded a #foreign body but classified
an empty-block extern fn as a plain free function, so existing extern fns
were wrongly counted in the bare-call ambiguity verdict (and eligible for
the out-of-line-slot / shadow-author pass). Both predicates now also
exclude extern_export == .extern_ (an external C symbol with no
sx-lowerable body, name-keyed first-wins dispatch like #foreign); export
keeps a real body and stays plain-free. Greens example 1230 — same-name
extern authors compile like their #foreign twins (0729).

646 corpus / 444 unit, 0 failed.
This commit is contained in:
agra
2026-06-15 03:46:34 +03:00
parent 270652186e
commit 3c94c14b5e
2 changed files with 14 additions and 4 deletions

View File

@@ -808,12 +808,16 @@ pub fn hasComptimeParams(fd: *const ast.FnDecl) bool {
}
/// A plain free function: no type params (not generic) and an ordinary sx
/// body (not `#foreign` / `#builtin` / `#compiler`). Only these get an
/// out-of-line identity-addressable slot — the bare-call disambiguation
/// body (not `#foreign` / `#builtin` / `#compiler` / `extern`). Only these get
/// an out-of-line identity-addressable slot — the bare-call disambiguation
/// and the shadow-author lowering pass leave every other shape
/// to the existing name-keyed dispatch.
pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
if (fd.type_params.len > 0) return false;
// An `extern` import is an external C symbol with no sx-lowerable body —
// name-keyed first-wins dispatch like a `#foreign` body, never a plain free
// fn. `export` DEFINES a real body, so it stays plain-free.
if (fd.extern_export == .extern_) return false;
return switch (fd.body.data) {
.foreign_expr, .builtin_expr, .compiler_expr => false,
else => true,

View File

@@ -173,10 +173,16 @@ pub fn fnDeclOf(raw: RawDeclRef) ?*const ast.FnDecl {
}
/// A PLAIN free function — no type params, an ordinary (non-`#foreign`/
/// `#builtin`/`#compiler`) body — the only callable kind the bare-call verdict
/// counts.
/// `#builtin`/`#compiler`/`extern`) body — the only callable kind the bare-call
/// verdict counts.
pub fn isPlainFreeFnDecl(fd: *const ast.FnDecl) bool {
if (fd.type_params.len > 0) return false;
// An `extern` import is an external C symbol with no sx-lowerable body —
// dispatched name-keyed first-wins, exactly like a `#foreign` body, so it
// is NOT a plain free fn (excluded from the bare-call ambiguity verdict and
// the out-of-line-slot / shadow-author pass). `export` DEFINES a real sx
// body, so it stays plain-free.
if (fd.extern_export == .extern_) return false;
return switch (fd.body.data) {
.foreign_expr, .builtin_expr, .compiler_expr => false,
else => true,