Expand description
L4 ModuleGraph — typed IR for a NixOS/nix-darwin/home-manager module system, compiled into worker/wrapper-split assignment closures with slice-keyed re-firing.
§Why this exists
Tvix has not staked out the module-system; cppnix re-runs the
entire fixed point on every rebuild. The single biggest sui-vs-nix
win on the rio sweep — 24 s on nixosConfigurations.rio.config. system.build.toplevel — lives here.
§Pipeline shape
AstGraph for each module .nix file (or .tlisp once dialect lands)
→ ModuleNode (declared options + config setter + import edges)
→ ModuleGraph (typed IR with dense ids + slice-keyed setters)
→ graph-hash cache key
→ compiled closure (defunctionalized setters, topo order)
→ fixed-point execution (Rayon per SCC, slice-keyed re-fire)
→ final config attrset
→ derivation graphThis module ships the typed IR + builder skeleton. The compilation pipeline (worker/wrapper synthesis, defunctionalization, NbE execution) lands in subsequent commits — the IR is its anchor.
§Invariants
- Module ids are dense u32s, root module is always 0.
- Every
ImportEdge::targetpoints at a valid module id. ConfigSetter::slicelists every option path the setter reads. Empty slice = setter writes but doesn’t read config (a “leaf” setter; rebuilds only when its own source hash changes).canonical_hashis the BLAKE3 of the rkyv archive bytes. Same module sources + same slices → same hash → cached compiled closure hits.
Structs§
- Archived
Config Setter - An archived
ConfigSetter - Archived
EnvPrefix Binding - An archived
EnvPrefixBinding - Archived
Import Edge - An archived
ImportEdge - Archived
Module Graph - An archived
ModuleGraph - Archived
Module Graph Hash - An archived
ModuleGraphHash - Archived
Module Node - An archived
ModuleNode - Archived
Option Decl - An archived
OptionDecl - Config
Setter - One config-setter fragment. The worker/wrapper-split shape is
captured in the types:
body_astis the worker (computes the contribution);sliceis the wrapper’s declared input projection (what the worker reads fromconfig). - Config
Setter Resolver - The resolver for an archived
ConfigSetter - EnvPrefix
Binding - One env-prefix binding captured from a module’s outer wrapping.
See
ModuleNode::body_env_prefix. - EnvPrefix
Binding Resolver - The resolver for an archived
EnvPrefixBinding - Import
Edge - One
imports = [ ... ];edge. - Import
Edge Resolver - The resolver for an archived
ImportEdge - Module
Graph - The compiled module graph.
- Module
Graph Fixture - Module
Graph Hash - 32-byte BLAKE3 hash. Same shape as the lockfile / AST graph hashes; kept separate so the type system distinguishes which graph kind a hash refers to.
- Module
Graph Hash Resolver - The resolver for an archived
ModuleGraphHash - Module
Graph Resolver - The resolver for an archived
ModuleGraph - Module
Node - One module: the typed projection of one
.nix(or future.tlisp) file’s contribution to the system. - Module
Node Resolver - The resolver for an archived
ModuleNode - Option
Decl - One
options.foo.bar = mkOption { ... }declaration. - Option
Decl Resolver - The resolver for an archived
OptionDecl
Enums§
- Archived
EnvPrefix Kind - An archived
EnvPrefixKind - EnvPrefix
Kind - Distinguishes let-bindings (just bind name=value) from with-clauses (unpack the value’s attrset attrs as top-level names in the env).
- EnvPrefix
Kind Resolver - The resolver for an archived
EnvPrefixKind - Module
Graph Error - Errors from the module-graph builder.
Constants§
- CANONICAL_
MODULE_ GRAPH_ FIXTURES_ LISP - SCHEMA_
VERSION - Bumped on every breaking change to the IR. Today: 1.
Functions§
- load_
fixtures - Load every authored fixture.
Type Aliases§
- Module
Id - Dense module identifier. Root module is always 0.
- Setter
Id - Dense setter identifier within a module. A module typically has
exactly one setter (the function body), but
mkMergeandmkIfconstructs can split a logical setter into multiple typed fragments — the IR keeps them separate so each can be fired independently.