From fca9d84260a531ec3063d801b9bfcb6304a83c0a Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Fri, 21 Nov 2025 20:03:23 +0200 Subject: [PATCH 1/7] fix: use unambiguous paths for Result This fixes a bug where code like this would break: type Result = std::result::Result; // or use std::io::Result; #[salsa::input] struct Example {} --- components/salsa-macro-rules/src/setup_input_struct.rs | 8 ++++---- components/salsa-macro-rules/src/setup_interned_struct.rs | 8 ++++---- components/salsa-macro-rules/src/setup_tracked_fn.rs | 8 ++++---- components/salsa-macro-rules/src/setup_tracked_struct.rs | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/components/salsa-macro-rules/src/setup_input_struct.rs b/components/salsa-macro-rules/src/setup_input_struct.rs index 741f9393e..ecc9fbdd1 100644 --- a/components/salsa-macro-rules/src/setup_input_struct.rs +++ b/components/salsa-macro-rules/src/setup_input_struct.rs @@ -123,7 +123,7 @@ macro_rules! setup_input_struct { fn serialize( fields: &Self::Fields, serializer: S, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $($serialize_fn(fields, serializer))? @@ -135,7 +135,7 @@ macro_rules! setup_input_struct { fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>( deserializer: D, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $($deserialize_fn(deserializer))? @@ -241,7 +241,7 @@ macro_rules! setup_input_struct { $zalsa::macro_if! { $persist => impl $zalsa::serde::Serialize for $Struct { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> ::std::result::Result where S: $zalsa::serde::Serializer, { @@ -250,7 +250,7 @@ macro_rules! setup_input_struct { } impl<'de> $zalsa::serde::Deserialize<'de> for $Struct { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> ::std::result::Result where D: $zalsa::serde::Deserializer<'de>, { diff --git a/components/salsa-macro-rules/src/setup_interned_struct.rs b/components/salsa-macro-rules/src/setup_interned_struct.rs index 1d27a33a2..b7a38f3b7 100644 --- a/components/salsa-macro-rules/src/setup_interned_struct.rs +++ b/components/salsa-macro-rules/src/setup_interned_struct.rs @@ -171,7 +171,7 @@ macro_rules! setup_interned_struct { fn serialize( fields: &Self::Fields<'_>, serializer: S, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $($serialize_fn(fields, serializer))? @@ -183,7 +183,7 @@ macro_rules! setup_interned_struct { fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>( deserializer: D, - ) -> Result, D::Error> { + ) -> ::std::result::Result, D::Error> { $zalsa::macro_if! { if $persist { $($deserialize_fn(deserializer))? @@ -269,7 +269,7 @@ macro_rules! setup_interned_struct { $zalsa::macro_if! { $persist => impl<$($db_lt_arg)?> $zalsa::serde::Serialize for $Struct<$($db_lt_arg)?> { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> ::std::result::Result where S: $zalsa::serde::Serializer, { @@ -278,7 +278,7 @@ macro_rules! setup_interned_struct { } impl<'de, $($db_lt_arg)?> $zalsa::serde::Deserialize<'de> for $Struct<$($db_lt_arg)?> { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> ::std::result::Result where D: $zalsa::serde::Deserializer<'de>, { diff --git a/components/salsa-macro-rules/src/setup_tracked_fn.rs b/components/salsa-macro-rules/src/setup_tracked_fn.rs index 9cb311fc5..57226888e 100644 --- a/components/salsa-macro-rules/src/setup_tracked_fn.rs +++ b/components/salsa-macro-rules/src/setup_tracked_fn.rs @@ -181,7 +181,7 @@ macro_rules! setup_tracked_fn { fn serialize( fields: &Self::Fields<'_>, serializer: S, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $zalsa::serde::Serialize::serialize(fields, serializer) @@ -193,7 +193,7 @@ macro_rules! setup_tracked_fn { fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>( deserializer: D, - ) -> Result, D::Error> { + ) -> ::std::result::Result, D::Error> { $zalsa::macro_if! { if $persist { $zalsa::serde::Deserialize::deserialize(deserializer) @@ -329,7 +329,7 @@ macro_rules! setup_tracked_fn { fn serialize( value: &Self::Output<'_>, serializer: S, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $zalsa::serde::Serialize::serialize(value, serializer) @@ -341,7 +341,7 @@ macro_rules! setup_tracked_fn { fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>( deserializer: D, - ) -> Result, D::Error> { + ) -> ::std::result::Result, D::Error> { $zalsa::macro_if! { if $persist { $zalsa::serde::Deserialize::deserialize(deserializer) diff --git a/components/salsa-macro-rules/src/setup_tracked_struct.rs b/components/salsa-macro-rules/src/setup_tracked_struct.rs index 92dc25974..1526dd7e7 100644 --- a/components/salsa-macro-rules/src/setup_tracked_struct.rs +++ b/components/salsa-macro-rules/src/setup_tracked_struct.rs @@ -209,7 +209,7 @@ macro_rules! setup_tracked_struct { fn serialize( fields: &Self::Fields<'_>, serializer: S, - ) -> Result { + ) -> ::std::result::Result { $zalsa::macro_if! { if $persist { $($serialize_fn(fields, serializer))? @@ -221,7 +221,7 @@ macro_rules! setup_tracked_struct { fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>( deserializer: D, - ) -> Result, D::Error> { + ) -> ::std::result::Result, D::Error> { $zalsa::macro_if! { if $persist { $($deserialize_fn(deserializer))? @@ -307,7 +307,7 @@ macro_rules! setup_tracked_struct { $zalsa::macro_if! { $persist => impl $zalsa::serde::Serialize for $Struct<'_> { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> ::std::result::Result where S: $zalsa::serde::Serializer, { @@ -316,7 +316,7 @@ macro_rules! setup_tracked_struct { } impl<'de> $zalsa::serde::Deserialize<'de> for $Struct<'_> { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> ::std::result::Result where D: $zalsa::serde::Deserializer<'de>, { From 77c02190a72547a9c9d48ea0c21356a47496472a Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Fri, 21 Nov 2025 20:03:23 +0200 Subject: [PATCH 2/7] docs: Partially document proc-macros --- book/src/overview.md | 11 +- components/salsa-macros/Cargo.toml | 4 + components/salsa-macros/src/db.rs | 7 - components/salsa-macros/src/lib.rs | 157 ++++++++++++++++++++++ components/salsa-macros/src/tracked_fn.rs | 7 - 5 files changed, 168 insertions(+), 18 deletions(-) diff --git a/book/src/overview.md b/book/src/overview.md index df468d2fa..0d049055f 100644 --- a/book/src/overview.md +++ b/book/src/overview.md @@ -39,7 +39,7 @@ The database is also used to implement interning (making a canonical version of ## Inputs -Every Salsa program begins with an **input**. +Every Salsa program begins with an **input**. See the [`#[input]` attribute macro's documentation](https://docs.rs/salsa/latest/salsa/attr.input.html) for options. Inputs are special structs that define the starting point of your program. Everything else in your program is ultimately a deterministic function of these inputs. @@ -123,7 +123,7 @@ This gives the ability to set the [durability](./reference/durability.md) and ot ## Tracked functions -Once you've defined your inputs, the next thing to define are **tracked functions**: +Once you've defined your inputs, the next thing to define are **tracked functions**. See the [`#[tracked]` attribute macro's documentation](https://docs.rs/salsa/latest/salsa/attr.tracked.html) for options. ```rust #[salsa::tracked] @@ -149,7 +149,7 @@ Tracked functions can return any clone-able type. A clone is required since, whe ## Tracked structs -**Tracked structs** are intermediate structs created during your computation. +**Tracked structs** are intermediate structs created during your computation. See the [`#[tracked]` attribute macro's documentation](https://docs.rs/salsa/latest/salsa/attr.tracked.html) for options. Like inputs, their fields are stored inside the database, and the struct itself just wraps an id. Unlike inputs, they can only be created inside a tracked function, and their fields can never change once they are created (until the next revision, at least). Getter methods are provided to read the fields, but there are no setter methods. @@ -180,6 +180,8 @@ fn parse_file(db: &dyn crate::Db, file: ProgramFile) -> Ast { ### `#[id]` fields + + When a tracked function is re-executed because its inputs have changed, the tracked structs it creates in the new execution are matched against those from the old execution, and the values of their fields are compared. If the field values have not changed, then other tracked functions that only read those fields will not be re-executed. @@ -246,6 +248,7 @@ Specifying is only possible for tracked functions that take a single tracked str The final kind of Salsa struct are **interned structs**. Interned structs are useful for quick equality comparison. They are commonly used to represent strings or other primitive values. + See the [`#[interned]` attribute macro's documentation](https://docs.rs/salsa/latest/salsa/attr.interned.html) for options. Most compilers, for example, will define a type to represent a user identifier: @@ -273,7 +276,7 @@ You can access the fields of an interned struct using a getter, like `word.text( ## Accumulators -The final Salsa concept are **accumulators**. Accumulators are a way to report errors or other "side channel" information that is separate from the main return value of your function. +The final Salsa concept are **accumulators**. Accumulators are a way to report errors or other "side channel" information that is separate from the main return value of your function. See the [`#[accumulator]` attribute macro's documentation](https://docs.rs/salsa/latest/salsa/attr.accumulator.html) for options. To create an accumulator, you declare a type as an _accumulator_: diff --git a/components/salsa-macros/Cargo.toml b/components/salsa-macros/Cargo.toml index a317bf498..bb7ab2728 100644 --- a/components/salsa-macros/Cargo.toml +++ b/components/salsa-macros/Cargo.toml @@ -17,6 +17,10 @@ quote = "1.0" syn = { version = "2.0.104", features = ["full", "visit-mut"] } synstructure = "0.13.2" +[dev-dependencies] +# For doc-tests +salsa.path = "../../" + [features] default = [] persistence = [] diff --git a/components/salsa-macros/src/db.rs b/components/salsa-macros/src/db.rs index 2c49604ad..ca444ce3f 100644 --- a/components/salsa-macros/src/db.rs +++ b/components/salsa-macros/src/db.rs @@ -4,13 +4,6 @@ use syn::parse::Nothing; use crate::hygiene::Hygiene; use crate::token_stream_with_error; -// Source: -// -// #[salsa::db] -// pub struct Database { -// storage: salsa::Storage, -// } - pub(crate) fn db( args: proc_macro::TokenStream, input: proc_macro::TokenStream, diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 1aea7da58..16675b4ae 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -55,11 +55,72 @@ pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream { accumulator::accumulator(args, input) } +/// Implements a custom database trait. +/// +/// Apply this on a custom database trait's definition and the `struct` and `impl` items of +/// implementors. +/// +/// When applied to `struct` items, this macro implements the necessary supertraits required for `salsa::Database`. +/// +/// When applied to `trait` and `impl` items, this macro adds some hidden trait methods required for [`#[tracked]`](fn@tracked) functions. +/// +/// # Example +/// +/// ``` +/// use std::path::PathBuf; +/// +/// #[salsa::input] +/// struct File { +// Doesn't work without the std::path:: prefix... +/// path: std::path::PathBuf, +/// #[returns(ref)] +/// contents: String, +/// } +/// +/// #[salsa::db] +/// trait Db: salsa::Database { +/// fn input(&self, path: PathBuf) -> std::io::Result; +/// } +/// +/// #[salsa::db] +/// #[derive(Clone)] +/// pub struct MyDatabase { +/// storage: salsa::Storage, +/// } +/// +/// #[salsa::db] +/// impl salsa::Database for MyDatabase {} +/// +/// #[salsa::db] +/// impl Db for MyDatabase { +/// fn input(&self, path: PathBuf) -> std::io::Result { +/// todo!() +/// } +/// } +/// ``` #[proc_macro_attribute] pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { db::db(args, input) } +/// Creates interned structs. +/// +/// **Container options:** +/// +/// - TODO +/// +/// **Field options:** +/// +/// - TODO +/// +/// # Example +/// +/// ``` +/// #[salsa::interned] +/// struct MyInterned<'db> { +/// field: String, +/// } +/// ``` #[proc_macro_attribute] pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream { interned::interned(args, input) @@ -70,11 +131,107 @@ pub fn supertype(input: TokenStream) -> TokenStream { supertype::supertype(input) } +/// Creates input structs. +/// +/// **Container options:** +/// +/// - TODO +/// +/// **Field options:** +/// +/// - `default`: Marks the field as tracked. +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) +/// - `get`: Name of the getter function (default: field name) +/// - `set`: Name of the setter function (default: `set_` + field name) +/// +/// # Example +/// +/// ``` +/// use std::path::PathBuf; +/// +/// #[salsa::input] +/// struct File { +// Doesn't work without the std::path:: prefix... +/// path: std::path::PathBuf, +/// #[returns(ref)] +/// contents: String, +/// } +/// ``` #[proc_macro_attribute] pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { input::input(args, input) } +/// Creates tracked structs, functions and `impl`s. +/// +/// # Tracked structs +/// +/// **Container options:** +/// +/// - `debug` +/// - `singleton` +/// - `data` +/// - `constructor_name` +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Fields) -> usize`, default: none) +/// - `persist(serialize = , deserialize = )` (Only with persistence feature) +/// * Type of `serialize`: `fn()` +/// * Type of `deserialize`: `fn()` +/// +/// **Field options:** +/// +/// - `tracked`: Marks the field as tracked. +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) +/// - `get`: Name of the getter function (default: field name) +/// - `maybe_update`: TODO +/// +/// # Tracked functions +/// +/// When you call a tracked function, Salsa will track which inputs it accesses and memoize the return value based on it. This data is saved in the database. When it's called again, the inputs are compared. If they're identical, the first. +/// +/// Tracked functions always take the database as the first argument and can take [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator) structs for the rest. +/// arguments. +/// +/// **Options:** +/// +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +/// - `specify`: Signal that the value can be externally specified (only works with a single Salsa struct as the input. incompatible with `lru`) +/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) +// Explicitly not documented: - `unsafe(non_update_return_type)` +/// - `cycle_fn = `: TODO +/// - `cycle_initial = `: TODO +/// - `cycle_result = `: TODO +/// - `lru = `: Set the LRU capacity (default: 0) +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) +/// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name +/// - `persist` (Only with persistence feature) +/// +/// # Tracked `impl`s +/// +/// TODO +/// +/// # Example +/// +/// ``` +/// #[salsa::tracked] +/// struct MyTracked<'db> { +/// value: u32, +/// #[returns(ref)] +/// links: Vec>, +/// } +/// +/// #[salsa::tracked] +/// fn sum<'db>(db: &'db dyn salsa::Database, input: MyTracked<'db>) -> u32 { +/// input.value(db) +/// + input +/// .links(db) +/// .iter() +/// .map(|&file| sum(db, file)) +/// .sum::() +/// } +/// +/// ``` #[proc_macro_attribute] pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream { tracked::tracked(args, input) diff --git a/components/salsa-macros/src/tracked_fn.rs b/components/salsa-macros/src/tracked_fn.rs index 12f9170c7..9ae30a005 100644 --- a/components/salsa-macros/src/tracked_fn.rs +++ b/components/salsa-macros/src/tracked_fn.rs @@ -7,13 +7,6 @@ use crate::hygiene::Hygiene; use crate::options::{AllowedOptions, AllowedPersistOptions, Options}; use crate::{db_lifetime, fn_util}; -// Source: -// -// #[salsa::db] -// pub struct Database { -// storage: salsa::Storage, -// } - pub(crate) fn tracked_fn(args: proc_macro::TokenStream, item: ItemFn) -> syn::Result { let hygiene = Hygiene::from2(&item); let args: FnArgs = syn::parse(args)?; From 81b2c855f3c579b889b72c075957110b42dfb344 Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Fri, 21 Nov 2025 23:15:40 +0200 Subject: [PATCH 3/7] docs: Document `debug` option of proc-macros --- components/salsa-macros/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 16675b4ae..4b41a8bec 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -107,6 +107,7 @@ pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { /// /// **Container options:** /// +/// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - TODO /// /// **Field options:** @@ -136,6 +137,7 @@ pub fn supertype(input: TokenStream) -> TokenStream { /// **Container options:** /// /// - TODO +/// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// /// **Field options:** /// @@ -169,7 +171,7 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// **Container options:** /// -/// - `debug` +/// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton` /// - `data` /// - `constructor_name` From 966078f42862de1bd53398c0423b3f66979df5d5 Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Sat, 22 Nov 2025 20:15:56 +0200 Subject: [PATCH 4/7] docs: document singleton option --- components/salsa-macros/src/lib.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 4b41a8bec..0fd5dbf81 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -108,6 +108,7 @@ pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { /// **Container options:** /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. +/// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. /// - TODO /// /// **Field options:** @@ -136,8 +137,9 @@ pub fn supertype(input: TokenStream) -> TokenStream { /// /// **Container options:** /// -/// - TODO /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. +/// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. +/// - TODO /// /// **Field options:** /// @@ -172,13 +174,13 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// **Container options:** /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. -/// - `singleton` -/// - `data` -/// - `constructor_name` +/// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. +/// - `data`: TODO +/// - `constructor_name`: TODO /// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Fields) -> usize`, default: none) /// - `persist(serialize = , deserialize = )` (Only with persistence feature) -/// * Type of `serialize`: `fn()` -/// * Type of `deserialize`: `fn()` +/// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` +/// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` /// /// **Field options:** /// From b602a338664539391ee9c0008242414d34b07403 Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Fri, 12 Dec 2025 23:31:54 +0200 Subject: [PATCH 5/7] docs: small corrections in macros' docs --- components/salsa-macros/src/lib.rs | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 0fd5dbf81..37a4db831 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -57,12 +57,12 @@ pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream { /// Implements a custom database trait. /// -/// Apply this on a custom database trait's definition and the `struct` and `impl` items of +/// Apply this on a custom database trait's definition and the [`struct`] and [`impl`] items of /// implementors. /// -/// When applied to `struct` items, this macro implements the necessary supertraits required for `salsa::Database`. +/// When applied to [`struct`] items, this macro implements the necessary supertraits required for `salsa::Database`. /// -/// When applied to `trait` and `impl` items, this macro adds some hidden trait methods required for [`#[tracked]`](fn@tracked) functions. +/// When applied to [`trait`] and [`impl`] items, this macro adds some hidden trait methods required for [`#[tracked]`](fn@tracked) functions. /// /// # Example /// @@ -98,6 +98,10 @@ pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// } /// ``` +/// +/// [`struct`]: https://doc.rust-lang.org/std/keyword.struct.html +/// [`impl`]: https://doc.rust-lang.org/std/keyword.impl.html +/// [`trait`]: https://doc.rust-lang.org/std/keyword.trait.html #[proc_macro_attribute] pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { db::db(args, input) @@ -105,13 +109,13 @@ pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { /// Creates interned structs. /// -/// **Container options:** +/// **Container attributes:** /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. /// - TODO /// -/// **Field options:** +/// **Field attributes:** /// /// - TODO /// @@ -135,13 +139,13 @@ pub fn supertype(input: TokenStream) -> TokenStream { /// Creates input structs. /// -/// **Container options:** +/// **Container attributes:** /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. /// - TODO /// -/// **Field options:** +/// **Field attributes:** /// /// - `default`: Marks the field as tracked. /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) @@ -167,11 +171,13 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { input::input(args, input) } -/// Creates tracked structs, functions and `impl`s. +/// Creates tracked structs, functions and [`impl`]s. /// /// # Tracked structs /// -/// **Container options:** +/// **Tracked structs** are usually used as parameters to tracked functions. They can only be created inside tracked functions. +/// +/// **Container attributes:** /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. @@ -182,9 +188,9 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` /// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` /// -/// **Field options:** +/// **Field attributes:** /// -/// - `tracked`: Marks the field as tracked. +/// - `tracked`: Marks the field as tracked. TODO: what does this actually mean? Fields without this attribute must implement [`Hash`](std::hash::Hash). /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) /// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) /// - `get`: Name of the getter function (default: field name) @@ -192,12 +198,11 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// # Tracked functions /// -/// When you call a tracked function, Salsa will track which inputs it accesses and memoize the return value based on it. This data is saved in the database. When it's called again, the inputs are compared. If they're identical, the first. +/// When you call a **tracked function**, Salsa will track which inputs it accesses and memoize the return value based on it. This data is saved in the database. When it's called again, the inputs are compared. If they're identical, the first call's return value is returned. /// -/// Tracked functions always take the database as the first argument and can take [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator) structs for the rest. -/// arguments. +/// Tracked functions always take the database as the first argument and can take [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator) structs for the rest of the arguments. /// -/// **Options:** +/// **Attributes:** /// /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) /// - `specify`: Signal that the value can be externally specified (only works with a single Salsa struct as the input. incompatible with `lru`) @@ -211,7 +216,7 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name /// - `persist` (Only with persistence feature) /// -/// # Tracked `impl`s +/// # Tracked [`impl`]s /// /// TODO /// @@ -236,6 +241,8 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// /// ``` +/// +/// [`impl`]: https://doc.rust-lang.org/std/keyword.impl.html #[proc_macro_attribute] pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream { tracked::tracked(args, input) From 98eceebfb13d4767f586446481e2172fce1cef88 Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Mon, 23 Mar 2026 20:24:57 +0200 Subject: [PATCH 6/7] docs: document all attributes --- components/salsa-macros/src/lib.rs | 166 ++++++++++++++++---- components/salsa-macros/src/options.rs | 23 +++ components/salsa-macros/src/salsa_struct.rs | 3 +- 3 files changed, 163 insertions(+), 29 deletions(-) diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 37a4db831..6e018f70c 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -50,6 +50,59 @@ mod tracked_struct; mod update; mod xform; +/// Collection of all attributes' documentation. Copy these and filter with +/// [`options::AllowedOptions`] and [`salsa_struct::SalsaStructAllowedOptions`] implementations. +/// +/// # Container attributes +/// +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +/// - `specify`: Indicate that the value can be externally specified (only works with a single Salsa struct as the input. Incompatible with `lru`) +// For functions: +/// - `no_eq`: Always mark the output as updated when function is re-created. The type does not have to implement [`Eq`]. This is incompatible with `cycle_fn`. +/// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. +// Explicitly not documented due to deprecation: - `no_lifetime`: TODO +// Explicitly not documented: - `unsafe(non_update_return_type)` +/// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. +// Explicitly not documented as it's unused: - `data = `: Name of the data type for an interned struct. +// Explicitly not documented as it's unused: - `db = `: Path to the database. +// For functions: +/// - `cycle_fn = `: Cycle recovery function. TODO (default: `salsa::plumbing::unexpected_cycle_recovery!`) +// For functions: +/// - `cycle_initial = `: Initial value for cycle iteration. TODO (default: `salsa::plumbing::unexpected_cycle_initial!`) +// For functions: +/// - `cycle_result = `: Result for non-fixpoint cycle. TODO +/// - `lru = `: Set the LRU capacity (default: 0) +/// - `constructor = `: Name of the constructor function. (default: `new`) +// Explicitly not documented: - `id = `: custom ID for interned structs. Must implement `salsa::plumbing::AsId`. (default: `salsa::Id`) +/// - `revisions = `: minimum number of revisions to keep a value interned. +/// (default: `salsa::plumbing::internal::Configuration::REVISIONS`) +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) +/// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name. +/// - `persist` (Only with persistence feature) +/// - `persist([serialize = ], [deserialize = ])` (Only with persistence feature) +/// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` +/// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` +/// +/// # Field attributes +/// +// Only if [`salsa_struct::SalsaStructAllowedOptions::ALLOW_TRACKED`]: +/// - `tracked`: Marks the field as tracked. Fields without this attribute must implement [`Hash`](std::hash::Hash). +/// * Modifications to tracked fields only invalidates the data depending on the tracked fields. Use tracked fields when you need fine-grained incremental recomputation. +/// * Modifications to untracked fields invalidates everything depending on the whole tracked struct. Use untracked fields for identity-defining data that rarely changes. +// Only if [`salsa_struct::SalsaStructAllowedOptions::ALLOW_DEFAULT`]: +/// - `default`: Marks the field as optional and as having a [`Default`] implementation. +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +// For input structs: +/// - `no_eq`: Always mark the field as updated when its setter is called. The type does not have to implement [`Eq`]. +// For tracked structs: +/// - `no_eq`: Always mark the field as updated when the struct is recreated inside a tracked function. The type does not have to implement [`Eq`]. +/// - `get()`: Name of the getter function (default: field name) +// Only for inputs: +/// - `set()`: Name of the setter function (default: `set_` + field name) +// Only if [`salsa_struct::SalsaStructAllowedOptions::ALLOW_MAYBE_UPDATE`]: +// Explicitly not documented: - `maybe_update()`: Function of type `unsafe fn(*mut #field_ty, #field_ty) -> bool`. TODO +mod attrs_doc {} + #[proc_macro_attribute] pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream { accumulator::accumulator(args, input) @@ -109,15 +162,28 @@ pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { /// Creates interned structs. /// -/// **Container attributes:** +/// **Interned structs** are dedpulicated, immutable structs used as parameters to tracked +/// functions. +/// +/// # Container attributes /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. +// Explicitly not documented due to deprecation: - `no_lifetime`: TODO /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. -/// - TODO +// Explicitly not documented as it's unused: - `data = `: TODO +/// - `constructor = `: Name of the constructor function. (default: `new`) +// Explicitly not documented: - `id = `: TODO (default: `salsa::Id`) +/// - `revisions = `: minimum number of revisions to keep a value interned. +/// (default: `salsa::plumbing::internal::Configuration::REVISIONS`) +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) +/// - `persist([serialize = ], [deserialize = ])` (Only with persistence feature) +/// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` +/// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` /// -/// **Field attributes:** +/// # Field attributes /// -/// - TODO +/// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) +/// - `get()`: Name of the getter function (default: field name) /// /// # Example /// @@ -126,6 +192,14 @@ pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { /// struct MyInterned<'db> { /// field: String, /// } +/// +/// let db = salsa::DatabaseImpl::new(); +/// let a = MyInterned::new(&db, "example"); +/// let b = MyInterned::new(&db, "example"); +/// +/// // There is only one String allocation. +/// +/// # drop((a, b)); /// ``` #[proc_macro_attribute] pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream { @@ -139,17 +213,25 @@ pub fn supertype(input: TokenStream) -> TokenStream { /// Creates input structs. /// -/// **Container attributes:** +/// **Input structs** are the starting point of your program. Everything else in your program is +/// a deterministic function of these inputs. +/// +/// # Container attributes /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. -/// - TODO +// Explicitly not documented as it's unused: - `data = `: TODO +/// - `constructor = `: Name of the constructor function. (default: `new`) +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) +/// - `persist([serialize = ], [deserialize = ])` (Only with persistence feature) +/// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` +/// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` /// -/// **Field attributes:** +/// # Field attributes /// -/// - `default`: Marks the field as tracked. +/// - `default`: Marks the field as optional and as having a [`Default`] implementation. /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) -/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) +/// - `no_eq`: Always mark the field as updated when its setter is called. The type does not have to implement [`Eq`]. /// - `get`: Name of the getter function (default: field name) /// - `set`: Name of the setter function (default: `set_` + field name) /// @@ -160,11 +242,16 @@ pub fn supertype(input: TokenStream) -> TokenStream { /// /// #[salsa::input] /// struct File { -// Doesn't work without the std::path:: prefix... +// FIXME: Doesn't work without the std::path:: prefix... /// path: std::path::PathBuf, /// #[returns(ref)] /// contents: String, /// } +/// +/// #[salsa::input(singleton, debug)] +/// struct MySingleton { +/// field: u32, +/// } /// ``` #[proc_macro_attribute] pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { @@ -177,24 +264,26 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// **Tracked structs** are usually used as parameters to tracked functions. They can only be created inside tracked functions. /// -/// **Container attributes:** +/// ## Container attributes /// /// - `debug`: Generate a [`Debug`](std::fmt::Debug) implementation for the struct. /// - `singleton`: Marks the struct as a singleton. There is a maximum of one instance of a singleton struct in a Salsa database. Singletons additionally have `get` and `try_get` methods, and their `new` method sets the singleton. -/// - `data`: TODO -/// - `constructor_name`: TODO -/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Fields) -> usize`, default: none) -/// - `persist(serialize = , deserialize = )` (Only with persistence feature) +// Explicitly not documented as it's unused: - `data = `: TODO +/// - `constructor = `: Name of the constructor function. (default: `new`) +/// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) +/// - `persist([serialize = ], [deserialize = ])` (Only with persistence feature) /// * Type of `serialize`: `fn(&Fields<'_>, S) -> Result where S: serde::Serializer` /// * Type of `deserialize`: `fn(D) -> Result, D::Error> where D: serde::Deserializer<'de>` /// -/// **Field attributes:** +/// ## Field attributes /// -/// - `tracked`: Marks the field as tracked. TODO: what does this actually mean? Fields without this attribute must implement [`Hash`](std::hash::Hash). +/// - `tracked`: Marks the field as tracked. Fields without this attribute must implement [`Hash`](std::hash::Hash). +/// * Modifications to tracked fields only invalidates the data depending on the tracked fields. Use tracked fields when you need fine-grained incremental recomputation. +/// * Modifications to untracked fields invalidates everything depending on the whole tracked struct. Use untracked fields for identity-defining data that rarely changes. /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) -/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) -/// - `get`: Name of the getter function (default: field name) -/// - `maybe_update`: TODO +/// - `no_eq`: Always mark the field as updated when the struct is recreated inside a tracked function. The type does not have to implement [`Eq`]. +/// - `get()`: Name of the getter function (default: field name) +// Explicitly not documented: - `maybe_update()`: Function of type `unsafe fn(*mut #field_ty, #field_ty) -> bool`. TODO /// /// # Tracked functions /// @@ -202,25 +291,25 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// Tracked functions always take the database as the first argument and can take [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator) structs for the rest of the arguments. /// -/// **Attributes:** +/// ## Attributes /// /// - `returns(copy | clone | ref | deref | as_ref | as_deref)`: Configure the "return mode" (default: `clone`) -/// - `specify`: Signal that the value can be externally specified (only works with a single Salsa struct as the input. incompatible with `lru`) -/// - `no_eq`: Signal that the output type does not implement the `Eq` trait (incompatible with `cycle_fn`) +/// - `specify`: Indicate that the value can be externally specified (only works with a single Salsa struct as the input. Incompatible with `lru`) +/// - `no_eq`: Always mark the output as updated when function is re-created. The type does not have to implement [`Eq`]. This is incompatible with `cycle_fn`. // Explicitly not documented: - `unsafe(non_update_return_type)` -/// - `cycle_fn = `: TODO -/// - `cycle_initial = `: TODO -/// - `cycle_result = `: TODO +/// - `cycle_fn = `: Cycle recovery function. TODO +/// - `cycle_initial = `: Initial value for cycle iteration. TODO +/// - `cycle_result = `: Result for non-fixpoint cycle. TODO /// - `lru = `: Set the LRU capacity (default: 0) /// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) -/// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name +/// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name. /// - `persist` (Only with persistence feature) /// /// # Tracked [`impl`]s /// /// TODO /// -/// # Example +/// # Examples /// /// ``` /// #[salsa::tracked] @@ -242,6 +331,27 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// ``` /// +/// ``` +/// //! Comparison between tracked and untracked fields. +/// +/// #[salsa::tracked] +/// struct MyStruct<'db> { +/// #[tracked] +/// tracked_field: u32, +/// untracked_field: String, // No #[tracked] attribute +/// } +/// +/// // If untracked_field changes, both functions re-execute +/// #[salsa::tracked] +/// fn uses_tracked<'db>(db: &'db dyn salsa::Database, s: MyStruct<'db>) -> u32 { +/// s.tracked_field(db) +/// } +/// #[salsa::tracked] +/// fn uses_untracked<'db>(db: &'db dyn salsa::Database, s: MyStruct<'db>) -> String { +/// s.untracked_field(db) +/// } +/// ``` +/// /// [`impl`]: https://doc.rust-lang.org/std/keyword.impl.html #[proc_macro_attribute] pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream { diff --git a/components/salsa-macros/src/options.rs b/components/salsa-macros/src/options.rs index b7bdc807b..6a0faa8e6 100644 --- a/components/salsa-macros/src/options.rs +++ b/components/salsa-macros/src/options.rs @@ -163,24 +163,47 @@ impl Default for Options { /// These flags determine which options are allowed in a given context pub(crate) trait AllowedOptions { + /// Syntax: `returns(copy | clone | ref | deref | as_ref | as_deref)` const RETURNS: bool; + /// Syntax: `specify` const SPECIFY: bool; + /// Syntax: `no_eq` const NO_EQ: bool; + /// Syntax: `debug` const DEBUG: bool; + /// Syntax: `no_lifetime` const NO_LIFETIME: bool; + /// Syntax: `unsafe(non_update_return_type)` const NON_UPDATE_RETURN_TYPE: bool; + /// Syntax: `singleton` const SINGLETON: bool; + /// Syntax: `data = ` const DATA: bool; + /// Syntax: `db = ` const DB: bool; + /// Syntax: `cycle_fn = ` const CYCLE_FN: bool; + /// Syntax: `cycle_initial = ` const CYCLE_INITIAL: bool; + /// Syntax: `cycle_result = ` const CYCLE_RESULT: bool; + /// Syntax: `lru = ` const LRU: bool; + /// Syntax: `constructor = ` const CONSTRUCTOR_NAME: bool; + /// Syntax: `id = ` const ID: bool; + /// Syntax: `revisions = ` const REVISIONS: bool; + /// Syntax: `heap_size = ` const HEAP_SIZE: bool; + /// Syntax: `self_ty = ` const SELF_TY: bool; + /// Syntax: `persist | persist([serialize = ], [deserialize = ])` + /// + /// Only with the `persistence` feature and not [`AllowedPersistOptions::Invalid`]. + /// + /// Overriding `serialize` and `deserialize` is only allowed with [`AllowedPersistOptions::AllowedValue`]. const PERSIST: AllowedPersistOptions; } diff --git a/components/salsa-macros/src/salsa_struct.rs b/components/salsa-macros/src/salsa_struct.rs index 463aca0bc..7e309383a 100644 --- a/components/salsa-macros/src/salsa_struct.rs +++ b/components/salsa-macros/src/salsa_struct.rs @@ -39,7 +39,7 @@ pub(crate) struct SalsaStruct<'s, A: SalsaStructAllowedOptions> { } pub(crate) trait SalsaStructAllowedOptions: AllowedOptions { - /// The kind of struct (e.g., interned, input, tracked). + /// The name of derive macro (e.g., interned, input, tracked). const KIND: &'static str; /// Are `#[maybe_update]` fields allowed? @@ -72,6 +72,7 @@ pub(crate) struct SalsaField<'s> { } const BANNED_FIELD_NAMES: &[&str] = &["from", "new"]; +/// When updating this, also update the documentation in [`AllowedOptions::RETURNS`] and elsewhere. const ALLOWED_RETURN_MODES: &[&str] = &["copy", "clone", "ref", "deref", "as_ref", "as_deref"]; #[allow(clippy::type_complexity)] From f1d2f714e4e6f794e4dc1cdd57f87496d07401c7 Mon Sep 17 00:00:00 2001 From: Axel Karjalainen Date: Wed, 25 Mar 2026 17:56:06 +0200 Subject: [PATCH 7/7] docs: more about tracked functions --- components/salsa-macros/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index 6e018f70c..c22ed496f 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -71,7 +71,7 @@ mod xform; /// - `cycle_initial = `: Initial value for cycle iteration. TODO (default: `salsa::plumbing::unexpected_cycle_initial!`) // For functions: /// - `cycle_result = `: Result for non-fixpoint cycle. TODO -/// - `lru = `: Set the LRU capacity (default: 0) +/// - `lru = `: Set to a nonzero value to enable LRU (Least Recently Used) eviction of memoized values and set the LRU capacity. (default: 0) /// - `constructor = `: Name of the constructor function. (default: `new`) // Explicitly not documented: - `id = `: custom ID for interned structs. Must implement `salsa::plumbing::AsId`. (default: `salsa::Id`) /// - `revisions = `: minimum number of revisions to keep a value interned. @@ -287,9 +287,14 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// /// # Tracked functions /// -/// When you call a **tracked function**, Salsa will track which inputs it accesses and memoize the return value based on it. This data is saved in the database. When it's called again, the inputs are compared. If they're identical, the first call's return value is returned. +/// When you call a **tracked function**, Salsa will track which queries it runs and memoize the return value based on it. This data is saved in the database. When the function is called again and there is no matching memoized value (e.g. when inputs change), the queries are re-run and their outputs are compared. If they're identical, the first output is returned. /// -/// Tracked functions always take the database as the first argument and can take [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator) structs for the rest of the arguments. +/// Tracked functions always take the database as the first argument and can take ingredients for the rest of the inputs. +/// +/// **Ingredients** are structs tagged with [`#[input]`](fn@input), [`#[tracked]`](fn@tracked), +/// [`#[interned]`](fn@interned) and [`#[accumulator]`](fn@accumulator). +/// **Queries** are getters of ingredients' fields (like `input.value(db)` in the example below) or +/// other tracked functions. /// /// ## Attributes /// @@ -300,7 +305,7 @@ pub fn input(args: TokenStream, input: TokenStream) -> TokenStream { /// - `cycle_fn = `: Cycle recovery function. TODO /// - `cycle_initial = `: Initial value for cycle iteration. TODO /// - `cycle_result = `: Result for non-fixpoint cycle. TODO -/// - `lru = `: Set the LRU capacity (default: 0) +/// - `lru = `: Set to a nonzero value to enable LRU (Least Recently Used) eviction of memoized values and set the LRU capacity. (default: 0) /// - `heap_size = `: Function to calculate the heap memory usage of memoized values (type: `fn(&Output) -> usize`, default: none) /// - `self_ty = `: Set the self type of the tracked impl, merely to refine the query name. /// - `persist` (Only with persistence feature)