# RESOLVED — 0130: `#library` declared behind two aliased imports is dropped — no `-l` flag, no JIT dlopen > **RESOLVED** (2026-06-12). Root cause as filed: `extractLibraries` > and `extractFrameworks` (src/main.zig) walked the merged root's > decls plus exactly ONE `namespace_decl` level, while aliased imports > nest namespaces arbitrarily deep — a `#library`/`#framework` two or > more aliases down never reached the AOT link args or the JIT dlopen > loop. Both walks are now recursive over `namespace_decl` children > (same `seen`-set dedup as before). Regression test: > `examples/1617-modules-library-nested-namespace.sx` (+ its module > dir) — `main → b :: #import → c :: #import` where 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 under `sx 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 repo `make test` 21/21 > at its HEAD plus a successful `make build` of 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` 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 declares `zlib :: #library "z"` → link line ends `-lc` (no `-lz`), `ld: symbol(s) not found: _zlibVersion`. - Expected: every `#library` reachable 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 `-lz` and links fine. A plain (unaliased) `#import` of 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`. ```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!); } ``` ```sx // b.sx — first namespace level #import "modules/std.sx"; c :: #import "c.sx"; ver_via_b :: () -> string { return c.zver(); } ``` ```sx // 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) walks `root.data.root.decls` and exactly ONE level of > `namespace_decl` children. Aliased imports lower to nested > `namespace_decl` nodes, so a `#library` (or, same pattern, > `#framework` — see `extractFrameworks` right 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 over > `namespace_decl` (a small explicit stack or recursive helper over > `ns.decls`), dedup as today via the `seen` set; apply the same to > `extractFrameworks`. Verify with the three-file libz repro from > issues/0130-library-decl-nested-namespace-dropped.md: `sx build` > must emit `-lz` and link, `sx run` must 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's `make test` (which now links SQLite through > dist.sx's ops→db→sqlite chain) to confirm the original failure is > gone.