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:
@@ -259,11 +259,13 @@ pub fn processCImport(
|
||||
else
|
||||
try mapCTypeToSxNode(allocator, ret_str);
|
||||
|
||||
// Create foreign_expr body (no library_ref — symbols resolved at runtime)
|
||||
const foreign_body = try allocator.create(Node);
|
||||
foreign_body.* = .{
|
||||
// Extern-import body: an empty block + `extern_export = .extern_` (no
|
||||
// LIB / csym — symbols resolve at runtime). Same shape the postfix
|
||||
// `extern` keyword produces; lowering reads `extern_export`.
|
||||
const extern_body = try allocator.create(Node);
|
||||
extern_body.* = .{
|
||||
.span = .{ .start = 0, .end = 0 },
|
||||
.data = .{ .foreign_expr = .{ .library_ref = null, .c_name = null } },
|
||||
.data = .{ .block = .{ .stmts = &.{}, .produces_value = false } },
|
||||
};
|
||||
|
||||
const fn_node = try allocator.create(Node);
|
||||
@@ -273,9 +275,10 @@ pub fn processCImport(
|
||||
.name = name,
|
||||
.params = try params.toOwnedSlice(allocator),
|
||||
.return_type = ret_node,
|
||||
.body = foreign_body,
|
||||
// A foreign C function whose own NAME collides with a reserved
|
||||
// type spelling (`int i2(int);`) is RAW — exempt from the
|
||||
.body = extern_body,
|
||||
.extern_export = .extern_,
|
||||
// A C function whose own NAME collides with a reserved type
|
||||
// spelling (`int i2(int);`) is RAW — exempt from the
|
||||
// reserved-type-name decl check so generated bindings import
|
||||
// without hand-edits.
|
||||
.is_raw = true,
|
||||
@@ -332,20 +335,12 @@ fn checkForeignRefs(valid: *const std.StringHashMap(void), decls: []const *Node,
|
||||
for (decls) |d| {
|
||||
switch (d.data) {
|
||||
.fn_decl => |fd| {
|
||||
// A library reference rides on the legacy `#foreign` body
|
||||
// (foreign_expr.library_ref) OR the new `extern LIB` keyword
|
||||
// (extern_lib); both must name a declared #library / #import c
|
||||
// unit. The diagnostic names the surface keyword the user wrote
|
||||
// so the two spellings stay self-describing.
|
||||
const kw: []const u8, const ref: []const u8 = switch (fd.body.data) {
|
||||
.foreign_expr => |fe| .{ "#foreign", fe.library_ref orelse continue },
|
||||
else => if (fd.extern_export == .extern_)
|
||||
.{ "extern", fd.extern_lib orelse continue }
|
||||
else
|
||||
continue,
|
||||
};
|
||||
// A library reference rides on the `extern LIB` keyword
|
||||
// (extern_lib); it must name a declared #library / #import c unit.
|
||||
if (fd.extern_export != .extern_) continue;
|
||||
const ref = fd.extern_lib orelse continue;
|
||||
if (!valid.contains(ref)) {
|
||||
diags.addFmt(.err, d.span, "{s} library '{s}' is not declared; expected a #library constant or a named '#import c' unit", .{ kw, ref });
|
||||
diags.addFmt(.err, d.span, "extern library '{s}' is not declared; expected a #library constant or a named '#import c' unit", .{ref});
|
||||
}
|
||||
},
|
||||
.namespace_decl => |ns| checkForeignRefs(valid, ns.decls, diags),
|
||||
|
||||
Reference in New Issue
Block a user