From daba89c2789793ecdd4b3fe9fa867f3f31d7bcd8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jul 2024 08:21:07 -0400 Subject: [PATCH] wip --- components/salsa-macro-rules/src/lib.rs | 5 - components/salsa-macro-rules/src/macro_if.rs | 8 + components/salsa-macro-rules/src/setup_fn.rs | 464 +++++-------------- components/salsa-macros/src/tracked_fn.rs | 104 ++--- src/id.rs | 5 +- src/lib.rs | 5 +- src/salsa_struct.rs | 9 +- 7 files changed, 150 insertions(+), 450 deletions(-) diff --git a/components/salsa-macro-rules/src/lib.rs b/components/salsa-macro-rules/src/lib.rs index d9554ae6..254aca48 100644 --- a/components/salsa-macro-rules/src/lib.rs +++ b/components/salsa-macro-rules/src/lib.rs @@ -21,8 +21,3 @@ mod setup_input_struct; mod setup_interned_struct; mod setup_tracked_struct; mod unexpected_cycle_recovery; - -#[macro_export] -macro_rules! setup_fn { - () => {}; -} diff --git a/components/salsa-macro-rules/src/macro_if.rs b/components/salsa-macro-rules/src/macro_if.rs index 91398b4e..684b7188 100644 --- a/components/salsa-macro-rules/src/macro_if.rs +++ b/components/salsa-macro-rules/src/macro_if.rs @@ -6,4 +6,12 @@ macro_rules! macro_if { (false => $($t:tt)*) => { }; + + (if true { $($t:tt)* } else { $($f:tt)*}) => { + $($t)* + }; + + (if false { $($t:tt)* } else { $($f:tt)*}) => { + $($f)* + }; } diff --git a/components/salsa-macro-rules/src/setup_fn.rs b/components/salsa-macro-rules/src/setup_fn.rs index 8d98d074..6a773286 100644 --- a/components/salsa-macro-rules/src/setup_fn.rs +++ b/components/salsa-macro-rules/src/setup_fn.rs @@ -1,315 +1,6 @@ -/// Macro for setting up a function with no arguments (but the database). -#[macro_export] -macro_rules! setup_constant_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, - - // 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:item, - - // Path to the cycle recovery function to use. - cycle_recovery_fn: ($($cycle_recovery_fn:tt)*), - - // Name of cycle recovery strategy variant to use. - cycle_recovery_strategy: $cycle_recovery_strategy: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, - $Configuration:ident, - $FN_CACHE:ident, - $inner:ident, - ] - ) => { - #[allow(non_camel_case_types)] - $vis struct $fn_name { - _priv: std::convert::Infallible, - } - - $(#[$attr])* - $vis fn $fn_name<$db_lt>( - $db: &$db_lt dyn $Db, - ) -> $output_ty { - use salsa::plumbing as $zalsa; - - struct $Configuration; - - static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> = - $zalsa::IngredientCache::new(); - - impl $Configuration { - fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> { - $FN_CACHE.get_or_create(db.as_salsa_database(), || { - ::zalsa_db(db); - db.add_or_lookup_jar_by_type(&$Configuration) - }) - } - } - - impl $zalsa::function::Configuration for $Configuration { - const DEBUG_NAME: &'static str = stringify!($fn_name); - - type DbView = dyn $Db; - - type SalsaStruct<$db_lt> = $zalsa::Singleton; - - type Input<$db_lt> = (); - - type Output<$db_lt> = $output_ty; - - const CYCLE_STRATEGY: $zalsa::CycleRecoveryStrategy = $zalsa::CycleRecoveryStrategy::$cycle_recovery_strategy; - - fn should_backdate_value( - old_value: &Self::Output<'_>, - new_value: &Self::Output<'_>, - ) -> bool { - $zalsa::should_backdate_value(old_value, new_value) - } - - fn execute<'db>($db: &'db Self::DbView, (): ()) -> Self::Output<'db> { - $inner_fn - - $inner($db) - } - - fn recover_from_cycle<'db>( - db: &$db_lt dyn $Db, - cycle: &$zalsa::Cycle, - (): (), - ) -> Self::Output<'db> { - $($cycle_recovery_fn)*( - db, - cycle, - ) - } - - fn id_to_input<'db>(db: &'db Self::DbView, key: salsa::Id) -> Self::Input<'db> { - () - } - } - - impl $zalsa::Jar for $Configuration { - fn create_ingredients( - &self, - first_index: $zalsa::IngredientIndex, - ) -> Vec> { - vec![ - Box::new(<$zalsa::function::IngredientImpl<$Configuration>>::new( - first_index, - )), - ] - } - } - - impl $fn_name { - pub fn accumulated<$db_lt, A: salsa::Accumulator>( - $db: &$db_lt dyn $Db, - ) -> Vec { - use salsa::plumbing as $zalsa; - let key = $zalsa::AsId::as_id(&$zalsa::Singleton); - let database_key_index = $Configuration::fn_ingredient($db).database_key_index(key); - $zalsa::accumulated_by($db.as_salsa_database(), database_key_index) - } - } - - $zalsa::attach_database($db, || { - $Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&$zalsa::Singleton)).clone() - }) - } - }; -} - /// Macro for setting up a function that must intern its arguments. #[macro_export] -macro_rules! setup_struct_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_id: $input_id:ident, - - // Types of the function arguments (may reference `$generics`). - input_ty: $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:item, - - // Path to the cycle recovery function to use. - cycle_recovery_fn: ($($cycle_recovery_fn: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, - - // 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, - $FN_CACHE:ident, - $inner:ident, - ] - ) => { - #[allow(non_camel_case_types)] - $vis struct $fn_name { - _priv: std::convert::Infallible, - } - - $(#[$attr])* - $vis fn $fn_name<$db_lt>( - $db: &$db_lt dyn $Db, - $input_id: $input_ty, - ) -> $output_ty { - use salsa::plumbing as $zalsa; - - struct $Configuration; - - static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> = - $zalsa::IngredientCache::new(); - - impl $Configuration { - fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> { - $FN_CACHE.get_or_create(db.as_salsa_database(), || { - ::zalsa_db(db); - db.add_or_lookup_jar_by_type(&$Configuration) - }) - } - } - - impl $zalsa::function::Configuration for $Configuration { - const DEBUG_NAME: &'static str = stringify!($fn_name); - - type DbView = dyn $Db; - - type SalsaStruct<$db_lt> = $input_ty; - - type Input<$db_lt> = $input_ty; - - type Output<$db_lt> = $output_ty; - - const CYCLE_STRATEGY: $zalsa::CycleRecoveryStrategy = $zalsa::CycleRecoveryStrategy::$cycle_recovery_strategy; - - fn should_backdate_value( - old_value: &Self::Output<'_>, - new_value: &Self::Output<'_>, - ) -> bool { - $zalsa::should_backdate_value(old_value, new_value) - } - - fn execute<'db>($db: &'db Self::DbView, $input_id: $input_ty) -> Self::Output<'db> { - $inner_fn - - $inner($db, $input_id) - } - - fn recover_from_cycle<'db>( - db: &$db_lt dyn $Db, - cycle: &$zalsa::Cycle, - $input_id: $input_ty, - ) -> Self::Output<'db> { - $($cycle_recovery_fn)*(db, cycle, $input_id) - } - - fn id_to_input<'db>(db: &'db Self::DbView, key: salsa::Id) -> Self::Input<'db> { - $zalsa::LookupId::lookup_id(key, db.as_salsa_database()) - } - } - - impl $zalsa::Jar for $Configuration { - fn create_ingredients( - &self, - first_index: $zalsa::IngredientIndex, - ) -> Vec> { - vec![ - Box::new(<$zalsa::function::IngredientImpl<$Configuration>>::new( - first_index, - )), - ] - } - } - - impl $fn_name { - pub fn accumulated<$db_lt, A: salsa::Accumulator>( - $db: &$db_lt dyn $Db, - $input_id: $input_ty, - ) -> Vec { - use salsa::plumbing as $zalsa; - let key = $zalsa::AsId::as_id(&$input_id); - let database_key_index = $Configuration::fn_ingredient($db).database_key_index(key); - $zalsa::accumulated_by($db.as_salsa_database(), database_key_index) - } - - $zalsa::macro_if! { $is_specifiable => - pub fn specify<$db_lt>( - $db: &$db_lt dyn $Db, - $input_id: $input_ty, - value: $output_ty, - ) { - let key = $zalsa::AsId::as_id(&$input_id); - $Configuration::fn_ingredient($db).specify_and_record( - $db, - key, - value, - ) - } - } - } - - $zalsa::attach_database($db, || { - $Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&$input_id)).clone() - }) - } - }; -} - -/// Macro for setting up a function that must intern its arguments. -#[macro_export] -macro_rules! setup_interned_fn { +macro_rules! setup_fn { ( // Attributes on the function attrs: [$(#[$attr:meta]),*], @@ -349,6 +40,12 @@ macro_rules! setup_interned_fn { // 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, + + // If true, the input needs an interner (because it has >1 argument). + needs_interner: $needs_interner: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. @@ -375,17 +72,45 @@ macro_rules! setup_interned_fn { struct $Configuration; - #[derive(Copy, Clone)] - struct $InternedData<$db_lt>( - std::ptr::NonNull<$zalsa::interned::Value<$Configuration>>, - std::marker::PhantomData<&'db $zalsa::interned::Value<$Configuration>>, - ); - static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> = $zalsa::IngredientCache::new(); - static $INTERN_CACHE: $zalsa::IngredientCache<$zalsa::interned::IngredientImpl<$Configuration>> = - $zalsa::IngredientCache::new(); + $zalsa::macro_if! { + if $needs_interner { + #[derive(Copy, Clone)] + struct $InternedData<$db_lt>( + std::ptr::NonNull<$zalsa::interned::Value<$Configuration>>, + std::marker::PhantomData<&'db $zalsa::interned::Value<$Configuration>>, + ); + + static $INTERN_CACHE: $zalsa::IngredientCache<$zalsa::interned::IngredientImpl<$Configuration>> = + $zalsa::IngredientCache::new(); + + impl $zalsa::SalsaStructInDb for $InternedData<'_> { + fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) {} + } + + impl $zalsa::interned::Configuration for $Configuration { + const DEBUG_NAME: &'static str = "Configuration"; + + type Data<$db_lt> = ($($input_ty),*); + + type Struct<$db_lt> = $InternedData<$db_lt>; + + unsafe fn struct_from_raw<'db>( + ptr: std::ptr::NonNull<$zalsa::interned::Value>, + ) -> Self::Struct<'db> { + $InternedData(ptr, std::marker::PhantomData) + } + + fn deref_struct(s: Self::Struct<'_>) -> &$zalsa::interned::Value { + unsafe { s.0.as_ref() } + } + } + } else { + type $InternedData<$db_lt> = ($($input_ty),*); + } + } impl $Configuration { fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> { @@ -395,19 +120,17 @@ macro_rules! setup_interned_fn { }) } - fn intern_ingredient( - db: &dyn $Db, - ) -> &$zalsa::interned::IngredientImpl<$Configuration> { - $INTERN_CACHE.get_or_create(db.as_salsa_database(), || { - db.add_or_lookup_jar_by_type(&$Configuration).successor(0) - }) + $zalsa::macro_if! { $needs_interner => + fn intern_ingredient( + db: &dyn $Db, + ) -> &$zalsa::interned::IngredientImpl<$Configuration> { + $INTERN_CACHE.get_or_create(db.as_salsa_database(), || { + db.add_or_lookup_jar_by_type(&$Configuration).successor(0) + }) + } } } - impl $zalsa::SalsaStructInDb for $InternedData<'_> { - fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) {} - } - impl $zalsa::function::Configuration for $Configuration { const DEBUG_NAME: &'static str = stringify!($fn_name); @@ -443,25 +166,13 @@ macro_rules! setup_interned_fn { } fn id_to_input<'db>(db: &'db Self::DbView, key: salsa::Id) -> Self::Input<'db> { - $Configuration::intern_ingredient(db).data(key).clone() - } - } - - impl $zalsa::interned::Configuration for $Configuration { - const DEBUG_NAME: &'static str = "Configuration"; - - type Data<$db_lt> = ($($input_ty),*); - - type Struct<$db_lt> = $InternedData<$db_lt>; - - unsafe fn struct_from_raw<'db>( - ptr: std::ptr::NonNull<$zalsa::interned::Value>, - ) -> Self::Struct<'db> { - $InternedData(ptr, std::marker::PhantomData) - } - - fn deref_struct(s: Self::Struct<'_>) -> &$zalsa::interned::Value { - unsafe { s.0.as_ref() } + $zalsa::macro_if! { + if $needs_interner { + $Configuration::intern_ingredient(db).data(key).clone() + } else { + $zalsa::LookupId::lookup_id(key, db.as_salsa_database()) + } + } } } @@ -470,14 +181,24 @@ macro_rules! setup_interned_fn { &self, first_index: $zalsa::IngredientIndex, ) -> Vec> { - vec![ - Box::new(<$zalsa::function::IngredientImpl<$Configuration>>::new( - first_index, - )), - Box::new(<$zalsa::interned::IngredientImpl<$Configuration>>::new( - first_index.successor(0) - )), - ] + $zalsa::macro_if! { + if $needs_interner { + vec![ + Box::new(<$zalsa::function::IngredientImpl<$Configuration>>::new( + first_index, + )), + Box::new(<$zalsa::interned::IngredientImpl<$Configuration>>::new( + first_index.successor(0) + )), + ] + } else { + vec![ + Box::new(<$zalsa::function::IngredientImpl<$Configuration>>::new( + first_index, + )), + ] + } + } } } @@ -487,15 +208,42 @@ macro_rules! setup_interned_fn { $($input_id: $input_ty,)* ) -> Vec { use salsa::plumbing as $zalsa; - let key = $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*)); + let key = $zalsa::macro_if! { + if $needs_interner { + $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*)) + } else { + $zalsa::AsId::as_id(&($($input_id),*)) + } + }; let database_key_index = $Configuration::fn_ingredient($db).database_key_index(key); $zalsa::accumulated_by($db.as_salsa_database(), database_key_index) } + + $zalsa::macro_if! { $is_specifiable => + pub fn specify<$db_lt>( + $db: &$db_lt dyn $Db, + $($input_id: $input_ty,)* + value: $output_ty, + ) { + let key = $zalsa::AsId::as_id(&($($input_id),*)); + $Configuration::fn_ingredient($db).specify_and_record( + $db, + key, + value, + ) + } + } } $zalsa::attach_database($db, || { - let key = $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*)); - $Configuration::fn_ingredient($db).fetch($db, key).clone() + $zalsa::macro_if! { + if $needs_interner { + let key = $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*)); + $Configuration::fn_ingredient($db).fetch($db, key).clone() + } else { + $Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&($($input_id),*))).clone() + } + } }) } }; diff --git a/components/salsa-macros/src/tracked_fn.rs b/components/salsa-macros/src/tracked_fn.rs index 065a5546..2e99b833 100644 --- a/components/salsa-macros/src/tracked_fn.rs +++ b/components/salsa-macros/src/tracked_fn.rs @@ -93,78 +93,38 @@ impl Macro { } } - match function_type { - FunctionType::Constant => Ok(crate::debug::dump_tokens( - fn_name, - quote![salsa::plumbing::setup_constant_fn! { - attrs: [#(#attrs),*], - vis: #vis, - fn_name: #fn_name, - db_lt: #db_lt, - Db: #db_path, - db: #db_ident, - output_ty: #output_ty, - inner_fn: #inner_fn, - cycle_recovery_fn: #cycle_recovery_fn, - cycle_recovery_strategy: #cycle_recovery_strategy, - unused_names: [ - #zalsa, - #Configuration, - #FN_CACHE, - #inner, - ] - }], - )), - FunctionType::RequiresInterning => Ok(crate::debug::dump_tokens( - fn_name, - quote![salsa::plumbing::setup_interned_fn! { - attrs: [#(#attrs),*], - vis: #vis, - fn_name: #fn_name, - db_lt: #db_lt, - Db: #db_path, - db: #db_ident, - input_ids: [#(#input_ids),*], - input_tys: [#(#input_tys),*], - output_ty: #output_ty, - inner_fn: #inner_fn, - cycle_recovery_fn: #cycle_recovery_fn, - cycle_recovery_strategy: #cycle_recovery_strategy, - unused_names: [ - #zalsa, - #Configuration, - #InternedData, - #FN_CACHE, - #INTERN_CACHE, - #inner, - ] - }], - )), - FunctionType::SalsaStruct => Ok(crate::debug::dump_tokens( - fn_name, - quote![salsa::plumbing::setup_struct_fn! { - attrs: [#(#attrs),*], - vis: #vis, - fn_name: #fn_name, - db_lt: #db_lt, - Db: #db_path, - db: #db_ident, - input_id: #(#input_ids,)* - input_ty: #(#input_tys,)* - output_ty: #output_ty, - inner_fn: #inner_fn, - cycle_recovery_fn: #cycle_recovery_fn, - cycle_recovery_strategy: #cycle_recovery_strategy, - is_specifiable: #is_specifiable, - unused_names: [ - #zalsa, - #Configuration, - #FN_CACHE, - #inner, - ] - }], - )), - } + let needs_interner = match function_type { + FunctionType::RequiresInterning => true, + FunctionType::Constant | FunctionType::SalsaStruct => false, + }; + + Ok(crate::debug::dump_tokens( + fn_name, + quote![salsa::plumbing::setup_fn! { + attrs: [#(#attrs),*], + vis: #vis, + fn_name: #fn_name, + db_lt: #db_lt, + Db: #db_path, + db: #db_ident, + input_ids: [#(#input_ids),*], + input_tys: [#(#input_tys),*], + output_ty: #output_ty, + inner_fn: #inner_fn, + cycle_recovery_fn: #cycle_recovery_fn, + cycle_recovery_strategy: #cycle_recovery_strategy, + is_specifiable: #is_specifiable, + needs_interner: #needs_interner, + unused_names: [ + #zalsa, + #Configuration, + #InternedData, + #FN_CACHE, + #INTERN_CACHE, + #inner, + ] + }], + )) } fn validity_check<'item>(&self, item: &'item syn::ItemFn) -> syn::Result> { diff --git a/src/id.rs b/src/id.rs index b0d8b6db..dc1008dd 100644 --- a/src/id.rs +++ b/src/id.rs @@ -114,16 +114,15 @@ impl FromId for Id { /// As a special case, we permit `Singleton` to be converted to an `Id`. /// This is useful for declaring functions with no arguments. -impl AsId for salsa_struct::Singleton { +impl AsId for () { fn as_id(&self) -> Id { Id::from_u32(0) } } -impl FromId for salsa_struct::Singleton { +impl FromId for () { fn from_id(id: Id) -> Self { assert_eq!(0, id.as_u32()); - salsa_struct::Singleton } } diff --git a/src/lib.rs b/src/lib.rs index b03dabe8..98fd6515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,6 @@ pub mod plumbing { pub use crate::runtime::Stamp; pub use crate::runtime::StampedValue; pub use crate::salsa_struct::SalsaStructInDb; - pub use crate::salsa_struct::Singleton; pub use crate::storage::views; pub use crate::storage::HasStorage; pub use crate::storage::IngredientCache; @@ -95,11 +94,9 @@ pub mod plumbing { pub use salsa_macro_rules::maybe_clone; pub use salsa_macro_rules::maybe_cloned_ty; pub use salsa_macro_rules::setup_accumulator_impl; - pub use salsa_macro_rules::setup_constant_fn; + pub use salsa_macro_rules::setup_fn; pub use salsa_macro_rules::setup_input_struct; - pub use salsa_macro_rules::setup_interned_fn; pub use salsa_macro_rules::setup_interned_struct; - pub use salsa_macro_rules::setup_struct_fn; pub use salsa_macro_rules::setup_tracked_struct; pub use salsa_macro_rules::unexpected_cycle_recovery; diff --git a/src/salsa_struct.rs b/src/salsa_struct.rs index 98c11834..8e2d3c8d 100644 --- a/src/salsa_struct.rs +++ b/src/salsa_struct.rs @@ -4,13 +4,6 @@ pub trait SalsaStructInDb { fn register_dependent_fn(db: &dyn Database, index: IngredientIndex); } -/// A ZST that implements [`SalsaStructInDb`] -/// -/// It is used for implementing "constant" tracked function -/// (ones that only take a database as an argument). -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct Singleton; - -impl SalsaStructInDb for Singleton { +impl SalsaStructInDb for () { fn register_dependent_fn(_db: &dyn Database, _index: IngredientIndex) {} }