Rewrote 20 issue writeups to the extern/runtime-class vocabulary (#foreign→extern, foreign_class_map→runtime_class_map, parseForeignClassDecl→parseRuntimeClassDecl, findForeignMethodInChain→findRuntimeMethodInChain, dedupeForeignSymbol→ dedupeExternSymbol, is_foreign_c_api→is_extern_c_api, stale filename refs to the renamed examples, foreign-class→runtime-class, bare foreign→extern). Renamed issues/0043-…-foreign-class-…→…-runtime-class-…. PHASE 9 COMPLETE — 9.4 GATE PASSES: zero 'foreign' across src/library/examples/ issues/docs/editors/specs/readme/CLAUDE, excluding only the SQLite API constant SQLITE_CONSTRAINT_FOREIGNKEY + vendored sqlite3.c/.h (upstream third-party). Suite green (644 corpus / 443 unit, 0 failed).
4.7 KiB
RESOLVED — 0130: #library declared behind two aliased imports is dropped — no -l flag, no JIT dlopen
RESOLVED (2026-06-12). Root cause as filed:
extractLibrariesandextractFrameworks(src/main.zig) walked the merged root's decls plus exactly ONEnamespace_decllevel, while aliased imports nest namespaces arbitrarily deep — a#library/#frameworktwo or more aliases down never reached the AOT link args or the JIT dlopen loop. Both walks are now recursive overnamespace_declchildren (sameseen-set dedup as before). Regression test:examples/1617-modules-library-nested-namespace.sx(+ its module dir) —main → b :: #import → c :: #importwhere c.sx declares#library "pcap"; libpcap is NOT in the compiler process's loaded images (unlike libz/libbz2/libsqlite3, which CoreServices/LLVM pull in and which mask the bug undersx run), so the pre-fix JIT fails symbol materialization and the pre-fix AOT link dies with undefined_pcap_lib_version. Gates: zig build test 426/426, tests/run_examples.sh 605/605, distribution repomake test21/21 at its HEAD plus a successfulmake buildof its P5.2 branch state (dist.sx → ops → db → sqlite, the original failing chain).
Symptom
A #library declaration in a module that is reached through TWO (or
more) levels of aliased #import never makes it into the build's
library list: sx build emits no -l<name> on the link line (link
fails with Undefined symbols for every extern fn of that
library), and sx run skips the dlopen of that library (the JIT then
resolves the extern symbol only if some already-loaded image happens
to export them).
- Observed:
main → b :: #import "b.sx" → c :: #import "c.sx"where c.sx declareszlib :: #library "z"→ link line ends-lc(no-lz),ld: symbol(s) not found: _zlibVersion. - Expected: every
#libraryreachable through the import graph is linked (AOT) / dlopened (JIT), regardless of import depth or aliasing. - Control: aliasing c.sx DIRECTLY from main (one namespace level)
produces
-lzand links fine. A plain (unaliased)#importof c.sx from a module that main aliases also works — the merged decls sit at one namespace level.
Reproduction
Three files in one directory; build a.sx.
// c.sx — declares the library + a extern fn
#import "modules/std.sx";
zlib :: #library "z";
zlibVersion :: () -> ?cstring extern zlib "zlibVersion";
zver :: () -> string {
p := zlibVersion();
if p == null { return ""; }
return from_cstring(p!);
}
// b.sx — first namespace level
#import "modules/std.sx";
c :: #import "c.sx";
ver_via_b :: () -> string { return c.zver(); }
// a.sx — main; c.sx's library now sits two namespace levels deep
#import "modules/std.sx";
b :: #import "b.sx";
main :: () -> i32 {
print("zlib {}\n", b.ver_via_b());
return 0;
}
$ sx build -o a a.sx
Undefined symbols for architecture arm64:
"_zlibVersion", referenced from: ...
error: linking failed
Replacing a.sx's import with c :: #import "c.sx" (and calling
c.zver() directly) links and prints the zlib version — same code,
one namespace level less.
Found in the distribution repo the first time a product chain nested
the SQLite bindings two aliases deep: dist.sx → ops :: #import "release/ops.sx" → db :: #import "../repo/db.sx" → #import "../db/sqlite.sx" loses -lsqlite3 even though the bindings compile
fine (the extern wrappers ARE in main.o; only the link flag is gone).
Investigation prompt
In /Users/agra/projects/sx:
extractLibraries(src/main.zig, ~line 877) walksroot.data.root.declsand exactly ONE level ofnamespace_declchildren. Aliased imports lower to nestednamespace_declnodes, so a#library(or, same pattern,#framework— seeextractFrameworksright below it, ~line 904) sitting two or more namespace levels deep is never collected. Both consumers are affected: the AOT link args (link(...)in src/target.zig receives this list) and the JIT dlopen loop (src/main.zig ~line 274). Fix: make the walk recursive overnamespace_decl(a small explicit stack or recursive helper overns.decls), dedup as today via theseenset; apply the same toextractFrameworks. Verify with the three-file libz repro from issues/0130-library-decl-nested-namespace-dropped.md:sx buildmust emit-lzand link,sx runmust dlopen libz; add an examples/ regression mirroring the repro (one library decl behind two aliased imports) that fails on pre-fix master. Then re-run the distribution repo'smake test(which now links SQLite through dist.sx's ops→db→sqlite chain) to confirm the original failure is gone.