fix(0128): foreign cstring returns + conflicting same-symbol bindings
Two genuine defects behind the 0128 filing (whose original repros were
both poisoned by binding getenv, which std already declares -> *u8):
1. Re-declaring a C symbol was silent first-wins: every call through
the later declaration was typed by the older signature. Foreign
registration now dedupes — equal signatures share one FuncId,
conflicting ones are diagnosed.
2. Foreign -> string / -> ?string returns read garbage: C returns one
char*, but the LLVM signature declared the fat {ptr,i64} (len =
register garbage), and ?string was mis-declared SRET (the hidden
out-pointer landed in the callee's first arg register). cstrRetKind
now classifies such returns, declares them as plain ptr (never
sret), and the call site synthesizes {ptr, strlen} via a
branch-guarded strlen (NULL -> {null,0} / optional null), wrapping
{string, i1} for ?string.
?[:0]u8 itself resolves fine (it is ?string); the spelling works in
return, param, local, and alias positions.
Regression: examples/1221 (plain + optional non-null + NULL paths) and
examples/1172 (conflict diagnostic); both FAIL pre-fix. The extern
dedupe collapses duplicate libc decls, so affected .ir snapshots were
regenerated. zig build test 426/426; run_examples 602/602;
distribution suite 21/21.
This commit is contained in:
@@ -2020,6 +2020,37 @@ fn returnGenericLeaf(node: *const Node) ?[]const u8 {
|
||||
}
|
||||
|
||||
/// Declare a function as an extern stub (signature only, no body).
|
||||
/// The same C SYMBOL declared more than once (two modules binding the same
|
||||
/// libc function, or a rename colliding with an existing binding): an EQUAL
|
||||
/// signature shares the first registration; a CONFLICTING one is diagnosed —
|
||||
/// silently letting the first registration win mis-types every call through
|
||||
/// the later declaration (a `-> string` view of a symbol registered `-> *u8`
|
||||
/// reads the wrong shape; issue 0128). True = handled (shared or diagnosed),
|
||||
/// caller must not declare again.
|
||||
pub fn dedupeForeignSymbol(self: *Lowering, fd: *const ast.FnDecl, sym_name: StringId, params: []const Function.Param, ret_ty: TypeId) bool {
|
||||
for (self.module.functions.items, 0..) |*func, i| {
|
||||
if (func.name != sym_name or !func.is_extern) continue;
|
||||
var same = func.ret == ret_ty and func.params.len == params.len;
|
||||
if (same) {
|
||||
for (func.params, params) |a, b| {
|
||||
if (a.ty != b.ty) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (same) {
|
||||
self.fn_decl_fids.put(fd, FuncId.fromIndex(@intCast(i))) catch {};
|
||||
return true;
|
||||
}
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, fd.body.span, "foreign symbol '{s}' is already bound with a different signature; two views of one C symbol must declare identical types", .{self.module.types.getString(sym_name)});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8) void {
|
||||
// Skip generic templates — they're monomorphized on demand, not declared as extern
|
||||
if (fd.type_params.len > 0) return;
|
||||
@@ -2083,6 +2114,10 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
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;
|
||||
@@ -2096,6 +2131,7 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
}
|
||||
|
||||
const name_id = self.module.types.internString(name);
|
||||
if (is_foreign 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;
|
||||
|
||||
Reference in New Issue
Block a user