Skip to main content

Crate salsa

Crate salsa 

Source
Expand description

A framework for writing incremental, on-demand computations.

§Choosing a Salsa construct

  • input structs hold mutable state supplied from outside Salsa.
  • tracked structs represent derived entities owned by one query invocation.
  • interned structs canonicalize immutable values for structural equality and sharing.
  • tracked functions are memoized computations. They record the queries and fields read by their body and act as incremental invalidation boundaries.
  • accumulator values are auxiliary outputs, such as diagnostics, that do not participate in a tracked function’s main result.
  • Supertype enums let one tracked function accept several different Salsa struct types as its key.

§Salsa structs

Input, tracked, and interned structs are the three kinds of Salsa struct. All three are compact, Copy handles whose fields live in the database, but they intentionally use different notions of identity, equality, and ownership:

KindHow identity is assignedLifecycle
InputEach constructor call creates a distinct identityLives until the database is dropped
TrackedProducing query, identity fields, and occurrenceOwned by the producing query
InternedAll field values; equal fields share an identityShared; low-durability values may be reclaimed

The generated Eq and Hash implementations compare the compact ID for every kind of Salsa struct: two handles are equal exactly when they have the same Salsa identity. What differs is how Salsa assigns and preserves that identity.

§Input structs

An input struct represents mutable state supplied from outside the incremental computation, such as the contents of a file. Inputs are the roots from which tracked computations read.

§Identity

Every constructor call creates a distinct input identity. Two inputs with equal field values are therefore unequal, while updating a field preserves the input’s identity.

Dependencies are recorded per field. If a query reads only file.text(db), changing another field does not invalidate that query. A setter always records its field as changed; Salsa does not compare the old and new values first. Each field also has a Durability. If a revision changes only lower-durability inputs, Salsa can skip validating queries that depend exclusively on higher-durability inputs.

§Lifecycle

An input handle has no 'db parameter and can be copied across revisions. It must still be used with the database in which it was created. The input’s data and memo entries keyed by it remain in that database until it is dropped; dropping every copy of the handle does not delete the input.

References returned by field getters are tied to an immutable database borrow. They cannot overlap the mutable borrow required to call a setter and begin a new revision.

See input structs in the Salsa book for examples of declaring, reading, and updating inputs, and the durability reference for choosing a durability.

§Tracked structs

A tracked struct represents a derived entity created while a tracked function executes. It is a good fit for intermediate values whose identity belongs to one computation rather than being shared structurally across the entire database.

§Identity

A tracked struct’s identity consists of its producing query invocation, the values of every field not marked #[tracked], and its occurrence among structs with the same identity created by that invocation. Equal-looking structs created by different queries, by different query keys, or twice by one invocation are distinct.

When the producing query re-executes, Salsa matches newly created structs with the previous execution. Recreating the same identities in the same order preserves their IDs.

A field marked #[tracked] is excluded from identity. When an entity is matched across executions, Salsa compares the old and new field values with PartialEq and replaces the stored value when they differ. Only queries that read a changed tracked field are invalidated; reading an identity field depends on the entity as a whole.

§Lifecycle

A tracked handle carries a 'db lifetime tied to an immutable database borrow, so it cannot be used across the mutable borrow that starts a new revision. The handle must be obtained again in a later revision even when Salsa preserves the entity’s identity.

Tracked structs are outputs owned by their producing query. Validating that query also validates its outputs. When it re-executes, any previous tracked struct that is not recreated becomes stale; Salsa reclaims it and may reuse its storage.

See tracked structs in the Salsa book for examples of tracked entities in an incremental IR.

§Interned structs

An interned struct canonicalizes an immutable set of field values. Interning is useful when every occurrence of equal field values should share one database-wide identity, regardless of where or how often those values are created. Comparing the resulting handles is then a cheap ID comparison.

§Identity

The complete set of fields determines an interned struct’s identity. Interning the same field values again in the same revision returns the same handle and shares the stored data, regardless of which query performs the interning. Once interned, comparing two values is a cheap ID comparison.

