refactor(ffi-linkage): Phase 9.1d — eliminate the foreign_expr AST node
The last linkage-family 'foreign' carrier. Migrated c_import.zig auto-synthesis
(#import c {#include}) to build the extern shape (empty-block body + extern_export
= .extern_) instead of a foreign_expr body — the Phase 5.0 fn-body flip applied to
auto-synth. With nothing left building it, deleted the foreign_expr union variant +
ForeignExpr struct (ast.zig) and every reader: the dead-arm switch cases (sema,
resolver, generic, call, semantic_diagnostics, lsp), the coalescing reads in
decl.zig (is_foreign local, cc/rename/dedup/variadic/visibility gates) + pack.zig,
and checkForeignRefs (now reads extern_lib only). 9.1 LINKAGE PURGE COMPLETE — all
that remains in src/ is the runtime-class family (9.2) + comments. Snapshot-neutral
(the #import c examples 1215/1216/1217 + sqlite 1624 exercise the synth path); suite
green (646 corpus / 444 unit, 0 failed).
This commit is contained in:
@@ -391,7 +391,7 @@ pub fn funcWantsImplicitCtx(self: *const Lowering, fd: *const ast.FnDecl) bool {
|
||||
// C ABI, no sx context (Phase 2, gap iv).
|
||||
if (fd.extern_export != .none) return false;
|
||||
return switch (fd.body.data) {
|
||||
.foreign_expr, .builtin_expr, .compiler_expr => false,
|
||||
.builtin_expr, .compiler_expr => false,
|
||||
else => !isExportedEntryName(fd.name),
|
||||
};
|
||||
}
|
||||
@@ -2084,21 +2084,18 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
// Foreign declarations with a trailing variadic param map to the C
|
||||
// calling convention's `...` tail. Drop the variadic param from the
|
||||
// IR signature (it has no C-level slot) and set is_variadic.
|
||||
const is_foreign = fd.body.data == .foreign_expr;
|
||||
// Bare `extern` import: an external C symbol declared via the new linkage
|
||||
// surface (empty-block placeholder body, no `foreign_expr`). It shares
|
||||
// `#foreign`'s C-ABI promotion + declareExtern routing below; the optional
|
||||
// `extern LIB "csym"` lib/rename axis (extern_lib/extern_name) is consumed
|
||||
// in Phase 1.2. (`export` defines take the beginFunction path, not here.)
|
||||
// Bare `extern` import: an external C symbol declared via the `extern`
|
||||
// linkage keyword (empty-block placeholder body). C-ABI promotion +
|
||||
// declareExtern routing below; the optional `extern LIB "csym"` lib/rename
|
||||
// axis is extern_lib/extern_name. (`export` defines take the beginFunction
|
||||
// path, not here.) The `#import c` auto-synthesis also produces this shape.
|
||||
const is_extern_decl = fd.extern_export == .extern_;
|
||||
var is_variadic = false;
|
||||
var effective_params = fd.params;
|
||||
// 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) {
|
||||
// A lib-less C-import with a C-variadic `...` tail: drop the trailing slice
|
||||
// param and set is_variadic (mirrored at the call site by
|
||||
// `packVariadicCallArgs`).
|
||||
if (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];
|
||||
}
|
||||
@@ -2119,23 +2116,19 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
// `#foreign` declarations are external C symbols by definition —
|
||||
// promote them to callconv(.c) when the user didn't write it
|
||||
// explicitly. This keeps fn-ptr coercion type-safe: anything
|
||||
// typed by name as `(args) -> ret` of a `#foreign` decl can be
|
||||
// assigned to / passed as a `callconv(.c)` fn-pointer without a
|
||||
// call-convention mismatch.
|
||||
const cc: Function.CallingConvention = if (fd.call_conv == .c or is_foreign or is_extern_decl or fd.extern_export == .export_) .c else .default;
|
||||
// `extern` declarations are external C symbols by definition — promote
|
||||
// them to callconv(.c) when the user didn't write it explicitly. This keeps
|
||||
// fn-ptr coercion type-safe: anything typed by name as `(args) -> ret` of an
|
||||
// `extern` decl can be assigned to / passed as a `callconv(.c)` fn-pointer
|
||||
// without a call-convention mismatch.
|
||||
const cc: Function.CallingConvention = if (fd.call_conv == .c or is_extern_decl or fd.extern_export == .export_) .c else .default;
|
||||
|
||||
// Symbol-name override: `#foreign … "csym"` (foreign_expr.c_name) or the new
|
||||
// `extern … "csym"` / `export … "csym"` (fd.extern_name). Declare under the C
|
||||
// name and map the sx name → C name so call sites resolve to the real symbol.
|
||||
// For `export` the stub is later promoted to a real definition (the body
|
||||
// lowers into this C-named function via lazyLowerFunction — Phase 2.2).
|
||||
// Symbol-name override: `extern … "csym"` / `export … "csym"` (fd.extern_name).
|
||||
// Declare under the C name and map the sx name → C name so call sites resolve
|
||||
// to the real symbol. For `export` the stub is later promoted to a real
|
||||
// definition (the body lowers into this C-named function via lazyLowerFunction).
|
||||
const is_export_decl = fd.extern_export == .export_;
|
||||
const rename_c_name: ?[]const u8 = if (is_foreign)
|
||||
fd.body.data.foreign_expr.c_name
|
||||
else if (is_extern_decl or is_export_decl)
|
||||
const rename_c_name: ?[]const u8 = if (is_extern_decl or is_export_decl)
|
||||
fd.extern_name
|
||||
else
|
||||
null;
|
||||
@@ -2157,7 +2150,7 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
}
|
||||
|
||||
const name_id = self.module.types.internString(name);
|
||||
if ((is_foreign or is_extern_decl) and self.dedupeExternSymbol(fd, name_id, params.items, ret_ty)) return;
|
||||
if (is_extern_decl and self.dedupeExternSymbol(fd, name_id, params.items, ret_ty)) return;
|
||||
const fid = self.builder.declareExtern(name_id, params.items, ret_ty);
|
||||
const func = self.module.getFunctionMut(fid);
|
||||
func.call_conv = cc;
|
||||
@@ -2204,7 +2197,7 @@ pub fn registerQualifiedFn(self: *Lowering, ns_name: []const u8, fd: *const ast.
|
||||
// Foreign / builtin / #compiler bodies keep their literal name; a
|
||||
// qualified alias has no distinct symbol to resolve to.
|
||||
switch (fd.body.data) {
|
||||
.foreign_expr, .builtin_expr, .compiler_expr => return,
|
||||
.builtin_expr, .compiler_expr => return,
|
||||
else => {},
|
||||
}
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ ns_name, short }) catch return;
|
||||
@@ -2251,28 +2244,16 @@ 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 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).
|
||||
// Extern-C gate: only a lib-less C-import fn_decl is policed; a
|
||||
// library-bound `extern LIB` decl (resolves via the named library,
|
||||
// not a module edge) or a non-extern body is unconditionally
|
||||
// visible. A lib-less `extern` decl routes to `visibleOverEdges` so
|
||||
// a transitive reference gets the "C function not visible"
|
||||
// diagnostic, not the generic top-level-name wording (example 1228).
|
||||
const fd = self.program_index.fn_ast_map.get(name) orelse return true;
|
||||
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);
|
||||
},
|
||||
}
|
||||
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),
|
||||
}
|
||||
@@ -2325,7 +2306,7 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
// a fresh ct_module via `evalComptimeString`) emits `.call` against a
|
||||
// FuncId that doesn't exist locally; the interp can't find the
|
||||
// foreign target and silently no-ops instead of dispatching to libc.
|
||||
if (fd.body.data == .foreign_expr or fd.extern_export == .extern_) {
|
||||
if (fd.extern_export == .extern_) {
|
||||
if (self.resolveFuncByName(name) == null) {
|
||||
self.declareFunction(fd, name);
|
||||
self.lowered_functions.put(name, {}) catch {};
|
||||
@@ -2532,7 +2513,7 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i
|
||||
|
||||
// Check if the function body is a builtin or foreign declaration (no body
|
||||
// needed). `extern` imports are declare-only too (empty placeholder body).
|
||||
if (fd.body.data == .builtin_expr or fd.body.data == .foreign_expr or fd.body.data == .compiler_expr or fd.extern_export == .extern_) {
|
||||
if (fd.body.data == .builtin_expr or fd.body.data == .compiler_expr or fd.extern_export == .extern_) {
|
||||
// Already declared by scanDecls/declareFunction (which handles #foreign renames)
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user