feat: duplicate C exports across #import c units are a compile diagnostic
All units share one link namespace (per-unit isolation is PLAN-C C3.2, deferred), so a symbol defined by two units previously died inside the JIT dylib link or the AOT link with raw linker spew. The clang shim gains sx_clang_object_exported_symbols (llvm::object scan: defined + global, format-specific excluded) and compileCToObjects cross-checks every unit object — collisions name both source files. Scan failures are non-fatal; the linker remains the backstop. Covers JIT and native AOT; the emcc path still relies on wasm-ld's own error.
This commit is contained in:
@@ -419,6 +419,7 @@ pub fn compileCToObjects(
|
||||
target_config: @import("target.zig").TargetConfig,
|
||||
) ![]c.LLVMMemoryBufferRef {
|
||||
var obj_bufs = std.ArrayList(c.LLVMMemoryBufferRef).empty;
|
||||
var labels = std.ArrayList([]const u8).empty; // source path per buffer, for diagnostics
|
||||
|
||||
var ver_maj: c_uint = 0;
|
||||
var ver_min: c_uint = 0;
|
||||
@@ -503,6 +504,7 @@ pub fn compileCToObjects(
|
||||
cache_path = try std.fmt.allocPrintSentinel(allocator, ".sx-cache/c-{x:0>16}.o", .{key}, 0);
|
||||
if (loadCachedObject(cache_path.?)) |cached| {
|
||||
try obj_bufs.append(allocator, cached);
|
||||
try labels.append(allocator, src);
|
||||
continue;
|
||||
}
|
||||
} else |_| {}
|
||||
@@ -527,6 +529,38 @@ pub fn compileCToObjects(
|
||||
|
||||
if (cache_path) |cp| saveCachedObject(allocator, obj_buf, io, cp);
|
||||
try obj_bufs.append(allocator, obj_buf);
|
||||
try labels.append(allocator, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-object duplicate exports are diagnosed HERE, before they
|
||||
// surface as an opaque dylib/binary link failure: every `#import c`
|
||||
// unit shares one link namespace (per-unit symbol isolation is
|
||||
// PLAN-C C3.2, deferred). Scan failures are non-fatal — the linker
|
||||
// remains the backstop.
|
||||
var sym_owner = std.StringHashMap(usize).init(allocator);
|
||||
defer sym_owner.deinit();
|
||||
for (obj_bufs.items, 0..) |buf, i| {
|
||||
var err_msg: [*c]u8 = null;
|
||||
const list = c.sx_clang_object_exported_symbols(buf, &err_msg);
|
||||
if (list == null) {
|
||||
if (err_msg != null) c.LLVMDisposeMessage(err_msg);
|
||||
continue;
|
||||
}
|
||||
defer c.sx_clang_free_symbol_list(list);
|
||||
const n: usize = @intCast(list.*.num_names);
|
||||
for (0..n) |j| {
|
||||
const nm = std.mem.span(list.*.names[j]);
|
||||
const gop = try sym_owner.getOrPut(try allocator.dupe(u8, nm));
|
||||
if (gop.found_existing) {
|
||||
if (gop.value_ptr.* != i) {
|
||||
const disp = if (nm.len > 1 and nm[0] == '_') nm[1..] else nm;
|
||||
std.debug.print("error: C symbol '{s}' is defined by multiple '#import c' sources: '{s}' and '{s}' — all units share one link namespace\n", .{ disp, labels.items[gop.value_ptr.*], labels.items[i] });
|
||||
return error.CompileError;
|
||||
}
|
||||
} else {
|
||||
gop.value_ptr.* = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user