This database-wide sharing requires coordination through the interner. Prefer a tracked struct when the value is a derived entity owned by one query and does not need structural sharing.

§Lifecycle

By default, an interned handle carries a 'db lifetime tied to an immutable database borrow and cannot be used across a new revision. Create or retrieve the value again to obtain a handle for the new revision.

Salsa may reclaim a low-durability interned value after it has not been used for the number of active revisions configured by the revisions option, which defaults to 3. Reclaiming reuses its slot with a new ID generation so dependencies on the old value are invalidated. Values with higher durability are not reclaimed; revisions = usize::MAX disables reclamation for the interned type.

See interned structs in the Salsa book for examples of canonicalizing names and other values.

§Return modes

Salsa struct field getters and tracked functions return references by default. The #[returns(MODE)] field attribute and returns(MODE) tracked-function option select another mode:

  • ref returns a reference to the stored field or memoized result. This is the default.
  • clone returns an owned clone.
  • copy returns an owned copy.
  • deref uses Deref and returns a reference to its Target.
  • as_ref uses SalsaAsRef.
  • as_deref uses SalsaAsDeref.

Owned results can outlive the database borrow if their types permit it. Borrowed results are tied to the immutable database borrow and cannot be used across a revision in which Salsa may replace or reclaim the stored value.

See returning references in the Salsa book for examples on fields and tracked functions.

§Tracked functions and memoized values

A tracked function is identified by the function and its non-database arguments. Those arguments select a memoized query but are not themselves dependencies: dependencies arise only when the body reads a Salsa field or calls another tracked function.

A function without non-database arguments has one query key and one memoized result in each database. A function with one non-database argument uses that Salsa struct’s ID directly as its query key. With multiple arguments, every call first interns the argument tuple to obtain a synthetic Salsa ID. This extra interning step lets Salsa use the tuple as a query key; the arguments’ Eq and Hash implementations determine whether two calls resolve to the same ID and memo.

If a query’s dependencies have not changed, Salsa reuses its memoized result. After re-execution, Salsa compares the old and new results with PartialEq. If they are equal, Salsa preserves the memo’s previous “changed at” revision. This optimization is called backdating; it prevents invalidation from propagating to dependents when the result has not changed. The no_eq option disables this comparison.

A memo stores one current result, not a history. By default, each key that remains in the database retains its result. Re-execution may update or replace the value; reclaiming the key or dropping the database removes it. The lru option additionally evicts least-recently-used results at the start of a new revision, but retains their memo entries so a later call can recompute the value.

The return mode controls whether callers receive an owned result or borrow it from the memo.

See tracked functions in the Salsa book for an introduction, the red-green algorithm for the full validation model, and cache tuning for controlling memo retention.

§Specifying results

The specify option supports queries with both an on-demand incremental implementation and a batch implementation. The tracked function defines how to compute one result on demand. A query that creates many tracked structs can instead compute their results together and call FUNCTION::specify(db, key, value) for each one, avoiding the per-key implementation when those results are later requested. It can also provide special results for built-in entities or model a value initialized after a tracked struct is created.

A specifiable function must take exactly one non-database argument, and that argument must be a tracked struct, not an input or interned struct. specify must be called during the same tracked query invocation that created the key. Salsa records the specified memo as an output of that creating query, so validating or re-executing the creator also validates or replaces the specified result. The specify and lru options cannot currently be combined.

See specifying query results in the Salsa book for an example.

§Accumulators

An accumulator is a side channel for auxiliary outputs such as diagnostics. Accumulated values are stored with a memoized query execution but do not participate in the query’s return value or result equality. Adding or removing one therefore does not by itself make the query’s main result change. Values can only be accumulated while a tracked function is executing; attempting to accumulate outside one panics.

Calling a tracked function’s generated accumulated method first brings the query up to date, then returns references to values emitted by that query and its transitive callees. The references are tied to the database borrow. If the query re-executes, its new accumulated values replace the previous set; if Salsa reuses the memo, the existing values remain available without rerunning the function body.

See accumulators in the Salsa book for a complete diagnostic-reporting example.

§Supertypes

