Skip to content

Database

Incremental inputs, Salsa queries, invalidation, and parity boundaries.

Updated View as Markdown

atoll-db is the memoized composition layer shared by the CLI and language server. It wraps compiler stages in Salsa queries; it does not define a second parser, checker, or backend.

Tracked inputs

The database tracks two kinds of input:

Input Examples
SourceFile Stable path, module path, mutable file contents
Project Root module, ordered files, dependencies, host allowlist, datasource routing

Changing file contents invalidates queries that read that file. Adding, removing, or reordering project files changes project enumeration and therefore invalidates queries that depend on the project shape. Configuration changes must behave like source changes when they affect resolution or lowering.

Long-lived consumers update a SourceFile through Salsa’s setter. The one-shot CLI driver materializes the same tracked inputs in a fresh database for each command, preserving pipeline parity without pretending that a process-local cache survives the command.

Query graph

The graph has both whole-project and fine-grained paths:

SourceFile ─→ parse_file ───────────────┐
                                        ├→ declaration surface
Project files ─→ parse_all_q ──────────┘

                     resolve and declarations

               per-function checks and assembly

                     project HIR / diagnostics

                specialization and MIR queries

                  unit slicing and unit emission

Results are generally shared behind Arc. A query cache hit clones a handle, not the complete AST or project.

Parse granularity

parse_file is memoized by SourceFile. Editing one file creates a new parsed value for that file; unchanged files retain their prior Arc<ParsedFile>. parse_all_q returns a new ordered outer collection when necessary while sharing the unchanged inner entries.

The language server can populate a separate incremental parse cache for an exact (path, contents) pair. The tracked query accepts that parse only when the contents match. Otherwise it performs a normal cold parse, preserving one semantic result for one source snapshot.

Declaration firewall

Function-body edits should not force project-wide import and declaration resolution when no declaration surface changed. The database computes a body-independent hash per file and combines those hashes into a project declaration snapshot.

On a body-only edit:

  1. the edited file reparses;
  2. its declaration-surface query reruns;
  3. the body-stripped hash remains equal;
  4. Salsa backdates the declaration result;
  5. project resolution remains a cache hit;
  6. affected body checks can rerun against the same frozen declaration base.

A signature, visibility, import, type declaration, or other surface edit changes the hash and correctly invalidates dependent resolution.

The hash is an invalidation firewall, not semantic authority. Parity tests must ensure it includes every source fact that can affect another body.

Per-function analysis

The database exposes per-function raw, checked, lowered, and assembly queries for editor and incremental consumers. Each function can be checked against a frozen declaration base into local arenas, then merged in canonical order.

Whole-project aggregation still has a role:

  • it produces the canonical project visible to existing compiler APIs;
  • it prevents repeated deep project cloning from turning a query fan-out into quadratic work;
  • it centralizes project-wide diagnostics and layouts;
  • it serves as an oracle for parity tests while the fine-grained path evolves.

Queries should be lazy. A top-level project request must not eagerly execute every per-function convenience query if the canonical aggregate can produce the result once.

Lower-stage boundaries

Incrementality is mixed, not uniform:

Boundary Current useful granularity
Parsing Per file
Declaration surface Per file, combined at project level
Body checking Per function with canonical assembly
Specialization Per-key query surfaces exist, but project mutation and reachable closure still create shared boundaries
MIR Symbol-subset builds exist; cross-function analyses and unit-wide transforms limit isolation
Emission Unit slicing and per-unit emission queries

A per-function query name therefore does not prove that no whole-project work occurred transitively. Performance claims must be measured from query execution and cache-hit data, not inferred from API shape.

Parity gates

Every incremental shortcut needs an oracle:

  • cold and incremental parses must agree;
  • assembled semantic output must agree with canonical analysis;
  • per-function worklist union must agree with the whole-project reachable set;
  • incremental unit emission must agree byte-for-byte with canonical emission;
  • diagnostic content and order must remain stable.

The database exposes parity validators for MIR aggregation, worklist union, and incremental emission. These are engineering tools as well as tests: when a new side table or implicit dependency is added, a parity failure identifies the missing query edge.

Query design rules

When adding a query:

  1. key it on the smallest stable semantic identity, not a transient pointer;
  2. read every tracked input that can change the result;
  3. return immutable shared snapshots;
  4. define equality or backdating only for facts truly irrelevant downstream;
  5. avoid ambient options that Salsa cannot observe;
  6. add a no-edit cache-hit test and an affected-edit invalidation test;
  7. compare output with the canonical non-incremental path.

The goal is not merely fewer recomputations. It is the same compiler result with a smaller dirty subgraph.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close