salsa-macro-rules-0.23.0/.cargo_vcs_info.json0000644000000001720000000000100144500ustar { "git": { "sha1": "572d144b33c766c792239c98b470265aaab3fef0" }, "path_in_vcs": "components/salsa-macro-rules" }salsa-macro-rules-0.23.0/CHANGELOG.md000064400000000000000000000104131046102023000150500ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.23.0](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.22.0...salsa-macro-rules-v0.23.0) - 2025-06-27 ### Added - `Update` derive field overwrite support ([#747](https://github.com/salsa-rs/salsa/pull/747)) ### Other - Emit self ty for query debug name of assoc function queries ([#927](https://github.com/salsa-rs/salsa/pull/927)) - Replace ingredient cache with faster ingredient map ([#921](https://github.com/salsa-rs/salsa/pull/921)) - add option to track heap memory usage of memos ([#925](https://github.com/salsa-rs/salsa/pull/925)) - Hide generated structs of tracked functions from docs via `#[doc(hidden)]` ([#917](https://github.com/salsa-rs/salsa/pull/917)) - add an option to tune interned garbage collection ([#911](https://github.com/salsa-rs/salsa/pull/911)) - Use explicit discriminants for `QueryOriginKind` for better comparisons ([#913](https://github.com/salsa-rs/salsa/pull/913)) - Preserve attributes on interned/tracked struct fields ([#905](https://github.com/salsa-rs/salsa/pull/905)) - Use `Revision` and `Durability` directly in input `Value` ([#902](https://github.com/salsa-rs/salsa/pull/902)) - Allow lifetimes in arguments in tracked fns with >1 parameters ([#880](https://github.com/salsa-rs/salsa/pull/880)) - Replace loom with shuttle ([#876](https://github.com/salsa-rs/salsa/pull/876)) ## [0.22.0](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.21.1...salsa-macro-rules-v0.22.0) - 2025-05-23 ### Other - Allow creation of tracked associated functions (without `self`) ([#859](https://github.com/salsa-rs/salsa/pull/859)) - Remove default `PartialOrd` and `Ord` derives for salsa-structs ([#868](https://github.com/salsa-rs/salsa/pull/868)) - Fix returns(deref | as_ref | as_deref) in tracked methods ([#857](https://github.com/salsa-rs/salsa/pull/857)) - Changed `return_ref` syntax to `returns(as_ref)` and `returns(cloned)` ([#772](https://github.com/salsa-rs/salsa/pull/772)) - Move salsa event system into `Zalsa` ([#849](https://github.com/salsa-rs/salsa/pull/849)) - Add loom support ([#842](https://github.com/salsa-rs/salsa/pull/842)) - Clean up some unsafety ([#830](https://github.com/salsa-rs/salsa/pull/830)) ## [0.21.1](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.21.0...salsa-macro-rules-v0.21.1) - 2025-04-30 ### Other - better debug name for interned query arguments ([#837](https://github.com/salsa-rs/salsa/pull/837)) ## [0.21.0](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.20.0...salsa-macro-rules-v0.21.0) - 2025-04-29 ### Fixed - correct debug output for tracked fields ([#826](https://github.com/salsa-rs/salsa/pull/826)) - allow unused lifetimes in tracked_struct expansion ([#824](https://github.com/salsa-rs/salsa/pull/824)) ### Other - Implement a query stack `Backtrace` analog ([#827](https://github.com/salsa-rs/salsa/pull/827)) - Simplify ID conversions ([#822](https://github.com/salsa-rs/salsa/pull/822)) - Remove unnecessary `Array` abstraction ([#821](https://github.com/salsa-rs/salsa/pull/821)) - Add a compile-fail test for a `'static` `!Update` struct ([#820](https://github.com/salsa-rs/salsa/pull/820)) - squelch most clippy warnings in generated code ([#809](https://github.com/salsa-rs/salsa/pull/809)) ## [0.20.0](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.19.0...salsa-macro-rules-v0.20.0) - 2025-04-22 ### Added - Drop `Debug` requirements and flip implementation defaults ([#756](https://github.com/salsa-rs/salsa/pull/756)) ### Other - Reduce memory usage by deduplicating type information ([#803](https://github.com/salsa-rs/salsa/pull/803)) - Inline/Outline more cold and slow paths ([#805](https://github.com/salsa-rs/salsa/pull/805)) - rewrite cycle handling to support fixed-point iteration ([#603](https://github.com/salsa-rs/salsa/pull/603)) ## [0.19.0](https://github.com/salsa-rs/salsa/compare/salsa-macro-rules-v0.18.0...salsa-macro-rules-v0.19.0) - 2025-03-10 ### Other - Store view downcaster in function ingredients directly ([#720](https://github.com/salsa-rs/salsa/pull/720)) salsa-macro-rules-0.23.0/Cargo.lock0000644000000002420000000000100124210ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "salsa-macro-rules" version = "0.23.0" salsa-macro-rules-0.23.0/Cargo.toml0000644000000016260000000000100124530ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.85" name = "salsa-macro-rules" version = "0.23.0" authors = ["Salsa developers"] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Declarative macros for the salsa crate" readme = false license = "Apache-2.0 OR MIT" repository = "https://github.com/salsa-rs/salsa" [lib] name = "salsa_macro_rules" path = "src/lib.rs" [dependencies] salsa-macro-rules-0.23.0/Cargo.toml.orig000064400000000000000000000004041046102023000161250ustar 00000000000000[package] name = "salsa-macro-rules" version = "0.23.0" authors.workspace = true edition.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true description = "Declarative macros for the salsa crate" [dependencies] salsa-macro-rules-0.23.0/src/lib.rs000064400000000000000000000015271046102023000151500ustar 00000000000000//! This crate defines various `macro_rules` macros //! used as part of Salsa's internal plumbing. //! These macros are re-exported under `salsa::plumbing``. //! The procedural macros emit calls to these //! `macro_rules` macros after doing error checking. //! //! Using `macro_rules` macro definitions is generally //! more ergonomic and also permits true hygiene for local variables //! (sadly not items). //! //! Currently the only way to have a macro that is re-exported //! from a submodule is to use multiple crates, hence the existence //! of this crate. mod macro_if; mod maybe_backdate; mod maybe_default; mod return_mode; mod setup_accumulator_impl; mod setup_input_struct; mod setup_interned_struct; mod setup_tracked_assoc_fn_body; mod setup_tracked_fn; mod setup_tracked_method_body; mod setup_tracked_struct; mod unexpected_cycle_recovery; salsa-macro-rules-0.23.0/src/macro_if.rs000064400000000000000000000006611046102023000161570ustar 00000000000000#[macro_export] macro_rules! macro_if { (true => $($t:tt)*) => { $($t)* }; (false => $($t:tt)*) => { }; (if true { $($t:tt)* } else { $($f:tt)*}) => { $($t)* }; (if false { $($t:tt)* } else { $($f:tt)*}) => { $($f)* }; (if0 0 { $($t:tt)* } else { $($f:tt)*}) => { $($t)* }; (if0 $n:literal { $($t:tt)* } else { $($f:tt)*}) => { $($f)* }; } salsa-macro-rules-0.23.0/src/maybe_backdate.rs000064400000000000000000000016641046102023000173170ustar 00000000000000/// Conditionally update field value and backdate revisions #[macro_export] macro_rules! maybe_backdate { ( ($return_mode:ident, no_backdate, $maybe_default:ident), $maybe_update:tt, $old_field_place:expr, $new_field_place:expr, $revision_place:expr, $current_revision:expr, $zalsa:ident, ) => { $zalsa::always_update( &mut $revision_place, $current_revision, &mut $old_field_place, $new_field_place, ); }; ( ($return_mode:ident, backdate, $maybe_default:ident), $maybe_update:tt, $old_field_place:expr, $new_field_place:expr, $revision_place:expr, $current_revision:expr, $zalsa:ident, ) => { if $maybe_update(std::ptr::addr_of_mut!($old_field_place), $new_field_place) { $revision_place = $current_revision; } }; } salsa-macro-rules-0.23.0/src/maybe_default.rs000064400000000000000000000013261046102023000172000ustar 00000000000000/// Generate either `field_ref_expr` or `field_ty::default` /// /// Used when generating an input's builder. #[macro_export] macro_rules! maybe_default { ( ($return_mode:ident, $maybe_backdate:ident, default), $field_ty:ty, $field_ref_expr:expr, ) => { <$field_ty>::default() }; ( ($return_mode:ident, $maybe_backdate:ident, required), $field_ty:ty, $field_ref_expr:expr, ) => { $field_ref_expr }; } #[macro_export] macro_rules! maybe_default_tt { (($return_mode:ident, $maybe_backdate:ident, default) => $($t:tt)*) => { $($t)* }; (($return_mode:ident, $maybe_backdate:ident, required) => $($t:tt)*) => { }; } salsa-macro-rules-0.23.0/src/return_mode.rs000064400000000000000000000045631046102023000167300ustar 00000000000000/// Generate the expression for the return type, depending on the return mode defined in [`salsa-macros::options::Options::returns`] /// /// Used when generating field getters. #[macro_export] macro_rules! return_mode_expression { ( (copy, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { *$field_ref_expr }; ( (clone, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { ::core::clone::Clone::clone($field_ref_expr) }; ( (ref, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { $field_ref_expr }; ( (deref, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { ::core::ops::Deref::deref($field_ref_expr) }; ( (as_ref, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { ::salsa::SalsaAsRef::as_ref($field_ref_expr) }; ( (as_deref, $maybe_backdate:ident, $maybe_default:ident), $field_ty:ty, $field_ref_expr:expr, ) => { ::salsa::SalsaAsDeref::as_deref($field_ref_expr) }; } #[macro_export] macro_rules! return_mode_ty { ( (copy, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { $field_ty }; ( (clone, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { $field_ty }; ( (ref, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { & $db_lt $field_ty }; ( (deref, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { & $db_lt <$field_ty as ::core::ops::Deref>::Target }; ( (as_ref, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { <$field_ty as ::salsa::SalsaAsRef>::AsRef<$db_lt> }; ( (as_deref, $maybe_backdate:ident, $maybe_default:ident), $db_lt:lifetime, $field_ty:ty ) => { <$field_ty as ::salsa::SalsaAsDeref>::AsDeref<$db_lt> }; } salsa-macro-rules-0.23.0/src/setup_accumulator_impl.rs000064400000000000000000000032111046102023000211520ustar 00000000000000/// Macro for setting up a function that must intern its arguments. #[macro_export] macro_rules! setup_accumulator_impl { ( // Name of the struct Struct: $Struct:ident, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $zalsa:ident, $zalsa_struct:ident, $CACHE:ident, $ingredient:ident, ] ) => { #[allow(clippy::all)] #[allow(dead_code)] const _: () = { use salsa::plumbing as $zalsa; use salsa::plumbing::accumulator as $zalsa_struct; fn $ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<$Struct> { static $CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Struct>> = $zalsa::IngredientCache::new(); $CACHE.get_or_create(zalsa, || { zalsa .lookup_jar_by_type::<$zalsa_struct::JarImpl<$Struct>>() .get_or_create() }) } impl $zalsa::Accumulator for $Struct { const DEBUG_NAME: &'static str = stringify!($Struct); fn accumulate(self, db: &Db) where Db: ?Sized + $zalsa::Database, { let (zalsa, zalsa_local) = db.zalsas(); $ingredient(zalsa).push(zalsa_local, self); } } }; }; } salsa-macro-rules-0.23.0/src/setup_input_struct.rs000064400000000000000000000341311046102023000203620ustar 00000000000000/// Macro for setting up a function that must intern its arguments. #[macro_export] macro_rules! setup_input_struct { ( // Attributes on the struct attrs: [$(#[$attr:meta]),*], // Visibility of the struct vis: $vis:vis, // Name of the struct Struct: $Struct:ident, // Name user gave for `new` new_fn: $new_fn:ident, // A series of option tuples; see `setup_tracked_struct` macro field_options: [$($field_option:tt),*], // Field names field_ids: [$($field_id:ident),*], // Names for field getter methods (typically `foo`) field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*], // Names for field setter methods (typically `set_foo`) field_setters: [$($field_setter_vis:vis $field_setter_id:ident),*], // Field types field_tys: [$($field_ty:ty),*], // Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`). field_indices: [$($field_index:tt),*], // Attributes for each field field_attrs: [$([$(#[$field_attr:meta]),*]),*], // Fields that are required (have no default value). Each item is the fields name and type. required_fields: [$($required_field_id:ident $required_field_ty:ty),*], // Names for the field durability methods on the builder (typically `foo_durability`) field_durability_ids: [$($field_durability_id:ident),*], // Number of fields num_fields: $N:literal, // If true, this is a singleton input. is_singleton: $is_singleton:tt, // If true, generate a debug impl. generate_debug_impl: $generate_debug_impl:tt, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $zalsa:ident, $zalsa_struct:ident, $Configuration:ident, $Builder:ident, $CACHE:ident, $Db:ident, ] ) => { $(#[$attr])* #[derive(Copy, Clone, PartialEq, Eq, Hash)] $vis struct $Struct(salsa::Id); #[allow(clippy::all)] #[allow(dead_code)] const _: () = { use salsa::plumbing as $zalsa; use $zalsa::input as $zalsa_struct; type $Configuration = $Struct; impl $zalsa_struct::Configuration for $Configuration { const LOCATION: $zalsa::Location = $zalsa::Location { file: file!(), line: line!(), }; const DEBUG_NAME: &'static str = stringify!($Struct); const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*]; type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}}; type Struct = $Struct; type Fields = ($($field_ty,)*); type Revisions = [$zalsa::Revision; $N]; type Durabilities = [$zalsa::Durability; $N]; } impl $Configuration { pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl { Self::ingredient_(db.zalsa()) } fn ingredient_(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl { static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); CACHE.get_or_create(zalsa, || { zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create() }) } pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl, &mut $zalsa::Runtime) { let zalsa_mut = db.zalsa_mut(); zalsa_mut.new_revision(); let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create(); let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index); let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl>(); (ingredient, runtime) } } impl $zalsa::FromId for $Struct { fn from_id(id: salsa::Id) -> Self { Self(id) } } impl $zalsa::AsId for $Struct { fn as_id(&self) -> salsa::Id { self.0 } } unsafe impl $zalsa::Update for $Struct { unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { if unsafe { *old_pointer } != new_value { unsafe { *old_pointer = new_value }; true } else { false } } } $zalsa::macro_if! { $generate_debug_impl => impl std::fmt::Debug for $Struct { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Self::default_debug_fmt(*self, f) } } } impl $zalsa::SalsaStructInDb for $Struct { type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex; fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices { aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into() } #[inline] fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option { if type_id == $zalsa::TypeId::of::<$Struct>() { $zalsa::Some($Struct(id)) } else { $zalsa::None } } } impl $Struct { #[inline] pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + salsa::Database, { Self::builder($($required_field_id,)*).new(db) } pub fn builder($($required_field_id: $required_field_ty),*) -> ::Builder { builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*) } $( $(#[$field_attr])* $field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty) where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { let fields = $Configuration::ingredient_(db.zalsa()).field( db.as_dyn_database(), self, $field_index, ); $zalsa::return_mode_expression!( $field_option, $field_ty, &fields.$field_index, ) } )* $( #[must_use] $field_setter_vis fn $field_setter_id<'db, $Db>(self, db: &'db mut $Db) -> impl salsa::Setter + use<'db, $Db> where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut()); $zalsa::input::SetterImpl::new( revision, self, $field_index, ingredient, |fields, f| std::mem::replace(&mut fields.$field_index, f), ) } )* $zalsa::macro_if! { $is_singleton => pub fn try_get<$Db>(db: &$Db) -> Option where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + salsa::Database, { let zalsa = db.zalsa(); $Configuration::ingredient_(zalsa).get_singleton_input(zalsa) } #[track_caller] pub fn get<$Db>(db: &$Db) -> Self where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + salsa::Database, { Self::try_get(db).unwrap() } } /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result where // rustc rejects trivial bounds, but it cannot see through higher-ranked bounds // with its check :^) $(for<'__trivial_bounds> $field_ty: std::fmt::Debug),* { $zalsa::with_attached_database(|db| { let fields = $Configuration::ingredient(db).leak_fields(db, this); let mut f = f.debug_struct(stringify!($Struct)); let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this)); $( let f = f.field(stringify!($field_id), &fields.$field_index); )* f.finish() }).unwrap_or_else(|| { f.debug_struct(stringify!($Struct)) .field("[salsa id]", &this.0) .finish() }) } } impl $zalsa_struct::HasBuilder for $Struct { type Builder = builder::$Builder; } // Implement `new` here instead of inside the builder module // because $Configuration can't be named in `builder`. impl builder::$Builder { /// Creates the new input with the set values. #[must_use] pub fn new<$Db>(self, db: &$Db) -> $Struct where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + salsa::Database { let zalsa = db.zalsa(); let current_revision = zalsa.current_revision(); let ingredient = $Configuration::ingredient_(zalsa); let (fields, revision, durabilities) = builder::builder_into_inner(self, current_revision); ingredient.new_input(db.as_dyn_database(), fields, revision, durabilities) } } mod builder { use super::*; use salsa::plumbing as $zalsa; use $zalsa::input as $zalsa_struct; // These are standalone functions instead of methods on `Builder` to prevent // that the enclosing module can call them. pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder { $Builder { fields: ($($field_id,)*), durabilities: [salsa::Durability::default(); $N], } } pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), [$zalsa::Revision; $N], [$zalsa::Durability; $N]) { (builder.fields, [revision; $N], [$(builder.durabilities[$field_index]),*]) } #[must_use] pub struct $Builder { /// The field values. fields: ($($field_ty,)*), /// The durabilities per field. durabilities: [salsa::Durability; $N], } impl $Builder { /// Sets the durability of all fields. /// /// Overrides any previously set durabilities. pub fn durability(mut self, durability: salsa::Durability) -> Self { self.durabilities = [durability; $N]; self } $($zalsa::maybe_default_tt! { $field_option => /// Sets the value of the field `$field_id`. #[must_use] pub fn $field_id(mut self, value: $field_ty) -> Self { self.fields.$field_index = value; self } })* $( /// Sets the durability for the field `$field_id`. #[must_use] pub fn $field_durability_id(mut self, durability: salsa::Durability) -> Self { self.durabilities[$field_index] = durability; self } )* } } }; }; } salsa-macro-rules-0.23.0/src/setup_interned_struct.rs000064400000000000000000000245651046102023000210450ustar 00000000000000/// Macro for setting up a function that must intern its arguments. #[macro_export] macro_rules! setup_interned_struct { ( // Attributes on the struct attrs: [$(#[$attr:meta]),*], // Visibility of the struct vis: $vis:vis, // Name of the struct Struct: $Struct:ident, // Name of the struct data. This is a parameter because `std::concat_idents` // is unstable and taking an additional dependency is unnecessary. StructData: $StructDataIdent:ident, // Name of the struct type with a `'static` argument (unless this type has no db lifetime, // in which case this is the same as `$Struct`) StructWithStatic: $StructWithStatic:ty, // Name of the `'db` lifetime that the user gave db_lt: $db_lt:lifetime, // optional db lifetime argument. db_lt_arg: $($db_lt_arg:lifetime)?, // the salsa ID id: $Id:path, // The minimum number of revisions to keep the value interned. revisions: $($revisions:expr)?, // the lifetime used in the desugared interned struct. // if the `db_lt_arg`, is present, this is `db_lt_arg`, but otherwise, // it is `'static`. interior_lt: $interior_lt:lifetime, // Name user gave for `new` new_fn: $new_fn:ident, // A series of option tuples; see `setup_tracked_struct` macro field_options: [$($field_option:tt),*], // Field names field_ids: [$($field_id:ident),*], // Names for field setter methods (typically `set_foo`) field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*], // Field types field_tys: [$($field_ty:ty),*], // Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`). field_indices: [$($field_index:tt),*], // Indexed types for each field (T0, T1, ...) field_indexed_tys: [$($indexed_ty:ident),*], // Attrs for each field. field_attrs: [$([$(#[$field_attr:meta]),*]),*], // Number of fields num_fields: $N:literal, // If true, generate a debug impl. generate_debug_impl: $generate_debug_impl:tt, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $zalsa:ident, $zalsa_struct:ident, $Configuration:ident, $CACHE:ident, $Db:ident, ] ) => { $(#[$attr])* #[derive(Copy, Clone, PartialEq, Eq, Hash)] $vis struct $Struct< $($db_lt_arg)? >( $Id, std::marker::PhantomData < & $interior_lt salsa::plumbing::interned::Value <$StructWithStatic> > ); #[allow(clippy::all)] #[allow(dead_code)] const _: () = { use salsa::plumbing as $zalsa; use $zalsa::interned as $zalsa_struct; type $Configuration = $StructWithStatic; type $StructDataIdent<$db_lt> = ($($field_ty,)*); /// Key to use during hash lookups. Each field is some type that implements `Lookup` /// for the owned type. This permits interning with an `&str` when a `String` is required and so forth. #[derive(Hash)] struct StructKey<$db_lt, $($indexed_ty),*>( $($indexed_ty,)* std::marker::PhantomData<&$db_lt ()>, ); impl<$db_lt, $($indexed_ty,)*> $zalsa::interned::HashEqLike> for $StructDataIdent<$db_lt> where $($field_ty: $zalsa::interned::HashEqLike<$indexed_ty>),* { fn hash(&self, h: &mut H) { $($zalsa::interned::HashEqLike::<$indexed_ty>::hash(&self.$field_index, &mut *h);)* } fn eq(&self, data: &StructKey<$db_lt, $($indexed_ty),*>) -> bool { ($($zalsa::interned::HashEqLike::<$indexed_ty>::eq(&self.$field_index, &data.$field_index) && )* true) } } impl<$db_lt, $($indexed_ty: $zalsa::interned::Lookup<$field_ty>),*> $zalsa::interned::Lookup<$StructDataIdent<$db_lt>> for StructKey<$db_lt, $($indexed_ty),*> { #[allow(unused_unit)] fn into_owned(self) -> $StructDataIdent<$db_lt> { ($($zalsa::interned::Lookup::into_owned(self.$field_index),)*) } } impl salsa::plumbing::interned::Configuration for $StructWithStatic { const LOCATION: $zalsa::Location = $zalsa::Location { file: file!(), line: line!(), }; const DEBUG_NAME: &'static str = stringify!($Struct); $( const REVISIONS: ::core::num::NonZeroUsize = ::core::num::NonZeroUsize::new($revisions).unwrap(); )? type Fields<'a> = $StructDataIdent<'a>; type Struct<'db> = $Struct< $($db_lt_arg)? >; } impl $Configuration { pub fn ingredient(db: &Db) -> &$zalsa_struct::IngredientImpl where Db: ?Sized + $zalsa::Database, { static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); let zalsa = db.zalsa(); CACHE.get_or_create(zalsa, || { zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create() }) } } impl< $($db_lt_arg)? > $zalsa::AsId for $Struct< $($db_lt_arg)? > { fn as_id(&self) -> salsa::Id { self.0.as_id() } } impl< $($db_lt_arg)? > $zalsa::FromId for $Struct< $($db_lt_arg)? > { fn from_id(id: salsa::Id) -> Self { Self(<$Id>::from_id(id), std::marker::PhantomData) } } unsafe impl< $($db_lt_arg)? > Send for $Struct< $($db_lt_arg)? > {} unsafe impl< $($db_lt_arg)? > Sync for $Struct< $($db_lt_arg)? > {} $zalsa::macro_if! { $generate_debug_impl => impl< $($db_lt_arg)? > std::fmt::Debug for $Struct< $($db_lt_arg)? > { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Self::default_debug_fmt(*self, f) } } } impl< $($db_lt_arg)? > $zalsa::SalsaStructInDb for $Struct< $($db_lt_arg)? > { type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex; fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices { aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into() } #[inline] fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option { if type_id == $zalsa::TypeId::of::<$Struct>() { $zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id)) } else { $zalsa::None } } } unsafe impl< $($db_lt_arg)? > $zalsa::Update for $Struct< $($db_lt_arg)? > { unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { if unsafe { *old_pointer } != new_value { unsafe { *old_pointer = new_value }; true } else { false } } } impl<$db_lt> $Struct< $($db_lt_arg)? > { pub fn $new_fn<$Db, $($indexed_ty: $zalsa::interned::Lookup<$field_ty> + std::hash::Hash,)*>(db: &$db_lt $Db, $($field_id: $indexed_ty),*) -> Self where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + salsa::Database, $( $field_ty: $zalsa::interned::HashEqLike<$indexed_ty>, )* { $Configuration::ingredient(db).intern(db.as_dyn_database(), StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*)) } $( $(#[$field_attr])* $field_getter_vis fn $field_getter_id<$Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty) where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), self); $zalsa::return_mode_expression!( $field_option, $field_ty, &fields.$field_index, ) } )* /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { $zalsa::with_attached_database(|db| { let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), this); let mut f = f.debug_struct(stringify!($Struct)); $( let f = f.field(stringify!($field_id), &fields.$field_index); )* f.finish() }).unwrap_or_else(|| { f.debug_tuple(stringify!($Struct)) .field(&$zalsa::AsId::as_id(&this)) .finish() }) } } }; }; } salsa-macro-rules-0.23.0/src/setup_tracked_assoc_fn_body.rs000064400000000000000000000024251046102023000221250ustar 00000000000000#[macro_export] macro_rules! setup_tracked_assoc_fn_body { ( salsa_tracked_attr: #[$salsa_tracked_attr:meta], self_ty: $self_ty:ty, db_lt: $($db_lt:lifetime)?, db: $db:ident, db_ty: ($($db_ty:tt)*), input_ids: [$($input_id:ident),*], input_tys: [$($input_ty:ty),*], output_ty: $output_ty:ty, inner_fn_name: $inner_fn_name:ident, inner_fn: $inner_fn:item, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $InnerTrait:ident, ] ) => { { trait $InnerTrait<$($db_lt)?> { fn $inner_fn_name(db: $($db_ty)*, $($input_id: $input_ty),*) -> $output_ty; } impl<$($db_lt)?> $InnerTrait<$($db_lt)?> for $self_ty { $inner_fn } #[$salsa_tracked_attr] fn $inner_fn_name<$($db_lt)?>(db: $($db_ty)*, $($input_id: $input_ty),*) -> $output_ty { <$self_ty as $InnerTrait>::$inner_fn_name(db, $($input_id),*) } $inner_fn_name($db, $($input_id),*) } }; } salsa-macro-rules-0.23.0/src/setup_tracked_fn.rs000064400000000000000000000410231046102023000177150ustar 00000000000000/// Macro for setting up a function that must intern its arguments. #[macro_export] macro_rules! setup_tracked_fn { ( // Attributes on the function attrs: [$(#[$attr:meta]),*], // Visibility of the function vis: $vis:vis, // Name of the function fn_name: $fn_name:ident, // Name of the `'db` lifetime that the user gave; if they didn't, then defaults to `'db` db_lt: $db_lt:lifetime, // Path to the database trait that the user's database parameter used Db: $Db:path, // Name of the database parameter given by the user. db: $db:ident, // An identifier for each function argument EXCEPT the database. // We prefer to use the identifier the user gave, but if the user gave a pattern // (e.g., `(a, b): (u32, u32)`) we will synthesize an identifier. input_ids: [$($input_id:ident),*], // Types of the function arguments (may reference `$generics`). input_tys: [$($input_ty:ty),*], // Types of the function arguments as should be put in interned struct. interned_input_tys: [$($interned_input_ty:ty),*], // Return type of the function (may reference `$generics`). output_ty: $output_ty:ty, // Function body, may reference identifiers defined in `$input_pats` and the generics from `$generics` inner_fn: {$($inner_fn:tt)*}, // Path to the cycle recovery function to use. cycle_recovery_fn: ($($cycle_recovery_fn:tt)*), // Path to function to get the initial value to use for cycle recovery. cycle_recovery_initial: ($($cycle_recovery_initial:tt)*), // Name of cycle recovery strategy variant to use. cycle_recovery_strategy: $cycle_recovery_strategy:ident, // If true, this is specifiable. is_specifiable: $is_specifiable:tt, // Equality check strategy function values_equal: {$($values_equal:tt)+}, // If true, the input needs an interner (because it has >1 argument). needs_interner: $needs_interner:tt, // The function used to implement `C::heap_size`. heap_size_fn: $($heap_size_fn:path)?, // LRU capacity (a literal, maybe 0) lru: $lru:tt, // The return mode for the function, see `salsa_macros::options::Option::returns` return_mode: $return_mode:tt, assert_return_type_is_update: {$($assert_return_type_is_update:tt)*}, $(self_ty: $self_ty:ty,)? // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $zalsa:ident, $Configuration:ident, $InternedData:ident, $FN_CACHE:ident, $INTERN_CACHE:ident, $inner:ident, ] ) => { // Suppress this clippy lint because we sometimes require `'db` where the ordinary Rust rules would not. #[allow(clippy::needless_lifetimes)] $(#[$attr])* $vis fn $fn_name<$db_lt>( $db: &$db_lt dyn $Db, $($input_id: $input_ty,)* ) -> salsa::plumbing::return_mode_ty!(($return_mode, __, __), $db_lt, $output_ty) { use salsa::plumbing as $zalsa; struct $Configuration; static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); $zalsa::macro_if! { if $needs_interner { #[derive(Copy, Clone)] struct $InternedData<$db_lt>( salsa::Id, std::marker::PhantomData<&$db_lt $zalsa::interned::Value<$Configuration>>, ); static $INTERN_CACHE: $zalsa::IngredientCache<$zalsa::interned::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); impl $zalsa::SalsaStructInDb for $InternedData<'_> { type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex; fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices { $zalsa::IngredientIndices::empty() } #[inline] fn cast(id: $zalsa::Id, type_id: ::core::any::TypeId) -> Option { if type_id == ::core::any::TypeId::of::<$InternedData>() { Some($InternedData(id, ::core::marker::PhantomData)) } else { None } } } impl $zalsa::AsId for $InternedData<'_> { #[inline] fn as_id(&self) -> salsa::Id { self.0 } } impl $zalsa::FromId for $InternedData<'_> { #[inline] fn from_id(id: salsa::Id) -> Self { Self(id, ::core::marker::PhantomData) } } impl $zalsa::interned::Configuration for $Configuration { const LOCATION: $zalsa::Location = $zalsa::Location { file: file!(), line: line!(), }; const DEBUG_NAME: &'static str = concat!($(stringify!($self_ty), "::",)? stringify!($fn_name), "::interned_arguments"); type Fields<$db_lt> = ($($interned_input_ty),*); type Struct<$db_lt> = $InternedData<$db_lt>; } } else { type $InternedData<$db_lt> = ($($interned_input_ty),*); } } impl $Configuration { fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> { let zalsa = db.zalsa(); $FN_CACHE.get_or_create(zalsa, || { let jar_entry = zalsa.lookup_jar_by_type::<$Configuration>(); // If the ingredient has already been inserted, we know that the downcaster // has also been registered. This is a fast-path for multi-database use cases // that bypass the ingredient cache and will always execute this closure. if let Some(index) = jar_entry.get() { return index; } ::zalsa_register_downcaster(db); jar_entry.get_or_create() }) } pub fn fn_ingredient_mut(db: &mut dyn $Db) -> &mut $zalsa::function::IngredientImpl { ::zalsa_register_downcaster(db); let zalsa_mut = db.zalsa_mut(); let index = zalsa_mut.lookup_jar_by_type::<$Configuration>().get_or_create(); let (ingredient, _) = zalsa_mut.lookup_ingredient_mut(index); ingredient.assert_type_mut::<$zalsa::function::IngredientImpl>() } $zalsa::macro_if! { $needs_interner => fn intern_ingredient( db: &dyn $Db, ) -> &$zalsa::interned::IngredientImpl<$Configuration> { let zalsa = db.zalsa(); $INTERN_CACHE.get_or_create(zalsa, || { ::zalsa_register_downcaster(db); zalsa.lookup_jar_by_type::<$Configuration>().get_or_create().successor(0) }) } } } impl $zalsa::function::Configuration for $Configuration { const LOCATION: $zalsa::Location = $zalsa::Location { file: file!(), line: line!(), }; const DEBUG_NAME: &'static str = concat!($(stringify!($self_ty), "::", )? stringify!($fn_name)); type DbView = dyn $Db; type SalsaStruct<$db_lt> = $InternedData<$db_lt>; type Input<$db_lt> = ($($interned_input_ty),*); type Output<$db_lt> = $output_ty; const CYCLE_STRATEGY: $zalsa::CycleRecoveryStrategy = $zalsa::CycleRecoveryStrategy::$cycle_recovery_strategy; $($values_equal)+ $( fn heap_size(value: &Self::Output<'_>) -> usize { $heap_size_fn(value) } )? fn execute<$db_lt>($db: &$db_lt Self::DbView, ($($input_id),*): ($($interned_input_ty),*)) -> Self::Output<$db_lt> { $($assert_return_type_is_update)* $($inner_fn)* $inner($db, $($input_id),*) } fn cycle_initial<$db_lt>(db: &$db_lt Self::DbView, ($($input_id),*): ($($interned_input_ty),*)) -> Self::Output<$db_lt> { $($cycle_recovery_initial)*(db, $($input_id),*) } fn recover_from_cycle<$db_lt>( db: &$db_lt dyn $Db, value: &Self::Output<$db_lt>, count: u32, ($($input_id),*): ($($interned_input_ty),*) ) -> $zalsa::CycleRecoveryAction> { $($cycle_recovery_fn)*(db, value, count, $($input_id),*) } fn id_to_input<$db_lt>(db: &$db_lt Self::DbView, key: salsa::Id) -> Self::Input<$db_lt> { $zalsa::macro_if! { if $needs_interner { $Configuration::intern_ingredient(db).data(db.as_dyn_database(), key).clone() } else { $zalsa::FromIdWithDb::from_id(key, db.zalsa()) } } } } impl $zalsa::Jar for $Configuration { fn create_dependencies(zalsa: &$zalsa::Zalsa) -> $zalsa::IngredientIndices where Self: Sized { $zalsa::macro_if! { if $needs_interner { $zalsa::IngredientIndices::empty() } else { <$InternedData as $zalsa::SalsaStructInDb>::lookup_or_create_ingredient_index(zalsa) } } } fn create_ingredients( zalsa: &$zalsa::Zalsa, first_index: $zalsa::IngredientIndex, struct_index: $zalsa::IngredientIndices, ) -> Vec> { let struct_index: $zalsa::IngredientIndices = $zalsa::macro_if! { if $needs_interner { first_index.successor(0).into() } else { struct_index } }; $zalsa::macro_if! { $needs_interner => let intern_ingredient = <$zalsa::interned::IngredientImpl<$Configuration>>::new( first_index.successor(0) ); } let intern_ingredient_memo_types = $zalsa::macro_if! { if $needs_interner { Some($zalsa::Ingredient::memo_table_types(&intern_ingredient)) } else { None } }; // SAFETY: We call with the correct memo types. let memo_ingredient_indices = unsafe { $zalsa::NewMemoIngredientIndices::create( zalsa, struct_index, first_index, $zalsa::function::MemoEntryType::of::<$zalsa::function::Memo<$Configuration>>(), intern_ingredient_memo_types, ) }; let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new( first_index, memo_ingredient_indices, $lru, zalsa.views().downcaster_for::(), ); $zalsa::macro_if! { if $needs_interner { vec![ Box::new(fn_ingredient), Box::new(intern_ingredient), ] } else { vec![ Box::new(fn_ingredient), ] } } } fn id_struct_type_id() -> $zalsa::TypeId { $zalsa::TypeId::of::<$InternedData<'static>>() } } #[allow(non_local_definitions)] impl $fn_name { pub fn accumulated<$db_lt, A: salsa::Accumulator>( $db: &$db_lt dyn $Db, $($input_id: $interned_input_ty,)* ) -> Vec<&$db_lt A> { use salsa::plumbing as $zalsa; let key = $zalsa::macro_if! { if $needs_interner { $Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data) } else { $zalsa::AsId::as_id(&($($input_id),*)) } }; $Configuration::fn_ingredient($db).accumulated_by::($db, key) } $zalsa::macro_if! { $is_specifiable => pub fn specify<$db_lt>( $db: &$db_lt dyn $Db, $($input_id: $interned_input_ty,)* value: $output_ty, ) { let key = $zalsa::AsId::as_id(&($($input_id),*)); $Configuration::fn_ingredient($db).specify_and_record( $db, key, value, ) } } $zalsa::macro_if! { if0 $lru { } else { /// Sets the lru capacity /// /// **WARNING:** Just like an ordinary write, this method triggers /// cancellation. If you invoke it while a snapshot exists, it /// will block until that snapshot is dropped -- if that snapshot /// is owned by the current thread, this could trigger deadlock. #[allow(dead_code)] fn set_lru_capacity(db: &mut dyn $Db, value: usize) { $Configuration::fn_ingredient_mut(db).set_capacity(value); } } } } $zalsa::attach($db, || { let result = $zalsa::macro_if! { if $needs_interner { { let key = $Configuration::intern_ingredient($db).intern_id($db.as_dyn_database(), ($($input_id),*), |_, data| data); $Configuration::fn_ingredient($db).fetch($db, key) } } else { $Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&($($input_id),*))) } }; $zalsa::return_mode_expression!(($return_mode, __, __), $output_ty, result,) }) } // The struct needs be last in the macro expansion in order to make the tracked // function's ident be identified as a function, not a struct, during semantic highlighting. // for more details, see https://github.com/salsa-rs/salsa/pull/612. #[doc(hidden)] #[allow(non_camel_case_types)] $vis struct $fn_name { _priv: std::convert::Infallible, } }; } salsa-macro-rules-0.23.0/src/setup_tracked_method_body.rs000064400000000000000000000025221046102023000216100ustar 00000000000000#[macro_export] macro_rules! setup_tracked_method_body { ( salsa_tracked_attr: #[$salsa_tracked_attr:meta], self: $self:ident, self_ty: $self_ty:ty, db_lt: $($db_lt:lifetime)?, db: $db:ident, db_ty: ($($db_ty:tt)*), input_ids: [$($input_id:ident),*], input_tys: [$($input_ty:ty),*], output_ty: $output_ty:ty, inner_fn_name: $inner_fn_name:ident, inner_fn: $inner_fn:item, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $InnerTrait:ident, ] ) => { { trait $InnerTrait<$($db_lt)?> { fn $inner_fn_name($self, db: $($db_ty)*, $($input_id: $input_ty),*) -> $output_ty; } impl<$($db_lt)?> $InnerTrait<$($db_lt)?> for $self_ty { $inner_fn } #[$salsa_tracked_attr] fn $inner_fn_name<$($db_lt)?>(db: $($db_ty)*, this: $self_ty, $($input_id: $input_ty),*) -> $output_ty { <$self_ty as $InnerTrait>::$inner_fn_name(this, db, $($input_id),*) } $inner_fn_name($db, $self, $($input_id),*) } }; } salsa-macro-rules-0.23.0/src/setup_tracked_struct.rs000064400000000000000000000326451046102023000206500ustar 00000000000000/// Macro for setting up a function that must intern its arguments. #[macro_export] macro_rules! setup_tracked_struct { ( // Attributes on the function. attrs: [$(#[$attr:meta]),*], // Visibility of the struct. vis: $vis:vis, // Name of the struct. Struct: $Struct:ident, // Name of the `'db` lifetime that the user gave. db_lt: $db_lt:lifetime, // Name user gave for `new`. new_fn: $new_fn:ident, // Field names. field_ids: [$($field_id:ident),*], // Tracked field names. tracked_ids: [$($tracked_id:ident),*], // Visibility and names of tracked fields. tracked_getters: [$($tracked_getter_vis:vis $tracked_getter_id:ident),*], // Visibility and names of untracked fields. untracked_getters: [$($untracked_getter_vis:vis $untracked_getter_id:ident),*], // Field types, may reference `db_lt`. field_tys: [$($field_ty:ty),*], // Tracked field types. tracked_tys: [$($tracked_ty:ty),*], // Untracked field types. untracked_tys: [$($untracked_ty:ty),*], // Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`). field_indices: [$($field_index:tt),*], // Absolute indices of any tracked fields, relative to all other fields of this struct. absolute_tracked_indices: [$($absolute_tracked_index:tt),*], // Indices of any tracked fields, relative to only tracked fields on this struct. relative_tracked_indices: [$($relative_tracked_index:tt),*], // Absolute indices of any untracked fields. absolute_untracked_indices: [$($absolute_untracked_index:tt),*], // Tracked field types. tracked_maybe_updates: [$($tracked_maybe_update:tt),*], // Untracked field types. untracked_maybe_updates: [$($untracked_maybe_update:tt),*], // A set of "field options" for each tracked field. // // Each field option is a tuple `(return_mode, maybe_backdate)` where: // // * `return_mode` is an identifier as specified in `salsa_macros::options::Option::returns` // * `maybe_backdate` is either the identifier `backdate` or `no_backdate` // // These are used to drive conditional logic for each field via recursive macro invocation // (see e.g. @return_mode below). tracked_options: [$($tracked_option:tt),*], // A set of "field options" for each untracked field. // // Each field option is a tuple `(return_mode, maybe_backdate)` where: // // * `return_mode` is an identifier as specified in `salsa_macros::options::Option::returns` // * `maybe_backdate` is either the identifier `backdate` or `no_backdate` // // These are used to drive conditional logic for each field via recursive macro invocation // (see e.g. @return_mode below). untracked_options: [$($untracked_option:tt),*], // Attrs for each field. tracked_field_attrs: [$([$(#[$tracked_field_attr:meta]),*]),*], untracked_field_attrs: [$([$(#[$untracked_field_attr:meta]),*]),*], // Number of tracked fields. num_tracked_fields: $N:literal, // If true, generate a debug impl. generate_debug_impl: $generate_debug_impl:tt, // Annoyingly macro-rules hygiene does not extend to items defined in the macro. // We have the procedural macro generate names for those items that are // not used elsewhere in the user's code. unused_names: [ $zalsa:ident, $zalsa_struct:ident, $Configuration:ident, $CACHE:ident, $Db:ident, $Revision:ident, ] ) => { $(#[$attr])* #[derive(Copy, Clone, PartialEq, Eq, Hash)] $vis struct $Struct<$db_lt>( salsa::Id, std::marker::PhantomData < & $db_lt salsa::plumbing::tracked_struct::Value < $Struct<'static> > > ); #[allow(clippy::all)] #[allow(dead_code)] const _: () = { use salsa::plumbing as $zalsa; use $zalsa::tracked_struct as $zalsa_struct; use $zalsa::Revision as $Revision; type $Configuration = $Struct<'static>; impl $zalsa_struct::Configuration for $Configuration { const LOCATION: $zalsa::Location = $zalsa::Location { file: file!(), line: line!(), }; const DEBUG_NAME: &'static str = stringify!($Struct); const TRACKED_FIELD_NAMES: &'static [&'static str] = &[ $(stringify!($tracked_id),)* ]; const TRACKED_FIELD_INDICES: &'static [usize] = &[ $($relative_tracked_index,)* ]; type Fields<$db_lt> = ($($field_ty,)*); type Revisions = [$Revision; $N]; type Struct<$db_lt> = $Struct<$db_lt>; fn untracked_fields(fields: &Self::Fields<'_>) -> impl std::hash::Hash { ( $( &fields.$absolute_untracked_index ),* ) } fn new_revisions(current_revision: $Revision) -> Self::Revisions { [current_revision; $N] } unsafe fn update_fields<$db_lt>( current_revision: $Revision, revisions: &mut Self::Revisions, old_fields: *mut Self::Fields<$db_lt>, new_fields: Self::Fields<$db_lt>, ) -> bool { use $zalsa::UpdateFallback as _; unsafe { $( $crate::maybe_backdate!( $tracked_option, $tracked_maybe_update, (*old_fields).$absolute_tracked_index, new_fields.$absolute_tracked_index, revisions[$relative_tracked_index], current_revision, $zalsa, ); )*; // If any untracked field has changed, return `true`, indicating that the tracked struct // itself should be considered changed. $( $untracked_maybe_update( &mut (*old_fields).$absolute_untracked_index, new_fields.$absolute_untracked_index, ) | )* false } } } impl $Configuration { pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl { Self::ingredient_(db.zalsa()) } fn ingredient_(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl { static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); CACHE.get_or_create(zalsa, || { zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create() }) } } impl<$db_lt> $zalsa::FromId for $Struct<$db_lt> { #[inline] fn from_id(id: salsa::Id) -> Self { $Struct(id, std::marker::PhantomData) } } impl $zalsa::AsId for $Struct<'_> { #[inline] fn as_id(&self) -> $zalsa::Id { self.0 } } impl $zalsa::SalsaStructInDb for $Struct<'_> { type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex; fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices { aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().get_or_create().into() } #[inline] fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option { if type_id == $zalsa::TypeId::of::<$Struct<'static>>() { $zalsa::Some(<$Struct<'static> as $zalsa::FromId>::from_id(id)) } else { $zalsa::None } } } impl $zalsa::TrackedStructInDb for $Struct<'_> { fn database_key_index(zalsa: &$zalsa::Zalsa, id: $zalsa::Id) -> $zalsa::DatabaseKeyIndex { $Configuration::ingredient_(zalsa).database_key_index(id) } } unsafe impl Send for $Struct<'_> {} unsafe impl Sync for $Struct<'_> {} $zalsa::macro_if! { $generate_debug_impl => impl std::fmt::Debug for $Struct<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Self::default_debug_fmt(*self, f) } } } unsafe impl $zalsa::Update for $Struct<'_> { unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { if unsafe { *old_pointer } != new_value { unsafe { *old_pointer = new_value }; true } else { false } } } impl<$db_lt> $Struct<$db_lt> { pub fn $new_fn<$Db>(db: &$db_lt $Db, $($field_id: $field_ty),*) -> Self where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { $Configuration::ingredient(db.as_dyn_database()).new_struct( db.as_dyn_database(), ($($field_id,)*) ) } $( $(#[$tracked_field_attr])* $tracked_getter_vis fn $tracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($tracked_option, $db_lt, $tracked_ty) where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { let db = db.as_dyn_database(); let fields = $Configuration::ingredient(db).tracked_field(db, self, $relative_tracked_index); $crate::return_mode_expression!( $tracked_option, $tracked_ty, &fields.$absolute_tracked_index, ) } )* $( $(#[$untracked_field_attr])* $untracked_getter_vis fn $untracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($untracked_option, $db_lt, $untracked_ty) where // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { let db = db.as_dyn_database(); let fields = $Configuration::ingredient(db).untracked_field(db, self); $crate::return_mode_expression!( $untracked_option, $untracked_ty, &fields.$absolute_untracked_index, ) } )* } #[allow(unused_lifetimes)] impl<'_db> $Struct<'_db> { /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result where // `zalsa::with_attached_database` has a local lifetime for the database // so we need this function to be higher-ranked over the db lifetime // Thus the actual lifetime of `Self` does not matter here so we discard // it with the `'_db` lifetime name as we cannot shadow lifetimes. $(for<$db_lt> $field_ty: std::fmt::Debug),* { $zalsa::with_attached_database(|db| { let fields = $Configuration::ingredient(db).leak_fields(db, this); let mut f = f.debug_struct(stringify!($Struct)); let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this)); $( let f = f.field(stringify!($field_id), &fields.$field_index); )* f.finish() }).unwrap_or_else(|| { f.debug_struct(stringify!($Struct)) .field("[salsa id]", &$zalsa::AsId::as_id(&this)) .finish() }) } } }; }; } salsa-macro-rules-0.23.0/src/unexpected_cycle_recovery.rs000064400000000000000000000012601046102023000216350ustar 00000000000000// Macro that generates the body of the cycle recovery function // for the case where no cycle recovery is possible. This has to be // a macro because it can take a variadic number of arguments. #[macro_export] macro_rules! unexpected_cycle_recovery { ($db:ident, $value:ident, $count:ident, $($other_inputs:ident),*) => {{ std::mem::drop($db); std::mem::drop(($($other_inputs),*)); panic!("cannot recover from cycle") }}; } #[macro_export] macro_rules! unexpected_cycle_initial { ($db:ident, $($other_inputs:ident),*) => {{ std::mem::drop($db); std::mem::drop(($($other_inputs),*)); panic!("no cycle initial value") }}; }