Module System
Organize code into modules with explicit imports and namespace control.
Module System
Sounio organizes code into modules. A file can declare itself as a module with an explicit module statement, or remain in the flat namespace if no declaration is present.
Declaring a module
// file: ir/profile.sio
module ir::profile
fn sprof_parse(data: &str) -> SprofProfile {
// ...
}
fn sprof_lookup(profile: &SprofProfile, name: &str) -> i64 {
// ...
}
The module ir::profile declaration places all definitions in this file under the ir::profile namespace.
Importing from modules
Use use to bring names into scope:
// Wildcard import — brings all public names into scope
use ir::profile::*
// The functions are now available directly
let profile = sprof_parse(data)
let count = sprof_lookup(profile, "main")
Selective imports are also supported:
use ir::profile::{sprof_parse, sprof_lookup}
Important: Wildcard imports (use module::*) are currently more reliable than selective imports for modules that have internal helper functions. Some internal helpers may fail to resolve with selective imports — prefer wildcard when in doubt.
Flat namespace files
Files without a module declaration are in the flat namespace. Their definitions are available directly to any file that can see them, without an explicit use statement:
// file: ir/normalize.sio
// (no module declaration)
fn normalize_instruction(instr: &!IrInstr) {
// available without import in other flat-namespace files
}
Files like normalize.sio, const_prop.sio, dce.sio, and ssa.sio in the self-hosted compiler use this pattern.
Module resolution rules
- A file with
module X::Ymust be imported withuse X::Y::*oruse X::Y::{name}. - A file without
moduleis in the flat namespace — its names are visible without imports. - Forward references are not supported: helpers must be defined before callers in the source file.
- Module paths map to file system paths:
module ir::profilecorresponds toir/profile.sio.
Combining modules and flat files
The self-hosted compiler mixes both patterns:
// main.sio — uses both module and flat-namespace imports
use ir::ir::* // module import: IrFunction, IrModule, etc.
use ir::profile::* // module import: SprofProfile, sprof_parse, etc.
use ir::inline::* // module import: inl_run_pass, etc.
// normalize.sio, const_prop.sio are flat — no import needed
Practical guidance
- Use
modulefor libraries and reusable components with clear interfaces. - Use flat namespace for tightly coupled implementation files that share many internal helpers.
- Always use
use parser::ast::*(wildcard) when importing AST types — selective imports can break operator comparisons on enum variants.