fix(C4): dedup c-import units by normalized content, not node identity
One module imported through several aliased chains materializes one c_import_decl copy per chain, each carrying a differently-spelled relative path to the same file (src/app/../repo/../db/../../vendor/x.c vs src/db/../../vendor/x.c). Dedup now keys on lexically-normalized sources/includes + defines + flags, so the unit compiles and links exactly once — pointer-identity dedup linked it once per chain and died with duplicate symbols at AOT link.
This commit is contained in:
@@ -624,7 +624,7 @@ pub fn collectCImportSources(allocator: std.mem.Allocator, root: *const Node) ![
|
||||
if (root.data != .root) return &.{};
|
||||
|
||||
var infos = std.ArrayList(CImportInfo).empty;
|
||||
var seen = std.AutoHashMap([*]const []const u8, void).init(allocator);
|
||||
var seen = std.StringHashMap(void).init(allocator);
|
||||
defer seen.deinit();
|
||||
|
||||
// Aliased imports lower to namespace_decl nodes and NEST when a
|
||||
@@ -632,10 +632,47 @@ pub fn collectCImportSources(allocator: std.mem.Allocator, root: *const Node) ![
|
||||
// unit two aliases deep is silently never compiled and its symbols
|
||||
// resolve from whatever process image carries the same names (the
|
||||
// extractLibraries depth bug, issue 0130, in c_import form).
|
||||
//
|
||||
// Dedup is by CONTENT (sources + includes + defines + flags), not
|
||||
// node identity: one module imported through several aliased paths
|
||||
// materializes several copies of its c_import_decl, and collecting
|
||||
// each copy would compile (and link!) the same unit repeatedly —
|
||||
// duplicate-symbol death at AOT link time.
|
||||
const walker = struct {
|
||||
// Lexically normalized: the SAME file reached through different
|
||||
// import chains spells differently ("src/app/../repo/../db/../..
|
||||
// /vendor/x.c" vs "src/db/../../vendor/x.c") and must dedup.
|
||||
fn appendNormalized(key: *std.ArrayList(u8), alloc: std.mem.Allocator, path: []const u8) !void {
|
||||
const norm = std.fs.path.resolve(alloc, &.{path}) catch path;
|
||||
try key.appendSlice(alloc, norm);
|
||||
try key.append(alloc, 0);
|
||||
}
|
||||
|
||||
fn contentKey(alloc: std.mem.Allocator, ci: anytype) ![]const u8 {
|
||||
var key = std.ArrayList(u8).empty;
|
||||
for (ci.sources) |s| {
|
||||
try appendNormalized(&key, alloc, s);
|
||||
}
|
||||
try key.append(alloc, 1);
|
||||
for (ci.includes) |s| {
|
||||
try appendNormalized(&key, alloc, s);
|
||||
}
|
||||
try key.append(alloc, 1);
|
||||
for (ci.defines) |s| {
|
||||
try key.appendSlice(alloc, s);
|
||||
try key.append(alloc, 0);
|
||||
}
|
||||
try key.append(alloc, 1);
|
||||
for (ci.flags) |s| {
|
||||
try key.appendSlice(alloc, s);
|
||||
try key.append(alloc, 0);
|
||||
}
|
||||
return try key.toOwnedSlice(alloc);
|
||||
}
|
||||
|
||||
fn walk(
|
||||
infos_: *std.ArrayList(CImportInfo),
|
||||
seen_: *std.AutoHashMap([*]const []const u8, void),
|
||||
seen_: *std.StringHashMap(void),
|
||||
alloc: std.mem.Allocator,
|
||||
decls: []const *Node,
|
||||
) !void {
|
||||
@@ -643,7 +680,7 @@ pub fn collectCImportSources(allocator: std.mem.Allocator, root: *const Node) ![
|
||||
switch (d.data) {
|
||||
.c_import_decl => |ci| {
|
||||
if (ci.sources.len > 0) {
|
||||
const key = ci.sources.ptr;
|
||||
const key = try contentKey(alloc, ci);
|
||||
if (!seen_.contains(key)) {
|
||||
try seen_.put(key, {});
|
||||
try infos_.append(alloc, .{
|
||||
|
||||
Reference in New Issue
Block a user