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 &.{};
|
if (root.data != .root) return &.{};
|
||||||
|
|
||||||
var infos = std.ArrayList(CImportInfo).empty;
|
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();
|
defer seen.deinit();
|
||||||
|
|
||||||
// Aliased imports lower to namespace_decl nodes and NEST when a
|
// 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
|
// unit two aliases deep is silently never compiled and its symbols
|
||||||
// resolve from whatever process image carries the same names (the
|
// resolve from whatever process image carries the same names (the
|
||||||
// extractLibraries depth bug, issue 0130, in c_import form).
|
// 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 {
|
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(
|
fn walk(
|
||||||
infos_: *std.ArrayList(CImportInfo),
|
infos_: *std.ArrayList(CImportInfo),
|
||||||
seen_: *std.AutoHashMap([*]const []const u8, void),
|
seen_: *std.StringHashMap(void),
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
decls: []const *Node,
|
decls: []const *Node,
|
||||||
) !void {
|
) !void {
|
||||||
@@ -643,7 +680,7 @@ pub fn collectCImportSources(allocator: std.mem.Allocator, root: *const Node) ![
|
|||||||
switch (d.data) {
|
switch (d.data) {
|
||||||
.c_import_decl => |ci| {
|
.c_import_decl => |ci| {
|
||||||
if (ci.sources.len > 0) {
|
if (ci.sources.len > 0) {
|
||||||
const key = ci.sources.ptr;
|
const key = try contentKey(alloc, ci);
|
||||||
if (!seen_.contains(key)) {
|
if (!seen_.contains(key)) {
|
||||||
try seen_.put(key, {});
|
try seen_.put(key, {});
|
||||||
try infos_.append(alloc, .{
|
try infos_.append(alloc, .{
|
||||||
|
|||||||
Reference in New Issue
Block a user