fix(0130): #library/#framework collection recurses into nested namespaces

extractLibraries/extractFrameworks walked the merged root plus exactly
one namespace_decl level, so a #library reached through two or more
aliased imports never made it to the AOT link line or the JIT dlopen
list. Both walks now recurse over namespace_decl children.

Regression: examples/1617-modules-library-nested-namespace.sx binds
libpcap (not in the compiler's loaded images, so the JIT cannot mask
the miss via RTLD_DEFAULT) behind two aliased imports.
This commit is contained in:
agra
2026-06-12 15:59:36 +03:00
parent 1d17b0abcf
commit d739c5bf11
8 changed files with 179 additions and 38 deletions

View File

@@ -877,54 +877,50 @@ fn hasTopLevelRun(root: *const sx.ast.Node) bool {
fn extractLibraries(allocator: std.mem.Allocator, root: *const sx.ast.Node) ![]const []const u8 {
var libs = std.ArrayList([]const u8).empty;
var seen = std.StringHashMap(void).init(allocator);
const addLib = struct {
fn f(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, name: []const u8) !void {
if (s.contains(name)) return;
try s.put(name, {});
try l.append(a, name);
}
}.f;
for (root.data.root.decls) |decl| {
switch (decl.data) {
.library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name),
.namespace_decl => |ns| {
for (ns.decls) |nd| {
switch (nd.data) {
.library_decl => |ld| try addLib(&libs, &seen, allocator, ld.lib_name),
else => {},
}
// Aliased imports lower to namespace_decl nodes and NEST when a
// namespaced module aliases its own imports, so the walk must recurse —
// a `#library` at any namespace depth belongs on the link line / in the
// JIT dlopen list.
const walker = struct {
fn walk(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, decls: []const *sx.ast.Node) !void {
for (decls) |d| {
switch (d.data) {
.library_decl => |ld| {
if (s.contains(ld.lib_name)) continue;
try s.put(ld.lib_name, {});
try l.append(a, ld.lib_name);
},
.namespace_decl => |ns| try walk(l, s, a, ns.decls),
else => {},
}
},
else => {},
}
}
}
};
try walker.walk(&libs, &seen, allocator, root.data.root.decls);
return try libs.toOwnedSlice(allocator);
}
fn extractFrameworks(allocator: std.mem.Allocator, root: *const sx.ast.Node) ![]const []const u8 {
var fws = std.ArrayList([]const u8).empty;
var seen = std.StringHashMap(void).init(allocator);
const addFw = struct {
fn f(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, name: []const u8) !void {
if (s.contains(name)) return;
try s.put(name, {});
try l.append(a, name);
}
}.f;
for (root.data.root.decls) |decl| {
switch (decl.data) {
.framework_decl => |fd| try addFw(&fws, &seen, allocator, fd.name),
.namespace_decl => |ns| {
for (ns.decls) |nd| {
switch (nd.data) {
.framework_decl => |fd| try addFw(&fws, &seen, allocator, fd.name),
else => {},
}
// Same nested-namespace recursion as extractLibraries: `#framework`
// declarations behind multiple aliased imports must still be linked.
const walker = struct {
fn walk(l: *std.ArrayList([]const u8), s: *std.StringHashMap(void), a: std.mem.Allocator, decls: []const *sx.ast.Node) !void {
for (decls) |d| {
switch (d.data) {
.framework_decl => |fd| {
if (s.contains(fd.name)) continue;
try s.put(fd.name, {});
try l.append(a, fd.name);
},
.namespace_decl => |ns| try walk(l, s, a, ns.decls),
else => {},
}
},
else => {},
}
}
}
};
try walker.walk(&fws, &seen, allocator, root.data.root.decls);
return try fws.toOwnedSlice(allocator);
}