diff --git a/src/lib.rs b/src/lib.rs index 12c74e8dc..c64e61618 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,7 +332,7 @@ where /// Trait implements by all of the "special types" associated with /// each of your queries. -pub trait Query: Debug + Default + Sized + 'static { +pub trait Query: Debug + Default + Sized { /// Type that you you give as a parameter -- for queries with zero /// or more than one input, this will be a tuple. type Key: Clone + Debug + Hash + Eq; @@ -540,26 +540,28 @@ where #[macro_export] macro_rules! query_group { ( - $(#[$attr:meta])* $v:vis trait $name:ident { $($t:tt)* } + $(#[$attr:meta])* $v:vis trait $name:ident $(<$lt:lifetime>)* { $($t:tt)* } ) => { $crate::query_group! { attr[$(#[$attr])*]; headers[$v, $name, ]; tokens[{ $($t)* }]; + lifetime[$($lt)*]; } }; ( - $(#[$attr:meta])* $v:vis trait $name:ident : $($t:tt)* + $(#[$attr:meta])* $v:vis trait $name:ident $(<$lt:lifetime>)* : $($t:tt)* ) => { $crate::query_group! { attr[$(#[$attr])*]; headers[$v, $name, ]; tokens[$($t)*]; + lifetime[$($lt)*]; } }; - // Base case: found the trait body + // Base case without lifetime: found the trait body ( attr[$($trait_attr:tt)*]; headers[$v:vis, $query_trait:ident, $($header:tt)*]; @@ -573,6 +575,7 @@ macro_rules! query_group { } )* }]; + lifetime[]; ) => { $($trait_attr)* $v trait $query_trait: $($crate::plumbing::GetQueryTable<$QueryType> +)* $($header)* { $( @@ -605,6 +608,66 @@ macro_rules! query_group { db_trait($query_trait); query_type($QueryType); key($($key_name: $key_ty),*); + lifetime(); + ] + } + )* + }; + + // Base case with lifetime: found the trait body + ( + attr[$($trait_attr:tt)*]; + headers[$v:vis, $query_trait:ident, $($header:tt)*]; + tokens[{ + $( + $(#[$method_attr:meta])* + fn $method_name:ident($($key_name:ident: $key_ty:ty),* $(,)*) -> $value_ty:ty { + type $QueryType:ident; + $(storage $storage:tt;)* // FIXME(rust-lang/rust#48075) should be `?` + $(use fn $fn_path:path;)* // FIXME(rust-lang/rust#48075) should be `?` + } + )* + }]; + lifetime[$lt:lifetime]; + ) => { + $($trait_attr)* $v trait $query_trait<$lt>: $($crate::plumbing::GetQueryTable<$QueryType<$lt>> +)* $($header)* { + $( + $(#[$method_attr])* + fn $method_name(&self, $($key_name: $key_ty),*) -> $value_ty { + >>::get_query_table(self) + .get(($($key_name),*)) + } + )* + } + + $( + #[derive(Default)] + $v struct $QueryType<$lt>(std::marker::PhantomData<&$lt ()>); + + impl std::fmt::Debug for $QueryType<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", stringify!($QueryType)) + } + } + + impl<$lt, DB> $crate::Query for $QueryType<$lt> + where + DB: $query_trait<$lt>, + { + type Key = ($($key_ty),*); + type Value = $value_ty; + type Storage = $crate::query_group! { @storage_ty[DB, Self, $($storage)*] }; + } + + $crate::query_group! { + @query_fn[ + storage($($storage)*); + method_name($method_name); + fn_path($($fn_path)*); + db_trait($query_trait<$lt>); + query_type($QueryType<$lt>); + key($($key_name: $key_ty),*); + lifetime($lt); ] } )* @@ -664,9 +727,10 @@ macro_rules! query_group { db_trait($DbTrait:path); query_type($QueryType:ty); key($key_name:ident: $key_ty:ty); + lifetime($($lts:lifetime)*); ] ) => { - impl $crate::plumbing::QueryFunction for $QueryType + impl<$($lts,)* DB> $crate::plumbing::QueryFunction for $QueryType where DB: $DbTrait { fn execute(db: &DB, $key_name: >::Key) @@ -687,9 +751,10 @@ macro_rules! query_group { db_trait($DbTrait:path); query_type($QueryType:ty); key($($key_name:ident: $key_ty:ty),*); + lifetime($($lts:lifetime)*); ] ) => { - impl $crate::plumbing::QueryFunction for $QueryType + impl<$($lts,)* DB> $crate::plumbing::QueryFunction for $QueryType where DB: $DbTrait { fn execute(db: &DB, ($($key_name),*): >::Key) @@ -706,11 +771,13 @@ macro_rules! query_group { attr[$($attr:tt)*]; headers[$($headers:tt)*]; tokens[$token:tt $($tokens:tt)*]; + $($rest:tt)* ) => { $crate::query_group! { attr[$($attr)*]; headers[$($headers)* $token]; tokens[$($tokens)*]; + $($rest)* } }; @@ -743,7 +810,7 @@ macro_rules! query_group { ( @storage_ty[$DB:ident, $Self:ident, input] ) => { - $crate::plumbing::InputStorage + $crate::plumbing::InputStorage<$DB, $Self> }; ( @@ -782,6 +849,7 @@ macro_rules! query_group { /// [hw]: https://github.com/salsa-rs/salsa/tree/master/examples/hello_world #[macro_export] macro_rules! database_storage { + // Variant without lifetime annotation. ( $(#[$attr:meta])* $v:vis struct $Storage:ident for $Database:ty { @@ -793,10 +861,67 @@ macro_rules! database_storage { } )* } + ) => { + $crate::database_storage! { + @STRUCT_IMPL + $(#[$attr])* + $v struct $Storage [] for $Database { + $( + impl $TraitName { + $( + fn $query_method() for $QueryType; + )* + } + )* + } + } + }; + + // Variant with lifetime annotation. + ( + $(#[$attr:meta])* + $v:vis struct $Storage:ident<$($lt:lifetime),*> for $Database:ty { + $( + impl $TraitName:path { + $( + fn $query_method:ident() for $QueryType:path; + )* + } + )* + } + ) => { + $crate::database_storage! { + @STRUCT_IMPL + $(#[$attr])* + $v struct $Storage [$($lt),*] for $Database { + $( + impl $TraitName { + $( + fn $query_method() for $QueryType; + )* + } + )* + } + } + }; + + // Main implementation of the macro. Not intended to be invoked by the user. + ( + @STRUCT_IMPL + $(#[$attr:meta])* + $v:vis struct $Storage:ident [$($lts:tt)*] for $Database:ty { + $( + impl $TraitName:path { + $( + fn $query_method:ident() for $QueryType:path; + )* + } + )* + } ) => { #[derive(Default)] $(#[$attr])* - $v struct $Storage { + $v struct $Storage<$($lts)*> { $( $( $query_method: <$QueryType as $crate::Query<$Database>>::Storage, @@ -811,12 +936,13 @@ macro_rules! database_storage { /// know any way to hide this with hygiene, so use `__` /// instead. #[derive(Clone, Debug, PartialEq, Eq, Hash)] - $v struct __SalsaQueryDescriptor { - kind: __SalsaQueryDescriptorKind + $v struct __SalsaQueryDescriptor<$($lts)*> { + kind: __SalsaQueryDescriptorKind<$($lts)*> } + #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq, Eq, Hash)] - enum __SalsaQueryDescriptorKind { + enum __SalsaQueryDescriptorKind<$($lts)*> { $( $( $query_method(<$QueryType as $crate::Query<$Database>>::Key), @@ -824,12 +950,12 @@ macro_rules! database_storage { )* } - impl $crate::plumbing::DatabaseStorageTypes for $Database { - type QueryDescriptor = __SalsaQueryDescriptor; - type DatabaseStorage = $Storage; + impl<$($lts)*> $crate::plumbing::DatabaseStorageTypes for $Database { + type QueryDescriptor = __SalsaQueryDescriptor<$($lts)*>; + type DatabaseStorage = $Storage<$($lts)*>; } - impl $crate::plumbing::DatabaseOps for $Database { + impl<$($lts)*> $crate::plumbing::DatabaseOps for $Database { fn for_each_query( &self, mut op: impl FnMut(&dyn $crate::plumbing::QueryStorageMassOps), @@ -844,7 +970,7 @@ macro_rules! database_storage { } } - impl $crate::plumbing::QueryDescriptor<$Database> for __SalsaQueryDescriptor { + impl<$($lts)*> $crate::plumbing::QueryDescriptor<$Database> for __SalsaQueryDescriptor<$($lts)*> { fn maybe_changed_since( &self, db: &$Database, @@ -870,44 +996,114 @@ macro_rules! database_storage { } } - $( - impl $TraitName for $Database { } + $crate::database_storage! { + @TRAIT_IMPL [$($lts)*] $Database; + $( + impl $TraitName { + $( + fn $query_method() for $QueryType; + )* + } + )* + } + }; + // Recursive formulation of trait implementation (induction step). + ( + @TRAIT_IMPL [$($lts:tt)*] $Database:ty; + impl $TraitName:path { $( - impl $crate::plumbing::GetQueryTable<$QueryType> for $Database { - fn get_query_table( - db: &Self, - ) -> $crate::QueryTable<'_, Self, $QueryType> { - $crate::QueryTable::new( - db, - &$crate::Database::salsa_runtime(db) - .storage() - .$query_method, - ) - } + fn $query_method:ident() for $QueryType:path; + )* + } + $( + impl $TailTraitName:path { + $( + fn $tail_query_method:ident() for $TailQueryType:path; + )* + } + )* + ) => { + impl<$($lts)*> $TraitName for $Database { } - fn get_query_table_mut( - db: &mut Self, - ) -> $crate::QueryTableMut<'_, Self, $QueryType> { - let db = &*db; - $crate::QueryTableMut::new( - db, - &$crate::Database::salsa_runtime(db) - .storage() - .$query_method, - ) - } + $crate::database_storage! { + @QUERY_IMPL [$($lts)*] $Database; + $( + fn $query_method() for $QueryType; + )* + } - fn descriptor( - db: &Self, - key: <$QueryType as $crate::Query>::Key, - ) -> ::QueryDescriptor { - __SalsaQueryDescriptor { - kind: __SalsaQueryDescriptorKind::$query_method(key), - } - } + $crate::database_storage! { + @TRAIT_IMPL [$($lts)*] $Database; + $( + impl $TailTraitName { + $( + fn $tail_query_method() for $TailQueryType; + )* } )* + } + }; + + // Recursive formulation of trait implementation (base case). + ( + @TRAIT_IMPL [$($lts:tt)*] $Database:ty; + ) => { + }; + + // Recursive formulation of per-trait query implementation (induction step). + ( + @QUERY_IMPL [$($lts:tt)*] $Database:ty; + fn $query_method:ident() for $QueryType:path; + $( + fn $tail_query_method:ident() for $TailQueryType:path; )* + ) => { + impl<$($lts)*> $crate::plumbing::GetQueryTable<$QueryType> for $Database { + fn get_query_table( + db: &Self, + ) -> $crate::QueryTable<'_, Self, $QueryType> { + $crate::QueryTable::new( + db, + &$crate::Database::salsa_runtime(db) + .storage() + .$query_method, + ) + } + + fn get_query_table_mut( + db: &mut Self, + ) -> $crate::QueryTableMut<'_, Self, $QueryType> { + let db = &*db; + $crate::QueryTableMut::new( + db, + &$crate::Database::salsa_runtime(db) + .storage() + .$query_method, + ) + } + + fn descriptor( + _db: &Self, + key: <$QueryType as $crate::Query>::Key, + ) -> ::QueryDescriptor { + __SalsaQueryDescriptor { + kind: __SalsaQueryDescriptorKind::$query_method(key), + } + } + } + + $crate::database_storage! { + @QUERY_IMPL [$($lts)*] $Database; + $( + fn $tail_query_method() for $TailQueryType; + )* + } + }; + + // Recursive formulation of per-trait query implementation (base case). + ( + @QUERY_IMPL [$($lts:tt)*] $Database:ty; + ) => { }; } diff --git a/tests/refs.rs b/tests/refs.rs new file mode 100644 index 000000000..3980defc1 --- /dev/null +++ b/tests/refs.rs @@ -0,0 +1,77 @@ +#![cfg(test)] + +pub struct DatabaseImpl<'a> { + runtime: salsa::Runtime>, + input: &'a str, +} + +impl<'a> DatabaseImpl<'a> { + pub fn new(input: &'a str) -> DatabaseImpl<'a> { + DatabaseImpl { + runtime: Default::default(), + input, + } + } +} + +pub trait DatabaseWithInput<'a>: salsa::Database { + fn input(&self) -> &'a str; +} + +impl<'a> salsa::Database for DatabaseImpl<'a> { + fn salsa_runtime(&self) -> &salsa::Runtime> { + &self.runtime + } +} + +impl<'a> DatabaseWithInput<'a> for DatabaseImpl<'a> { + fn input(&self) -> &'a str { + self.input + } +} + +salsa::database_storage! { + pub struct DatabaseImplStorage<'a> for DatabaseImpl<'a> { + impl Database<'a> { + fn unmodified() for Unmodified<'a>; + fn uppercase() for Uppercase<'a>; + } + } +} + +salsa::query_group! { + trait Database<'a>: DatabaseWithInput<'a> { + fn unmodified() -> &'a str { + type Unmodified; + storage volatile; + } + + fn uppercase() -> String { + type Uppercase; + } + } +} + +fn unmodified<'a>(db: &impl Database<'a>) -> &'a str { + db.input() +} + +fn uppercase<'a>(db: &impl Database<'a>) -> String { + db.unmodified().to_uppercase() +} + +#[test] +fn static_ref() { + let input: &'static str = "Hello Salsa"; + let db = DatabaseImpl::new(input); + assert_eq!(db.unmodified(), "Hello Salsa"); + assert_eq!(db.uppercase(), "HELLO SALSA"); +} + +#[test] +fn local_ref() { + let input = String::from("Hello Salsa"); + let db = DatabaseImpl::new(&input); + assert_eq!(db.unmodified(), "Hello Salsa"); + assert_eq!(db.uppercase(), "HELLO SALSA"); +}