A Supertype enum is a heterogeneous Salsa-struct key. It lets one tracked function operate on several input, tracked, or interned struct types. Salsa uses the wrapped struct’s ID directly as the query key, while its concrete Salsa struct type determines the enum variant. Without a supertype, the query must be duplicated for every concrete type or callers must convert every value into some other common Salsa struct.

The enum must be nonempty. Each variant must contain exactly one unnamed field wrapping a Salsa struct or another Supertype; nesting supertypes can build larger groups from smaller ones. A concrete Salsa struct must be reachable through only one variant, ensuring that Salsa can determine the variant unambiguously from the wrapped ID.

A supertype has no storage of its own, so its validity and lifecycle follow the wrapped value.

The Salsa book develops these constructs as part of a complete incremental program. Its chapter on the 'db database lifetime explains why tracked and interned values cannot cross revisions.

§Values retained across revisions

Salsa retains tracked and interned fields and memoized query results after the database borrow that produced them has ended. SalsaValue marks types whose database lifetime can safely be replaced with 'static for storage and restored when the value is accessed. The derive checks this through the value’s fields.

A 'static field type is accepted without a SalsaValue implementation. Custom field types that carry the database lifetime should normally derive SalsaValue. See its safety documentation before implementing it manually or exempting a field from the generated checks.

Modules§

prelude

Structs§

Backtrace
CancellationToken
A cancellation token that can be used to cancel a query computation for a specific local Database.
Cycle
The context that the cycle recovery function receives when a query cycle occurs.
DatabaseImpl
Default database implementation that you can use if you don’t require any custom user data.
DatabaseKeyIndex
An integer that uniquely identifies a particular query instance within the database. Used to track input and output dependencies between queries. Fully ordered and equatable but those orderings are arbitrary, and meant to be used only for inserting into maps and the like.
Durability
Describes how likely a value is to change—how “durable” it is.
Event
The Event struct identifies various notable things that can occur during salsa execution. Instances of this struct are given to salsa_event.
Id
The Id of a salsa struct in the database Table.
IngredientIndex
An ingredient index identifies a particular [Ingredient] in the database.
IngredientInfo
Information about instances of a particular Salsa ingredient.
PageInfo
Page occupancy information for a Salsa struct ingredient.
Revision
A unique identifier for the current version of the database.
Runtime
Storage
Concrete implementation of the Database trait with local state that can be used to drive computations.
StorageHandle
A handle to non-local database state.

Enums§

Cancelled
A panic payload indicating that execution of a salsa query was cancelled.
EventKind
An enum identifying the various kinds of events that can occur.

Traits§

Accumulator
Trait implemented on the struct that user annotated with #[salsa::accumulator]. The Self type is therefore the types to be accumulated.
Database
The trait implemented by all Salsa databases. You can create your own subtraits of this trait using the #[salsa::db](crate::db) procedural macro.
HashEqLike
A trait for types that hash and compare like O.
Lookup
The Lookup trait is a more flexible variant on std::borrow::Borrow and std::borrow::ToOwned.
SalsaAsDeref
Used to determine the return type and value for tracked fields and functions annotated with returns(as_deref).
SalsaAsRef
Used to determine the return type and value for tracked fields and functions annotated with returns(as_ref).
SalsaValue
A value Salsa can safely retain across revisions.
Setter
Setter for a field of an input.

Functions§

attach
Attach the database to the current thread and execute op. Panics if a different database has already been attached.
attach_allow_change
Attach the database to the current thread and execute op. Allows a different database than currently attached. The original database will be restored on return.
with_attached_database
Access the “attached” database. Returns None if no database is attached. Databases are attached with attach_database.

Attribute Macros§

accumulator
Defines a type whose values can be accumulated by tracked functions.
db
Defines a Salsa database struct or database trait.
input
Defines a mutable input to a Salsa database.
interned
Defines an interned struct.
tracked
Defines a tracked struct or function, or enables tracked methods in an impl block.

Derive Macros§

SalsaValue
Derives the unsafe salsa::SalsaValue trait for a struct or enum.
Supertype
Derives a heterogeneous query key from an enum of Salsa structs.