diff --git a/components/salsa-macro-rules/src/lib.rs b/components/salsa-macro-rules/src/lib.rs index 24432e6..d9554ae 100644 --- a/components/salsa-macro-rules/src/lib.rs +++ b/components/salsa-macro-rules/src/lib.rs @@ -12,6 +12,7 @@ //! from a submodule is to use multiple crates, hence the existence //! of this crate. +mod macro_if; mod maybe_backdate; mod maybe_clone; mod setup_accumulator_impl; diff --git a/components/salsa-macro-rules/src/macro_if.rs b/components/salsa-macro-rules/src/macro_if.rs new file mode 100644 index 0000000..91398b4 --- /dev/null +++ b/components/salsa-macro-rules/src/macro_if.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! macro_if { + (true => $($t:tt)*) => { + $($t)* + }; + + (false => $($t:tt)*) => { + }; +} diff --git a/components/salsa-macro-rules/src/setup_input_struct.rs b/components/salsa-macro-rules/src/setup_input_struct.rs index cfeb4d8..03f749a 100644 --- a/components/salsa-macro-rules/src/setup_input_struct.rs +++ b/components/salsa-macro-rules/src/setup_input_struct.rs @@ -35,10 +35,11 @@ macro_rules! setup_input_struct { // Number of fields num_fields: $N:literal, - // Control customization: each path below either appears or doesn't. - customized: [ - $($DebugTrait:path)?, // std::fmt::Debug - ], + // 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 @@ -64,6 +65,7 @@ macro_rules! setup_input_struct { impl $zalsa_struct::Configuration for $Configuration { const DEBUG_NAME: &'static str = stringify!($Struct); const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*]; + const IS_SINGLETON: bool = $is_singleton; /// The input struct (which wraps an `Id`) type Struct = $Struct; @@ -104,13 +106,13 @@ macro_rules! setup_input_struct { } } - $( - impl $DebugTrait 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) } } - )? + } impl $zalsa::SalsaStructInDb for $Struct { fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) { @@ -163,6 +165,25 @@ macro_rules! setup_input_struct { } )* + $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, + { + $Configuration::ingredient(db.as_salsa_database()).get_singleton_input() + } + + #[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 { $zalsa::with_attached_database(|db| { diff --git a/components/salsa-macro-rules/src/setup_interned_struct.rs b/components/salsa-macro-rules/src/setup_interned_struct.rs index 3cfe60d..e69e805 100644 --- a/components/salsa-macro-rules/src/setup_interned_struct.rs +++ b/components/salsa-macro-rules/src/setup_interned_struct.rs @@ -35,10 +35,8 @@ macro_rules! setup_interned_struct { // Number of fields num_fields: $N:literal, - // Control customization: each path below either appears or doesn't. - customized: [ - $($DebugTrait:path)?, // std::fmt::Debug - ], + // 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 @@ -99,13 +97,13 @@ macro_rules! setup_interned_struct { unsafe impl Sync for $Struct<'_> {} - $( - impl $DebugTrait 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) } } - )? + } impl $zalsa::SalsaStructInDb for $Struct<'_> { fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) { diff --git a/components/salsa-macro-rules/src/setup_tracked_struct.rs b/components/salsa-macro-rules/src/setup_tracked_struct.rs index 220fa59..8e8619a 100644 --- a/components/salsa-macro-rules/src/setup_tracked_struct.rs +++ b/components/salsa-macro-rules/src/setup_tracked_struct.rs @@ -44,10 +44,8 @@ macro_rules! setup_tracked_struct { // Number of fields num_fields: $N:literal, - // Control customization: each path below either appears or doesn't. - customized: [ - $($DebugTrait:path)?, // std::fmt::Debug - ], + // 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 @@ -167,13 +165,13 @@ macro_rules! setup_tracked_struct { unsafe impl Sync for $Struct<'_> {} - $( - impl $DebugTrait 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) } } - )? + } impl<$db_lt> $Struct<$db_lt> { pub fn $new_fn<$Db>(db: &$db_lt $Db, $($field_id: $field_ty),*) -> Self diff --git a/components/salsa-macros/src/input.rs b/components/salsa-macros/src/input.rs index b5ed9fa..deb4817 100644 --- a/components/salsa-macros/src/input.rs +++ b/components/salsa-macros/src/input.rs @@ -84,8 +84,8 @@ impl Macro { let field_setter_ids = salsa_struct.field_setter_ids(); let field_options = salsa_struct.field_options(); let field_tys = salsa_struct.field_tys(); - - let DebugTrait = salsa_struct.customized_debug_trait(); + let is_singleton = self.args.singleton.is_some(); + let generate_debug_impl = salsa_struct.generate_debug_impl(); let zalsa = self.hygiene.ident("zalsa"); let zalsa_struct = self.hygiene.ident("zalsa_struct"); @@ -108,9 +108,8 @@ impl Macro { field_tys: [#(#field_tys),*], field_indices: [#(#field_indices),*], num_fields: #num_fields, - customized: [ - #DebugTrait, - ], + is_singleton: #is_singleton, + generate_debug_impl: #generate_debug_impl, unused_names: [ #zalsa, #zalsa_struct, diff --git a/components/salsa-macros/src/interned.rs b/components/salsa-macros/src/interned.rs index 95c8882..42ae970 100644 --- a/components/salsa-macros/src/interned.rs +++ b/components/salsa-macros/src/interned.rs @@ -85,8 +85,7 @@ impl Macro { let field_getter_ids = salsa_struct.field_getter_ids(); let field_options = salsa_struct.field_options(); let field_tys = salsa_struct.field_tys(); - - let DebugTrait = salsa_struct.customized_debug_trait(); + let generate_debug_impl = salsa_struct.generate_debug_impl(); let zalsa = self.hygiene.ident("zalsa"); let zalsa_struct = self.hygiene.ident("zalsa_struct"); @@ -109,9 +108,7 @@ impl Macro { field_tys: [#(#field_tys),*], field_indices: [#(#field_indices),*], num_fields: #num_fields, - customized: [ - #DebugTrait, - ], + generate_debug_impl: #generate_debug_impl, unused_names: [ #zalsa, #zalsa_struct, diff --git a/components/salsa-macros/src/salsa_struct.rs b/components/salsa-macros/src/salsa_struct.rs index 09c7b1f..db220d4 100644 --- a/components/salsa-macros/src/salsa_struct.rs +++ b/components/salsa-macros/src/salsa_struct.rs @@ -213,12 +213,8 @@ where .collect() } - pub fn customized_debug_trait(&self) -> TokenStream { - if self.args.no_debug.is_some() { - quote!() - } else { - quote!(std::fmt::Debug) - } + pub fn generate_debug_impl(&self) -> bool { + self.args.no_debug.is_none() } } diff --git a/components/salsa-macros/src/tracked_struct.rs b/components/salsa-macros/src/tracked_struct.rs index 2db5c8c..3a2d52f 100644 --- a/components/salsa-macros/src/tracked_struct.rs +++ b/components/salsa-macros/src/tracked_struct.rs @@ -81,8 +81,7 @@ impl Macro { let num_fields = salsa_struct.num_fields(); let field_options = salsa_struct.field_options(); let field_tys = salsa_struct.field_tys(); - - let DebugTrait = salsa_struct.customized_debug_trait(); + let generate_debug_impl = salsa_struct.generate_debug_impl(); let zalsa = self.hygiene.ident("zalsa"); let zalsa_struct = self.hygiene.ident("zalsa_struct"); @@ -108,9 +107,7 @@ impl Macro { id_field_indices: [#(#id_field_indices),*], field_options: [#(#field_options),*], num_fields: #num_fields, - customized: [ - #DebugTrait, - ], + generate_debug_impl: #generate_debug_impl, unused_names: [ #zalsa, #zalsa_struct, diff --git a/src/input.rs b/src/input.rs index 02a7a4f..e08cd7c 100644 --- a/src/input.rs +++ b/src/input.rs @@ -26,6 +26,7 @@ use crate::{ pub trait Configuration: Any { const DEBUG_NAME: &'static str; const FIELD_DEBUG_NAMES: &'static [&'static str]; + const IS_SINGLETON: bool; /// The input struct (which wraps an `Id`) type Struct: FromId + 'static + Send + Sync; @@ -94,6 +95,11 @@ impl IngredientImpl { } pub fn new_input(&self, fields: C::Fields, stamps: C::Stamps) -> C::Struct { + // If declared as a singleton, only allow a single instance + if C::IS_SINGLETON && self.counter.load(Ordering::Relaxed) >= 1 { + panic!("singleton struct may not be duplicated"); + } + let next_id = Id::from_u32(self.counter.fetch_add(1, Ordering::Relaxed)); let value = Value { struct_ingredient_index: self.ingredient_index, @@ -130,19 +136,12 @@ impl IngredientImpl { setter(&mut r.fields) } - /// Creates a new singleton input. - pub fn new_singleton_input(&self, _runtime: &Runtime) -> C::Struct { - // when one exists already, panic - if self.counter.load(Ordering::Relaxed) >= 1 { - panic!("singleton struct may not be duplicated"); - } - // fresh new ingredient - self.counter.store(1, Ordering::Relaxed); - C::Struct::from_id(Id::from_u32(0)) - } - - /// Get the singleton input previously created. - pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option { + /// Get the singleton input previously created (if any). + pub fn get_singleton_input(&self) -> Option { + assert!( + C::IS_SINGLETON, + "get_singleton_input invoked on a non-singleton" + ); (self.counter.load(Ordering::Relaxed) > 0).then(|| C::Struct::from_id(Id::from_u32(0))) } diff --git a/src/lib.rs b/src/lib.rs index 1a01a24..b03dabe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ pub mod plumbing { pub use crate::update::helper::Dispatch as UpdateDispatch; pub use crate::update::helper::Fallback as UpdateFallback; + pub use salsa_macro_rules::macro_if; pub use salsa_macro_rules::maybe_backdate; pub use salsa_macro_rules::maybe_clone; pub use salsa_macro_rules::maybe_cloned_ty; diff --git a/tests/panic-when-creating-tracked-struct-outside-of-tracked-fn.rs b/tests/panic-when-creating-tracked-struct-outside-of-tracked-fn.rs index 8b7b588..d59741a 100644 --- a/tests/panic-when-creating-tracked-struct-outside-of-tracked-fn.rs +++ b/tests/panic-when-creating-tracked-struct-outside-of-tracked-fn.rs @@ -1,26 +1,20 @@ //! Test that creating a tracked struct outside of a //! tracked function panics with an assert message. -#[salsa::jar(db = Db)] -struct Jar(MyTracked<'_>); - -trait Db: salsa::DbWithJar {} - #[salsa::tracked] struct MyTracked<'db> { field: u32, } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, } +#[salsa::db] impl salsa::Database for Database {} -impl Db for Database {} - #[test] #[should_panic( expected = "cannot create a tracked struct disambiguator outside of a tracked function" diff --git a/tests/singleton.rs b/tests/singleton.rs index adf339e..0e092f9 100644 --- a/tests/singleton.rs +++ b/tests/singleton.rs @@ -3,33 +3,32 @@ //! Singleton structs are created only once. Subsequent `get`s and `new`s after creation return the same `Id`. use expect_test::expect; -use salsa::DebugWithDb; mod common; use common::{HasLogger, Logger}; +use salsa::Database as _; use test_log::test; -#[salsa::jar(db = Db)] -struct Jar(MyInput); - -trait Db: salsa::DbWithJar + HasLogger {} +#[salsa::db] +trait Db: salsa::Database + HasLogger {} #[salsa::input(singleton)] struct MyInput { field: u32, - #[id] id_field: u16, } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, logger: Logger, } +#[salsa::db] impl salsa::Database for Database {} +#[salsa::db] impl Db for Database {} impl HasLogger for Database { @@ -65,9 +64,10 @@ fn twice() { #[test] fn debug() { - let db = Database::default(); - let input = MyInput::new(&db, 3, 4); - let actual = format!("{:?}", input.debug(&db)); - let expected = expect!["MyInput { [salsa id]: 0, field: 3, id_field: 4 }"]; - expected.assert_eq(&actual); + Database::default().attach(|db| { + let input = MyInput::new(db, 3, 4); + let actual = format!("{:?}", input); + let expected = expect!["MyInput { [salsa id]: 0, field: 3, id_field: 4 }"]; + expected.assert_eq(&actual); + }); } diff --git a/tests/tracked_fn_constant.rs b/tests/tracked_fn_constant.rs index e311b13..db443a7 100644 --- a/tests/tracked_fn_constant.rs +++ b/tests/tracked_fn_constant.rs @@ -2,28 +2,22 @@ //! compiles and executes successfully. #![allow(warnings)] -#[salsa::jar(db = Db)] -struct Jar(tracked_fn); - -trait Db: salsa::DbWithJar {} - #[salsa::tracked] -fn tracked_fn(db: &dyn Db) -> u32 { +fn tracked_fn(db: &dyn salsa::Database) -> u32 { 44 } #[test] fn execute() { - #[salsa::db(Jar)] + #[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, } + #[salsa::db] impl salsa::Database for Database {} - impl Db for Database {} - let mut db = Database::default(); assert_eq!(tracked_fn(&db), 44); } diff --git a/tests/tracked_fn_on_input.rs b/tests/tracked_fn_on_input.rs index 2d44794..2ea3fa6 100644 --- a/tests/tracked_fn_on_input.rs +++ b/tests/tracked_fn_on_input.rs @@ -2,33 +2,27 @@ //! compiles and executes successfully. #![allow(warnings)] -#[salsa::jar(db = Db)] -struct Jar(MyInput, tracked_fn); - -trait Db: salsa::DbWithJar {} - #[salsa::input] struct MyInput { field: u32, } #[salsa::tracked] -fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 { +fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> u32 { input.field(db) * 2 } #[test] fn execute() { - #[salsa::db(Jar)] + #[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, } + #[salsa::db] impl salsa::Database for Database {} - impl Db for Database {} - let mut db = Database::default(); let input = MyInput::new(&db, 22); assert_eq!(tracked_fn(&db, input), 44); diff --git a/tests/tracked_fn_on_tracked.rs b/tests/tracked_fn_on_tracked.rs index 172f171..06019c3 100644 --- a/tests/tracked_fn_on_tracked.rs +++ b/tests/tracked_fn_on_tracked.rs @@ -1,11 +1,6 @@ //! Test that a `tracked` fn on a `salsa::input` //! compiles and executes successfully. -#[salsa::jar(db = Db)] -struct Jar(MyInput, MyTracked<'_>, tracked_fn); - -trait Db: salsa::DbWithJar {} - #[salsa::input] struct MyInput { field: u32, @@ -17,20 +12,19 @@ struct MyTracked<'db> { } #[salsa::tracked] -fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { +fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'db> { MyTracked::new(db, input.field(db) * 2) } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, } +#[salsa::db] impl salsa::Database for Database {} -impl Db for Database {} - #[test] fn execute() { let db = Database::default(); diff --git a/tests/tracked_fn_on_tracked_specify.rs b/tests/tracked_fn_on_tracked_specify.rs index 9744e20..9a76b49 100644 --- a/tests/tracked_fn_on_tracked_specify.rs +++ b/tests/tracked_fn_on_tracked_specify.rs @@ -2,11 +2,6 @@ //! compiles and executes successfully. #![allow(warnings)] -#[salsa::jar(db = Db)] -struct Jar(MyInput, MyTracked<'_>, tracked_fn, tracked_fn_extra); - -trait Db: salsa::DbWithJar {} - #[salsa::input] struct MyInput { field: u32, @@ -18,7 +13,7 @@ struct MyTracked<'db> { } #[salsa::tracked] -fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { +fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'db> { let t = MyTracked::new(db, input.field(db) * 2); if input.field(db) != 0 { tracked_fn_extra::specify(db, t, 2222); @@ -26,21 +21,20 @@ fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { t } -#[salsa::tracked(jar = Jar, specify)] -fn tracked_fn_extra<'db>(_db: &'db dyn Db, _input: MyTracked<'db>) -> u32 { +#[salsa::tracked(specify)] +fn tracked_fn_extra<'db>(_db: &'db dyn salsa::Database, _input: MyTracked<'db>) -> u32 { 0 } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, } +#[salsa::db] impl salsa::Database for Database {} -impl Db for Database {} - #[test] fn execute_when_specified() { let mut db = Database::default(); diff --git a/tests/tracked_fn_read_own_entity.rs b/tests/tracked_fn_read_own_entity.rs index 0aa354d..e269f9f 100644 --- a/tests/tracked_fn_read_own_entity.rs +++ b/tests/tracked_fn_read_own_entity.rs @@ -4,12 +4,11 @@ use expect_test::expect; mod common; use common::{HasLogger, Logger}; +use salsa::Setter; use test_log::test; -#[salsa::jar(db = Db)] -struct Jar(MyInput, MyTracked<'_>, final_result, intermediate_result); - -trait Db: salsa::DbWithJar + HasLogger {} +#[salsa::db] +trait Db: salsa::Database + HasLogger {} #[salsa::input] struct MyInput { @@ -35,15 +34,17 @@ fn intermediate_result<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { tracked } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, logger: Logger, } +#[salsa::db] impl salsa::Database for Database {} +#[salsa::db] impl Db for Database {} impl HasLogger for Database { diff --git a/tests/tracked_fn_read_own_specify.rs b/tests/tracked_fn_read_own_specify.rs index 71b439e..bbfa836 100644 --- a/tests/tracked_fn_read_own_specify.rs +++ b/tests/tracked_fn_read_own_specify.rs @@ -3,10 +3,8 @@ use salsa::{Database as SalsaDatabase, DebugWithDb}; mod common; use common::{HasLogger, Logger}; -#[salsa::jar(db = Db)] -struct Jar(MyInput, MyTracked<'_>, tracked_fn, tracked_fn_extra); - -trait Db: salsa::DbWithJar + HasLogger {} +#[salsa::db] +trait Db: salsa::Database + HasLogger {} #[salsa::input] struct MyInput { @@ -20,27 +18,29 @@ struct MyTracked<'db> { #[salsa::tracked] fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> u32 { - db.push_log(format!("tracked_fn({:?})", input.debug(db))); + db.push_log(format!("tracked_fn({input:?})")); let t = MyTracked::new(db, input.field(db) * 2); tracked_fn_extra::specify(db, t, 2222); tracked_fn_extra(db, t) } -#[salsa::tracked(jar = Jar, specify)] +#[salsa::tracked(specify)] fn tracked_fn_extra<'db>(db: &dyn Db, input: MyTracked<'db>) -> u32 { - db.push_log(format!("tracked_fn_extra({:?})", input.debug(db))); + db.push_log(format!("tracked_fn_extra({input:?})")); 0 } -#[salsa::db(Jar)] +#[salsa::db] #[derive(Default)] struct Database { storage: salsa::Storage, logger: Logger, } +#[salsa::db] impl salsa::Database for Database {} +#[salsa::db] impl Db for Database {} impl HasLogger for Database {