salsa/lib.rs
1//! A framework for writing incremental, on-demand computations.
2//!
3//! # Choosing a Salsa construct
4//!
5//! - [`input`] structs hold mutable state supplied from outside Salsa.
6//! - [`tracked`] structs represent derived entities owned by one query invocation.
7//! - [`interned`] structs canonicalize immutable values for structural equality and sharing.
8//! - [`tracked`] functions are memoized computations. They record the queries and fields read by
9//! their body and act as incremental invalidation boundaries.
10//! - [`accumulator`] values are auxiliary outputs, such as diagnostics, that do not participate in
11//! a tracked function's main result.
12//! - [`Supertype`] enums let one tracked function accept several different Salsa struct types as
13//! its key.
14//!
15//! # Salsa structs
16//!
17//! Input, tracked, and interned structs are the three kinds of Salsa struct. All three are compact,
18//! [`Copy`] handles whose fields live in the database, but they intentionally use different notions
19//! of identity, equality, and ownership:
20//!
21//! | Kind | How identity is assigned | Lifecycle |
22//! |------|--------------------------|-----------|
23//! | [Input](#input-structs) | Each constructor call creates a distinct identity | Lives until the database is dropped |
24//! | [Tracked](#tracked-structs) | Producing query, identity fields, and occurrence | Owned by the producing query |
25//! | [Interned](#interned-structs) | All field values; equal fields share an identity | Shared; low-durability values may be reclaimed |
26//!
27//! The generated [`Eq`] and [`Hash`] implementations compare the compact ID for every kind of Salsa
28//! struct: two handles are equal exactly when they have the same Salsa identity. What differs is how
29//! Salsa assigns and preserves that identity.
30//!
31//! ## Input structs
32//!
33//! An [`input`] struct represents mutable state supplied from outside the incremental computation,
34//! such as the contents of a file. Inputs are the roots from which tracked computations read.
35//!
36//! ### Identity
37//!
38//! Every constructor call creates a distinct input identity. Two inputs with equal field values are
39//! therefore unequal, while updating a field preserves the input's identity.
40//!
41//! Dependencies are recorded per field. If a query reads only `file.text(db)`, changing another
42//! field does not invalidate that query. A setter always records its field as changed; Salsa does
43//! not compare the old and new values first. Each field also has a [`Durability`]. If a revision
44//! changes only lower-durability inputs, Salsa can skip validating queries that depend exclusively
45//! on higher-durability inputs.
46//!
47//! ### Lifecycle
48//!
49//! An input handle has no `'db` parameter and can be copied across revisions. It must still be used
50//! with the database in which it was created. The input's data and memo entries keyed by it remain
51//! in that database until it is dropped; dropping every copy of the handle does not delete the
52//! input.
53//!
54//! References returned by field getters are tied to an immutable database borrow. They cannot
55//! overlap the mutable borrow required to call a setter and begin a new revision.
56//!
57//! See [input structs in the Salsa book] for examples of declaring, reading, and updating inputs,
58//! and the [durability reference] for choosing a durability.
59//!
60//! ## Tracked structs
61//!
62//! A [`tracked`] struct represents a derived entity created while a tracked function executes. It
63//! is a good fit for intermediate values whose identity belongs to one computation rather than
64//! being shared structurally across the entire database.
65//!
66//! ### Identity
67//!
68//! A tracked struct's identity consists of its producing query invocation, the values of every
69//! field not marked `#[tracked]`, and its occurrence among structs with the same identity created
70//! by that invocation. Equal-looking structs created by different queries, by different query
71//! keys, or twice by one invocation are distinct.
72//!
73//! When the producing query re-executes, Salsa matches newly created structs with the previous
74//! execution. Recreating the same identities in the same order preserves their IDs.
75//!
76//! A field marked `#[tracked]` is excluded from identity. When an entity is matched across
77//! executions, Salsa compares the old and new field values with [`PartialEq`] and replaces the
78//! stored value when they differ. Only queries that read a changed tracked field are invalidated;
79//! reading an identity field depends on the entity as a whole.
80//!
81//! ### Lifecycle
82//!
83//! A tracked handle carries a `'db` lifetime tied to an immutable database borrow, so it cannot be
84//! used across the mutable borrow that starts a new revision. The handle must be obtained again in
85//! a later revision even when Salsa preserves the entity's identity.
86//!
87//! Tracked structs are outputs owned by their producing query. Validating that query also validates
88//! its outputs. When it re-executes, any previous tracked struct that is not recreated becomes
89//! stale; Salsa reclaims it and may reuse its storage.
90//!
91//! See [tracked structs in the Salsa book] for examples of tracked entities in an incremental IR.
92//!
93//! ## Interned structs
94//!
95//! An [`interned`] struct canonicalizes an immutable set of field values. Interning is useful when
96//! every occurrence of equal field values should share one database-wide identity, regardless of
97//! where or how often those values are created. Comparing the resulting handles is then a cheap ID
98//! comparison.
99//!
100//! ### Identity
101//!
102//! The complete set of fields determines an interned struct's identity. Interning the same field
103//! values again in the same revision returns the same handle and shares the stored data, regardless
104//! of which query performs the interning. Once interned, comparing two values is a cheap ID
105//! comparison.
106//!
107//! This database-wide sharing requires coordination through the interner. Prefer a tracked struct
108//! when the value is a derived entity owned by one query and does not need structural sharing.
109//!
110//! ### Lifecycle
111//!
112//! By default, an interned handle carries a `'db` lifetime tied to an immutable database borrow and
113//! cannot be used across a new revision. Create or retrieve the value again to obtain a handle for
114//! the new revision.
115//!
116//! Salsa may reclaim a low-durability interned value after it has not been used for the number of
117//! active revisions configured by the `revisions` option, which defaults to `3`. Reclaiming reuses
118//! its slot with a new ID generation so dependencies on the old value are invalidated. Values with
119//! higher durability are not reclaimed; `revisions = usize::MAX` disables reclamation for the
120//! interned type.
121//!
122//! See [interned structs in the Salsa book] for examples of canonicalizing names and other values.
123//!
124//! # Return modes
125//!
126//! Salsa struct field getters and tracked functions return references by default. The
127//! `#[returns(MODE)]` field attribute and `returns(MODE)` tracked-function option select another
128//! mode:
129//!
130//! - `ref` returns a reference to the stored field or memoized result. This is the default.
131//! - `clone` returns an owned clone.
132//! - `copy` returns an owned copy.
133//! - `deref` uses [`Deref`] and returns a reference to its `Target`.
134//! - `as_ref` uses [`SalsaAsRef`].
135//! - `as_deref` uses [`SalsaAsDeref`].
136//!
137//! Owned results can outlive the database borrow if their types permit it. Borrowed results are
138//! tied to the immutable database borrow and cannot be used across a revision in which Salsa may
139//! replace or reclaim the stored value.
140//!
141//! See [returning references in the Salsa book] for examples on fields and tracked functions.
142//!
143//! # Tracked functions and memoized values
144//!
145//! A [`tracked`] function is identified by the function and its non-database arguments. Those
146//! arguments select a memoized query but are not themselves dependencies: dependencies arise only
147//! when the body reads a Salsa field or calls another tracked function.
148//!
149//! A function without non-database arguments has one query key and one memoized result in each
150//! database. A function with one non-database argument uses that Salsa struct's ID directly as its
151//! query key. With multiple arguments, every call first interns the argument tuple to obtain a
152//! synthetic Salsa ID. This extra interning step lets Salsa use the tuple as a query key; the
153//! arguments' [`Eq`] and [`Hash`] implementations determine whether two calls resolve to the same
154//! ID and memo.
155//!
156//! If a query's dependencies have not changed, Salsa reuses its memoized result. After
157//! re-execution, Salsa compares the old and new results with [`PartialEq`]. If they are equal, Salsa
158//! preserves the memo's previous "changed at" revision. This optimization is called [backdating];
159//! it prevents invalidation from propagating to dependents when the result has not changed. The
160//! `no_eq` option disables this comparison.
161//!
162//! A memo stores one current result, not a history. By default, each key that remains in the
163//! database retains its result. Re-execution may update or replace the value; reclaiming the key or
164//! dropping the database removes it. The `lru` option additionally evicts least-recently-used
165//! results at the start of a new revision, but retains their memo entries so a later call can
166//! recompute the value.
167//!
168//! The [return mode](#return-modes) controls whether callers receive an owned result or borrow it
169//! from the memo.
170//!
171//! See [tracked functions in the Salsa book] for an introduction, the [red-green algorithm] for the
172//! full validation model, and [cache tuning] for controlling memo retention.
173//!
174//! ## Specifying results
175//!
176//! The `specify` option supports queries with both an on-demand incremental implementation and a
177//! batch implementation. The tracked function defines how to compute one result on demand. A
178//! query that creates many tracked structs can instead compute their results together and call
179//! `FUNCTION::specify(db, key, value)` for each one, avoiding the per-key implementation when those
180//! results are later requested. It can also provide special results for built-in entities or model
181//! a value initialized after a tracked struct is created.
182//!
183//! A specifiable function must take exactly one non-database argument, and that argument must be a
184//! tracked struct, not an input or interned struct. `specify` must be called during the same tracked
185//! query invocation that created the key. Salsa records the specified memo as an output of that
186//! creating query, so validating or re-executing the creator also validates or replaces the
187//! specified result. The `specify` and `lru` options cannot currently be combined.
188//!
189//! See [specifying query results in the Salsa book] for an example.
190//!
191//! # Accumulators
192//!
193//! An [`accumulator`] is a side channel for auxiliary outputs such as diagnostics. Accumulated
194//! values are stored with a memoized query execution but do not participate in the query's return
195//! value or result equality. Adding or removing one therefore does not by itself make the query's
196//! main result change. Values can only be accumulated while a tracked function is executing;
197//! attempting to accumulate outside one panics.
198//!
199//! Calling a tracked function's generated `accumulated` method first brings the query up to date,
200//! then returns references to values emitted by that query and its transitive callees. The
201//! references are tied to the database borrow. If the query re-executes, its new accumulated values
202//! replace the previous set; if Salsa reuses the memo, the existing values remain available without
203//! rerunning the function body.
204//!
205//! See [accumulators in the Salsa book] for a complete diagnostic-reporting example.
206//!
207//! # Supertypes
208//!
209//! A [`Supertype`] enum is a heterogeneous Salsa-struct key. It lets one tracked function operate
210//! on several input, tracked, or interned struct types. Salsa uses the wrapped struct's ID directly
211//! as the query key, while its concrete Salsa struct type determines the enum variant. Without a
212//! supertype, the query must be duplicated for every concrete type or callers must convert every
213//! value into some other common Salsa struct.
214//!
215//! The enum must be nonempty. Each variant must contain exactly one unnamed field wrapping a Salsa
216//! struct or another `Supertype`; nesting supertypes can build larger groups from smaller ones. A
217//! concrete Salsa struct must be reachable through only one variant, ensuring that Salsa can
218//! determine the variant unambiguously from the wrapped ID.
219//!
220//! A supertype has no storage of its own, so its validity and lifecycle follow the wrapped value.
221//!
222//! The [Salsa book](https://salsa-rs.github.io/salsa) develops these constructs as part of a
223//! complete incremental program. Its chapter on the [`'db` database lifetime] explains why tracked
224//! and interned values cannot cross revisions.
225//!
226//! # Values retained across revisions
227//!
228//! Salsa retains tracked and interned fields and memoized query results after the database borrow
229//! that produced them has ended. [`SalsaValue`] marks types whose database lifetime can safely be
230//! replaced with `'static` for storage and restored when the value is accessed. The derive checks
231//! this through the value's fields.
232//!
233//! A `'static` field type is accepted without a `SalsaValue` implementation. Custom field types
234//! that carry the database lifetime should normally derive `SalsaValue`. See its safety
235//! documentation before implementing it manually or exempting a field from the generated checks.
236//!
237//! [`Deref`]: std::ops::Deref
238//! [`Hash`]: std::hash::Hash
239//! [`'db` database lifetime]: https://salsa-rs.github.io/salsa/plumbing/db_lifetime.html
240//! [accumulators in the Salsa book]: https://salsa-rs.github.io/salsa/tutorial/accumulators.html
241//! [backdating]: https://salsa-rs.github.io/salsa/reference/algorithm.html#backdating-sometimes-we-can-be-smarter
242//! [cache tuning]: https://salsa-rs.github.io/salsa/tuning.html#cache-eviction-lru
243//! [durability reference]: https://salsa-rs.github.io/salsa/reference/durability.html
244//! [input structs in the Salsa book]: https://salsa-rs.github.io/salsa/overview.html#inputs
245//! [interned structs in the Salsa book]: https://salsa-rs.github.io/salsa/overview.html#interned-structs
246//! [red-green algorithm]: https://salsa-rs.github.io/salsa/reference/algorithm.html
247//! [returning references in the Salsa book]: https://salsa-rs.github.io/salsa/tutorial/parser.html#the-returnscopy-annotation
248//! [specifying query results in the Salsa book]: https://salsa-rs.github.io/salsa/overview.html#specify-the-result-of-tracked-functions-for-particular-structs
249//! [tracked functions in the Salsa book]: https://salsa-rs.github.io/salsa/overview.html#tracked-functions
250//! [tracked structs in the Salsa book]: https://salsa-rs.github.io/salsa/overview.html#tracked-structs
251
252#![deny(clippy::undocumented_unsafe_blocks)]
253#![forbid(unsafe_op_in_unsafe_fn)]
254
255#[cfg(feature = "accumulator")]
256mod accumulator;
257mod active_query;
258mod attach;
259mod cancelled;
260mod cycle;
261mod database;
262mod database_impl;
263mod durability;
264mod event;
265mod function;
266mod hash;
267mod id;
268mod ingredient;
269mod ingredient_cache;
270mod input;
271mod interned;
272mod key;
273mod memo_ingredient_indices;
274mod return_mode;
275mod revision;
276mod runtime;
277mod salsa_struct;
278mod salsa_value;
279mod storage;
280mod sync;
281mod table;
282mod tracing;
283mod tracked_struct;
284mod views;
285mod zalsa;
286mod zalsa_local;
287
288#[cfg(not(feature = "inventory"))]
289mod nonce;
290
291#[cfg(feature = "macros")]
292pub use salsa_macros::{SalsaValue, Supertype, accumulator, db, input, interned, tracked};
293
294#[cfg(feature = "salsa_unstable")]
295pub use self::database::{IngredientInfo, PageInfo};
296
297#[cfg(feature = "accumulator")]
298pub use self::accumulator::Accumulator;
299pub use self::active_query::Backtrace;
300pub use self::cancelled::Cancelled;
301
302pub use self::cycle::Cycle;
303pub use self::database::Database;
304pub use self::database_impl::DatabaseImpl;
305pub use self::durability::Durability;
306pub use self::event::{Event, EventKind};
307pub use self::id::Id;
308pub use self::input::setter::Setter;
309pub use self::key::DatabaseKeyIndex;
310pub use self::return_mode::SalsaAsDeref;
311pub use self::return_mode::SalsaAsRef;
312pub use self::revision::Revision;
313pub use self::runtime::Runtime;
314pub use self::salsa_value::SalsaValue;
315pub use self::storage::{Storage, StorageHandle};
316pub use self::zalsa::IngredientIndex;
317pub use self::zalsa_local::CancellationToken;
318pub use crate::attach::{attach, attach_allow_change, with_attached_database};
319pub use crate::interned::{HashEqLike, Lookup};
320
321pub mod prelude {
322 #[cfg(feature = "accumulator")]
323 pub use crate::accumulator::Accumulator;
324 pub use crate::{Database, Setter};
325}
326
327/// Internal names used by salsa macros.
328///
329/// # WARNING
330///
331/// The contents of this module are NOT subject to semver.
332#[doc(hidden)]
333pub mod plumbing {
334 pub use std::any::TypeId;
335 pub use std::option::Option::{self, None, Some};
336 pub use typeid::ConstTypeId;
337
338 #[cfg(feature = "accumulator")]
339 pub use salsa_macro_rules::setup_accumulator_impl;
340 pub use salsa_macro_rules::{
341 gate_accumulated, macro_if, maybe_default, maybe_default_tt, return_mode_expression,
342 return_mode_ty, setup_input_struct, setup_interned_struct, setup_tracked_assoc_fn_body,
343 setup_tracked_fn, setup_tracked_method_body, setup_tracked_struct,
344 unexpected_cycle_initial, unexpected_cycle_recovery,
345 };
346
347 pub use crate::SalsaValue;
348 #[cfg(feature = "accumulator")]
349 pub use crate::accumulator::Accumulator;
350 pub use crate::attach::{attach, with_attached_database};
351 pub use crate::cycle::CycleRecoveryStrategy;
352 pub use crate::database::{Database, current_revision};
353 pub use crate::durability::Durability;
354 pub use crate::id::{AsId, FromId, FromIdWithDb, Id};
355 pub use crate::ingredient::{Ingredient, Jar, Location};
356 pub use crate::ingredient_cache::IngredientCache;
357 pub use crate::interned::{HashEqLike, Lookup};
358 pub use crate::key::DatabaseKeyIndex;
359 pub use crate::memo_ingredient_indices::{
360 IngredientIndices, MemoIngredientIndices, MemoIngredientMap, MemoIngredientSingletonIndex,
361 NewMemoIngredientIndices,
362 };
363 pub use crate::revision::{AtomicRevision, Revision};
364 pub use crate::runtime::{Runtime, Stamp, stamp};
365 pub use crate::salsa_struct::{SalsaStructInDb, assert_supertype_no_overlap};
366 pub use crate::salsa_value::helper::{
367 Dispatch as SalsaValueDispatch, Fallback as SalsaValueFallback, assert_salsa_value,
368 };
369 pub use crate::storage::{HasStorage, Storage};
370 pub use crate::table::memo::MemoTableWithTypes;
371 pub use crate::tracked_struct::{TrackedStructInDb, update_field};
372 pub use crate::views::DatabaseDownCaster;
373 pub use crate::zalsa::{
374 ErasedJar, HasJar, IngredientIndex, JarKind, Zalsa, ZalsaDatabase, register_jar,
375 transmute_data_ptr, views,
376 };
377 pub use crate::zalsa_local::ZalsaLocal;
378
379 #[cfg(feature = "persistence")]
380 pub use serde;
381
382 // A stub for `serde` used when persistence is disabled.
383 //
384 // We provide dummy types to avoid detecting features during macro expansion.
385 #[cfg(not(feature = "persistence"))]
386 pub mod serde {
387 pub trait Serializer {
388 type Ok;
389 type Error;
390 }
391
392 pub trait Deserializer<'de> {
393 type Ok;
394 type Error;
395 }
396 }
397
398 #[cfg(feature = "accumulator")]
399 pub mod accumulator {
400 pub use crate::accumulator::{IngredientImpl, JarImpl};
401 }
402
403 pub mod input {
404 pub use crate::input::input_field::FieldIngredientImpl;
405 pub use crate::input::setter::SetterImpl;
406 pub use crate::input::singleton::{NotSingleton, Singleton};
407 pub use crate::input::{Configuration, HasBuilder, IngredientImpl, JarImpl, Value};
408 }
409
410 pub mod interned {
411 pub use crate::interned::{Configuration, IngredientImpl, JarImpl, Value};
412 }
413
414 pub mod function {
415 pub use crate::function::{Configuration, IngredientImpl, Memo};
416 pub use crate::function::{EvictionPolicy, HasCapacity, Lru, NoopEviction};
417 pub use crate::table::memo::MemoEntryType;
418 }
419
420 pub mod tracked_struct {
421 pub use crate::tracked_struct::tracked_field::FieldIngredientImpl;
422 pub use crate::tracked_struct::{Configuration, IngredientImpl, JarImpl, Value};
423 }
424}