fix(ffi-linkage): Phase 5.0 prereq — police lib-less extern like #foreign in c_import_bare gate

The non-transitive C-import visibility gate (`isVisible(.c_import_bare)`)
only recognised the legacy `#foreign` body shape; a bare `extern` fn
(empty-block body + extern_export == .extern_) escaped via the
`body != foreign_expr -> return true` arm and was caught only by the
general isNameVisible gate, yielding the generic 'not visible' wording
instead of the C-specific 'C function not visible; add #import' one.

Now both lib-less spellings route to visibleOverEdges, and a library-
bound `extern LIB` (like a library-bound `#foreign LIB`) stays
unconditionally visible. This makes a future fn-decl `#foreign`->`extern`
migration byte-identical at this gate. Greens example 1228.

644 corpus / 444 unit, 0 failed.
This commit is contained in:
agra
2026-06-14 20:57:19 +03:00
parent 717c35d26d
commit 7d8ba1aabc

View File

@@ -2247,13 +2247,28 @@ pub fn isVisible(self: *Lowering, name: []const u8, vis: resolver_mod.Visibility
// this predicate is single-hop only.
.impl_transitive => @panic("isVisible: transitive visibility is owned by findVisibleImpls"),
.c_import_bare => {
// Foreign-C gate: only C-import fn_decls without a library_ref
// are policed; a non-foreign body or a library-bound foreign
// decl is unconditionally visible.
// Foreign-C gate: only a lib-less C-import fn_decl is policed; a
// library-bound decl (resolves via the named library, not a
// module edge) or a non-C body is unconditionally visible. The
// legacy `#foreign` form (a `foreign_expr` body) and the new
// `extern` keyword (`extern_export == .extern_`, empty-block body)
// are two spellings of the same lib-less C-symbol import, so BOTH
// route to `visibleOverEdges` here — a migrated `extern` decl must
// get the identical "C function not visible" diagnostic its
// `#foreign` twin did, not the generic top-level-name wording
// (FFI-linkage Part B; example 1228).
const fd = self.program_index.fn_ast_map.get(name) orelse return true;
if (fd.body.data != .foreign_expr) return true;
if (fd.body.data.foreign_expr.library_ref != null) return true;
return self.visibleOverEdges(name);
switch (fd.body.data) {
.foreign_expr => |fe| {
if (fe.library_ref != null) return true;
return self.visibleOverEdges(name);
},
else => {
if (fd.extern_export != .extern_) return true;
if (fd.extern_lib != null) return true;
return self.visibleOverEdges(name);
},
}
},
.user_bare_flat => return self.visibleOverEdges(name),
}