Compiler design / 14. Module artefacts and reuse
Immutable artefact lanes and fingerprints let one module serve every entry without entry-specific recompilation.
Later stages need a coherent object, not a loose mutable AST and a prayer. One shared module must serve every entry that needs it without recompiling once per page.
A successful module compilation produces an immutable CompiledModuleArtifact. Think of a sealed growth ring: later stages can graft onto it without cutting the trunk open again.
Think of four layers plus records of what changed:
Public semantic interface. Exported identities, types, values, access/effect facts, and stable lifetime summaries such as fresh roots, aliases, retained parameters, and outlives constraints.
Executable state. Module-local TypeEnvironment, validated HIR, borrow facts, and local lifetime-region/escape facts.
Link facts. Backend-neutral per-function edges, binding-backed call ids, helpers, capabilities, reactive features, numeric and cast operations, paths, assets, provenance and generated-function requests.
Compiler and builder metadata. Dormant root activity, folded fragment values and insertion indexes, entry metadata, documentation fragments, path usages, structured warnings.
Fingerprints sit beside the lanes so reuse can say what changed. Fingerprints include topology-relevant public summaries and link facts. Project-level topology is not stored as donor-local region IDs; exported lifetime summaries use stable semantic relationships.
Backend planning facts do not live in the public interface. Private bodies do not leak into consumer-visible identity.
For greeting.moth, the constant and function land in the interface when exported, in executable state as HIR and types, and in link facts as call and capability edges as needed.
Diagram placeholder: Interface, executable, link and metadata lanes sit beside fingerprints
Caption: accepted artefact shape simplified. Question: where does each fact live?
Accepted design records separate fingerprints, including:
Invalidation meaning follows the split:
Stable origin identities and export bindings keep re-exports honest. Renaming a re-export alias changes the exporting module's public fingerprint. It does not rewrite the origin declaration's identity.
Diagram placeholder: Semantic and implementation changes invalidate different lanes
Caption: conceptual matrix. Question: what can be reused on a second build?
Outcomes stay strict:
A diagnosed provider cannot expose "half of greet" for experimental linking. Partial linkable data is a bug farm.
Base module artefacts remain immutable. Concrete generic functions live in generated sidecars owned by the consuming project or package compilation.
Each sidecar carries its request identity, generated-local type context, concrete HIR, borrow facts, lifetime facts and summaries, link facts and fingerprints. A generated function may request further instances. The build system runs a worklist to a fixed point.
This avoids mutating the declaring module every time a consumer monomorphises a call. New concrete code grows beside the base. The base stays put.
Diagram placeholder: A concrete function sidecar points to its base module request
Caption: accepted sidecar relation. Question: how does generic reuse avoid mutation?
Canonical identity is assigned before workers race. Canonical merge order applies after workers finish. Semantic, implementation, root, link and documentation changes invalidate different dependents.
The second build of the greeting project should reuse an unchanged greeting artefact and only rebuild what fingerprints say moved.
Diagram placeholder: The second build reuses unchanged module artefacts and recompiles affected consumers
Caption: teaching schedule. Question: what does deterministic reuse mean?
Alternative: recompile each module for every entry, or let backends inspect mutable frontend state.
Choice: immutable canonical artefacts with explicit lanes and fingerprints.
Benefit: reuse and parallel scheduling preserve one semantic result.
Cost: invalidation and sidecar ownership need precise contracts.
CompiledModuleArtifact lanes and fingerprints.You leave with successful immutable module artefacts and fingerprints. Next: which compiled roots and functions form one entry when the builder finally chooses what runs.