From 5777ff62ade08fe4dc489e7c1bd4452b2f584f40 Mon Sep 17 00:00:00 2001 From: agra Date: Sun, 14 Jun 2026 13:30:59 +0300 Subject: [PATCH] feat(ffi-linkage): consume extern LIB "csym" rename for fns (Phase 1.2b) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseFnDecl parses the optional [LIB] ["csym"] tail after the extern/export keyword into FnDecl.extern_lib/extern_name (mirrors '#foreign LIB "csym"'). declareFunction unifies the symbol-name override: rename_c_name = foreign_expr.c_name (for #foreign) OR fd.extern_name (for extern) -> declare under the C name and map sx->C in foreign_name_map; the dedupe guard now covers extern too. examples/1224 green: 'c_abs :: (n) -> i32 extern "abs";' resolves c_abs to libc abs -> c_abs(-42) = 42. 1223 (bare extern) unregressed. Suite green (635 corpus / 443 unit). extern_lib is parsed + stored but not a linking driver — like '#foreign libc', it references a lib; the #library decl + build flags remain the separate linking axis (decision 4). green commit. --- current/CHECKPOINT-EXTERN-EXPORT.md | 5 ++++ src/ir/lower/decl.zig | 39 ++++++++++++++++------------- src/parser.zig | 20 +++++++++++++++ 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/current/CHECKPOINT-EXTERN-EXPORT.md b/current/CHECKPOINT-EXTERN-EXPORT.md index 23c4646..99f5b5e 100644 --- a/current/CHECKPOINT-EXTERN-EXPORT.md +++ b/current/CHECKPOINT-EXTERN-EXPORT.md @@ -75,6 +75,11 @@ historical carve-out — keep `issues/*.md` provenance, gate the live tree only. hand-authored success snapshot (`c_abs(-42) = 42`). RED (635 ran, 1 failed — parse error: `"abs"` after `extern` not yet accepted). `xfail`; 1.2b greens it. (Also recovered a formatter-clobbered `parser.zig` — see Known issues.) +- (1.2b) `parseFnDecl` parses the optional `[LIB] ["csym"]` tail into + `extern_lib`/`extern_name`; `declareFunction` unifies the rename (foreign c_name OR + extern_name → declare under C name, map sx→C) and extends the dedupe guard to + extern. 1224 green (`c_abs`→`abs`); 1223 unregressed. Suite green (635/443). + `green` commit. extern_lib parsed+stored (lib linking stays the `#library` axis). ## Known issues - **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the diff --git a/src/ir/lower/decl.zig b/src/ir/lower/decl.zig index a13b56d..c140137 100644 --- a/src/ir/lower/decl.zig +++ b/src/ir/lower/decl.zig @@ -2118,29 +2118,34 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8) // call-convention mismatch. const cc: Function.CallingConvention = if (fd.call_conv == .c or is_foreign or is_extern_decl) .c else .default; - // For #foreign with C name override, declare under C name and map sx name → C name - if (is_foreign) { - const fe = fd.body.data.foreign_expr; - if (fe.c_name) |c_name| { - const c_name_id = self.module.types.internString(c_name); - if (self.dedupeForeignSymbol(fd, c_name_id, params.items, ret_ty)) { - self.foreign_name_map.put(name, c_name) catch {}; - return; - } - const fid = self.builder.declareExtern(c_name_id, params.items, ret_ty); - const func = self.module.getFunctionMut(fid); - func.call_conv = cc; - func.source_file = self.current_source_file; - func.is_variadic = is_variadic; - func.has_implicit_ctx = wants_ctx; + // Symbol-name override: `#foreign … "csym"` (foreign_expr.c_name) or the new + // `extern … "csym"` (fd.extern_name). Declare under the C name and map the sx + // name → C name so call sites resolve to the real symbol. + const rename_c_name: ?[]const u8 = if (is_foreign) + fd.body.data.foreign_expr.c_name + else if (is_extern_decl) + fd.extern_name + else + null; + if (rename_c_name) |c_name| { + const c_name_id = self.module.types.internString(c_name); + if (self.dedupeForeignSymbol(fd, c_name_id, params.items, ret_ty)) { self.foreign_name_map.put(name, c_name) catch {}; - self.fn_decl_fids.put(fd, fid) catch {}; return; } + const fid = self.builder.declareExtern(c_name_id, params.items, ret_ty); + const func = self.module.getFunctionMut(fid); + func.call_conv = cc; + func.source_file = self.current_source_file; + func.is_variadic = is_variadic; + func.has_implicit_ctx = wants_ctx; + self.foreign_name_map.put(name, c_name) catch {}; + self.fn_decl_fids.put(fd, fid) catch {}; + return; } const name_id = self.module.types.internString(name); - if (is_foreign and self.dedupeForeignSymbol(fd, name_id, params.items, ret_ty)) return; + if ((is_foreign or is_extern_decl) and self.dedupeForeignSymbol(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; diff --git a/src/parser.zig b/src/parser.zig index ae7ab5c..543b1a8 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -1952,6 +1952,24 @@ pub const Parser = struct { // Optional postfix linkage modifier: `extern` (import) / `export` (define). const extern_export = self.parseOptionalExternExport(); + // Optional `[LIB] ["csym"]` tail after extern/export — a library-alias + // ident then a C symbol-name string, both optional (mirrors + // `#foreign LIB "csym"`). Stored on extern_lib/extern_name; the rename + // is consumed in `declareFunction`, the lib reference in Part B. + var extern_lib: ?[]const u8 = null; + var extern_name: ?[]const u8 = null; + if (extern_export != .none) { + if (self.current.tag == .identifier) { + extern_lib = self.tokenSlice(self.current); + self.advance(); + } + if (self.current.tag == .string_literal) { + const raw = self.tokenSlice(self.current); + extern_name = raw[1 .. raw.len - 1]; + self.advance(); + } + } + // Body: block `{ ... }`, arrow `=> expr;`, #builtin, #compiler, or #foreign marker. // An `extern` import has NO body — just `;`. The extern_export modifier // carries the linkage; we synthesize an empty block as the (non-optional) @@ -2025,6 +2043,8 @@ pub const Parser = struct { .is_arrow = is_arrow, .call_conv = call_conv, .extern_export = extern_export, + .extern_lib = extern_lib, + .extern_name = extern_name, .name_span = name_span, .is_raw = name_is_raw, } });