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-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>, { 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..c22ed496f 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -50,16 +50,157 @@ 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 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. +/// (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) } +/// 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!() +/// } +/// } +/// ``` +/// +/// [`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) } +/// Creates interned structs. +/// +/// **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. +// 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 +/// +/// - `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 +/// +/// ``` +/// #[salsa::interned] +/// 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 { interned::interned(args, input) @@ -70,11 +211,153 @@ pub fn supertype(input: TokenStream) -> TokenStream { supertype::supertype(input) } +/// Creates input structs. +/// +/// **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. +// 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 +/// +/// - `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`: 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) +/// +/// # Example +/// +/// ``` +/// use std::path::PathBuf; +/// +/// #[salsa::input] +/// struct File { +// 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 { input::input(args, input) } +/// Creates tracked structs, functions and [`impl`]s. +/// +/// # Tracked structs +/// +/// **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. +// 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 +/// +/// - `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`: 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 +/// +/// 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 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 +/// +/// - `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`) +/// - `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 = `: Cycle recovery function. TODO +/// - `cycle_initial = `: Initial value for cycle iteration. TODO +/// - `cycle_result = `: Result for non-fixpoint cycle. TODO +/// - `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) +/// +/// # Tracked [`impl`]s +/// +/// TODO +/// +/// # Examples +/// +/// ``` +/// #[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::() +/// } +/// +/// ``` +/// +/// ``` +/// //! 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 { tracked::tracked(args, input) 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)] 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)?;