v2026.3.22 — use jan: Multi-File Programs
v2026.3.22 — Multi-File Programs Ship
Section titled “v2026.3.22 — Multi-File Programs Ship”Released: March 22, 2026
Janus programs can now span multiple files. This is the G-01 deliverable — the import resolution path from spec to working binary in a single session.
The Feature
Section titled “The Feature”func add(a: i64, b: i64) -> i64 do return a + benduse math
func main() -> i64 do return math.add(21, 21)end$ janus build main.jan -o programImported 1 functions from Janus module 'math.jan'SPEC-041: Module 'math' CID=9415369dac44c07a...
$ ./program; echo $?42What’s New
Section titled “What’s New”Parser (Phase 1)
Section titled “Parser (Phase 1)”Three new AST node kinds — use_jan, import_list, import_alias — and full syntax support:
use math // basic importuse math.trig // dotted pathuse math { sin, cos } // selective importuse math { sin as trig_sin } // aliased selectiveuse crypto.hash as h // module aliasuse .sibling // relative import (single dot)use ..parent.child // relative import (double dot)19 parser tests verify all forms.
Content-Addressed Module Identity (Phase 2)
Section titled “Content-Addressed Module Identity (Phase 2)”Every module gets a BLAKE3 CID — a cryptographic hash of its canonical AST. Identity is content, not filesystem path. Moving a file doesn’t change its CID; changing a function does.
Module 'math' CID=9415369dac44c07a643863356137d5fe5e768d23291526d5ebe159e7ea46252cThe ModuleIndex tracks path aliases to CIDs. The Cid.computeModule() API is available for tooling.
4 golden tests verify determinism, path-independence, and import-independence.
Semantic Import Resolution (Phase 3)
Section titled “Semantic Import Resolution (Phase 3)”The collectModuleImports() sema pass resolves use_jan nodes to ASTDB compilation units. Module paths match against unit paths (math matches math.jan or src/math.jan). Unresolved imports are tracked for error reporting.
7 sema tests verify resolution and edge cases.
Multi-Unit Compilation (Phase 4+5)
Section titled “Multi-Unit Compilation (Phase 4+5)”The pipeline discovers imported files, parses them into the ASTDB, lowers ALL units into QTJIR, and emits a single LLVM module. Cross-module function calls resolve because the LLVM emitter pre-declares all symbols before emitting bodies.
Qualified calls (math.add()) work through an imported_modules set in the lowering context — the field-call path recognizes module names and skips UFCS receiver probing.
Cycle detection prevents infinite recursion on circular imports.
Bug Fixes
Section titled “Bug Fixes”- Parser single-identifier quirk —
use math(bare, no dots) now correctly emits an edge child. Branch 2 condition changed from negated (NOT dot, NOT brace) to positive (IS string_literal) for FFI form detection. - Monomorphized generic return types —
truncate[u8]now correctly returnsi8in LLVM instead ofvoid. AddedisJanusIntegerType()helper covering alli8-u64types. - Speculative stderr suppressed — UFCS receiver probing no longer emits “undefined variable” diagnostics when the receiver is a module name.
Test Results
Section titled “Test Results”1,771 tests passing. Green build.
| Suite | Tests | Coverage |
|---|---|---|
test-parser-trivia | 21 | All use_jan syntax forms |
test-module-cid | 4 | CID determinism, path-independence |
test-module-index | 6 | ModuleIndex CRUD operations |
test-sema-imports | 7 | Import resolution, missing modules |
test-import-e2e | 6 | End-to-end multi-file compilation |
test-conv-e2e | 9 | Generic type conversion (fixed) |
SPEC-041
Section titled “SPEC-041”The work follows SPEC-041: Content-Addressed Module Import Resolution, which defines:
- Three-layer resolution: path alias -> module index -> content store
- BLAKE3 CID computation via
canon.zigdeterministic encoder - Error codes E4100-E4106 for import validation
- Name mangling scheme for ecosystem-scale collision avoidance (future)
Known Limitations
Section titled “Known Limitations”- Snapshot unit-0 hardcoded — each imported module uses its own temporary ASTDB. True shared-ASTDB multi-unit requires a Snapshot refactor.
- Selective imports not enforced at lowering —
use math { add }parses correctly but lowering imports all functions. Enforcement is a Phase 3 follow-up. - No visibility enforcement — non-pub functions are currently importable. E4101/E4106 errors are specified but not yet emitted.
pub usere-exports — parser support deferred; requires top-level statement dispatcher changes.
“A module is not where it lives. A module is what it is.”