feat(ffi-linkage): consume extern LIB "csym" rename for fns (Phase 1.2b)
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.
This commit is contained in:
@@ -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
|
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
|
error: `"abs"` after `extern` not yet accepted). `xfail`; 1.2b greens it. (Also
|
||||||
recovered a formatter-clobbered `parser.zig` — see Known issues.)
|
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
|
## Known issues
|
||||||
- **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the
|
- **Workflow hazard (1.2):** an editor format-on-save (or `zig fmt`) clobbered the
|
||||||
|
|||||||
@@ -2118,29 +2118,34 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
|||||||
// call-convention mismatch.
|
// call-convention mismatch.
|
||||||
const cc: Function.CallingConvention = if (fd.call_conv == .c or is_foreign or is_extern_decl) .c else .default;
|
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
|
// Symbol-name override: `#foreign … "csym"` (foreign_expr.c_name) or the new
|
||||||
if (is_foreign) {
|
// `extern … "csym"` (fd.extern_name). Declare under the C name and map the sx
|
||||||
const fe = fd.body.data.foreign_expr;
|
// name → C name so call sites resolve to the real symbol.
|
||||||
if (fe.c_name) |c_name| {
|
const rename_c_name: ?[]const u8 = if (is_foreign)
|
||||||
const c_name_id = self.module.types.internString(c_name);
|
fd.body.data.foreign_expr.c_name
|
||||||
if (self.dedupeForeignSymbol(fd, c_name_id, params.items, ret_ty)) {
|
else if (is_extern_decl)
|
||||||
self.foreign_name_map.put(name, c_name) catch {};
|
fd.extern_name
|
||||||
return;
|
else
|
||||||
}
|
null;
|
||||||
const fid = self.builder.declareExtern(c_name_id, params.items, ret_ty);
|
if (rename_c_name) |c_name| {
|
||||||
const func = self.module.getFunctionMut(fid);
|
const c_name_id = self.module.types.internString(c_name);
|
||||||
func.call_conv = cc;
|
if (self.dedupeForeignSymbol(fd, c_name_id, params.items, ret_ty)) {
|
||||||
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.foreign_name_map.put(name, c_name) catch {};
|
||||||
self.fn_decl_fids.put(fd, fid) catch {};
|
|
||||||
return;
|
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);
|
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 fid = self.builder.declareExtern(name_id, params.items, ret_ty);
|
||||||
const func = self.module.getFunctionMut(fid);
|
const func = self.module.getFunctionMut(fid);
|
||||||
func.call_conv = cc;
|
func.call_conv = cc;
|
||||||
|
|||||||
@@ -1952,6 +1952,24 @@ pub const Parser = struct {
|
|||||||
// Optional postfix linkage modifier: `extern` (import) / `export` (define).
|
// Optional postfix linkage modifier: `extern` (import) / `export` (define).
|
||||||
const extern_export = self.parseOptionalExternExport();
|
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.
|
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, #compiler, or #foreign marker.
|
||||||
// An `extern` import has NO body — just `;`. The extern_export modifier
|
// An `extern` import has NO body — just `;`. The extern_export modifier
|
||||||
// carries the linkage; we synthesize an empty block as the (non-optional)
|
// carries the linkage; we synthesize an empty block as the (non-optional)
|
||||||
@@ -2025,6 +2043,8 @@ pub const Parser = struct {
|
|||||||
.is_arrow = is_arrow,
|
.is_arrow = is_arrow,
|
||||||
.call_conv = call_conv,
|
.call_conv = call_conv,
|
||||||
.extern_export = extern_export,
|
.extern_export = extern_export,
|
||||||
|
.extern_lib = extern_lib,
|
||||||
|
.extern_name = extern_name,
|
||||||
.name_span = name_span,
|
.name_span = name_span,
|
||||||
.is_raw = name_is_raw,
|
.is_raw = name_is_raw,
|
||||||
} });
|
} });
|
||||||
|
|||||||
Reference in New Issue
Block a user