diff --git a/components/salsa-macros/src/db.rs b/components/salsa-macros/src/db.rs index a7c08e63..091175d7 100644 --- a/components/salsa-macros/src/db.rs +++ b/components/salsa-macros/src/db.rs @@ -108,7 +108,8 @@ impl DbMacro { fn add_salsa_view_method(&self, input: &mut syn::ItemTrait) -> syn::Result<()> { input.items.push(parse_quote! { - fn __salsa_add_view__(&self); + #[doc(hidden)] + fn zalsa_add_view(&self); }); Ok(()) } @@ -121,8 +122,10 @@ impl DbMacro { )); }; input.items.push(parse_quote! { - fn __salsa_add_view__(&self) { - salsa::storage::views(self).add::(|t| t, |t| t); + #[doc(hidden)] + #[allow(uncommon_codepoins)] + fn zalsa_add_view(&self) { + salsa::storage::views(self).add::(|t| t, |t| t); } }); Ok(()) diff --git a/components/salsa-macros/src/jar.rs b/components/salsa-macros/src/jar.rs deleted file mode 100644 index c1f70289..00000000 --- a/components/salsa-macros/src/jar.rs +++ /dev/null @@ -1,184 +0,0 @@ -use proc_macro2::extra::DelimSpan; -use proc_macro2::{Delimiter, Group, Literal, TokenStream}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::visit_mut::VisitMut; -use syn::{Field, FieldsUnnamed, Ident, ItemStruct, Path, Token}; - -use crate::options::Options; -use crate::xform::ChangeLt; - -// Source: -// -// #[salsa::jar(db = Jar0Db)] -// pub struct Jar0(Entity0, Ty0, EntityComponent0, my_func); - -pub(crate) fn jar( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let options = syn::parse_macro_input!(args as Args); - let db_path = match options.db_path { - Some(v) => v, - None => panic!("no `db` specified"), - }; - let input = syn::parse_macro_input!(input as ItemStruct); - jar_struct_and_friends(&db_path, &input).into() -} - -type Args = Options; - -struct Jar; - -impl crate::options::AllowedOptions for Jar { - const RETURN_REF: bool = false; - - const SPECIFY: bool = false; - - const NO_EQ: bool = false; - - const SINGLETON: bool = false; - - const JAR: bool = false; - - const DATA: bool = false; - - const DB: bool = true; - - const RECOVERY_FN: bool = false; - - const LRU: bool = false; - - const CONSTRUCTOR_NAME: bool = false; -} - -pub(crate) fn jar_struct_and_friends( - jar_trait: &Path, - input: &ItemStruct, -) -> proc_macro2::TokenStream { - let output_struct = jar_struct(input); - - let jar_struct = &input.ident; - - // for each field, we need to generate an impl of `HasIngredientsFor` - let has_ingredients_for_impls: Vec<_> = input - .fields - .iter() - .zip(0..) - .map(|(field, index)| has_ingredients_for_impl(jar_struct, field, index)) - .collect(); - - let jar_impl = jar_impl(jar_struct, jar_trait, input); - - quote! { - #output_struct - - #(#has_ingredients_for_impls)* - - #jar_impl - } -} - -pub(crate) fn has_ingredients_for_impl( - jar_struct: &Ident, - field: &Field, - index: u32, -) -> proc_macro2::TokenStream { - let field_ty = &field.ty; - let index = Literal::u32_unsuffixed(index); - quote! { - impl salsa::storage::HasIngredientsFor<#field_ty> for #jar_struct { - fn ingredient(&self) -> &<#field_ty as salsa::storage::IngredientsFor>::Ingredients { - &self.#index - } - - fn ingredient_mut(&mut self) -> &mut <#field_ty as salsa::storage::IngredientsFor>::Ingredients { - &mut self.#index - } - } - } -} - -pub(crate) fn jar_impl( - jar_struct: &Ident, - jar_trait: &Path, - input: &ItemStruct, -) -> proc_macro2::TokenStream { - let field_tys: Vec<_> = input.fields.iter().map(|f| &f.ty).collect(); - let field_var_names: &Vec<_> = &input - .fields - .iter() - .zip(0..) - .map(|(f, i)| syn::LitInt::new(&format!("{}", i), f.ty.span())) - .collect(); - // ANCHOR: init_jar - quote! { - unsafe impl salsa::jar::Jar for #jar_struct { - type DynDb = dyn #jar_trait; - - unsafe fn init_jar(place: *mut Self, routes: &mut salsa::routes::Routes) - where - DB: salsa::storage::JarFromJars + salsa::storage::DbWithJar, - { - #( - unsafe { - std::ptr::addr_of_mut!((*place).#field_var_names) - .write(<#field_tys as salsa::storage::IngredientsFor>::create_ingredients(routes)); - } - )* - } - } - } - // ANCHOR_END: init_jar -} - -pub(crate) fn jar_struct(input: &ItemStruct) -> ItemStruct { - let mut output_struct = input.clone(); - output_struct.fields = generate_fields(input).into(); - if output_struct.semi_token.is_none() { - output_struct.semi_token = Some(Token![;](input.struct_token.span)); - } - output_struct -} - -fn generate_fields(input: &ItemStruct) -> FieldsUnnamed { - // Generate the - let mut output_fields = Punctuated::new(); - for field in input.fields.iter() { - let mut field = field.clone(); - - // Convert to anonymous fields - field.ident = None; - - // Convert ty to reference static and not `'_` - ChangeLt::elided_to_static().visit_type_mut(&mut field.ty); - - let field_ty = &field.ty; - field.ty = - syn::parse2(quote!(< #field_ty as salsa::storage::IngredientsFor >::Ingredients)) - .unwrap(); - - output_fields.push(field); - } - - let paren_token = match &input.fields { - syn::Fields::Named(f) => syn::token::Paren { - span: f.brace_token.span, - }, - syn::Fields::Unnamed(f) => f.paren_token, - syn::Fields::Unit => syn::token::Paren { - span: to_delim_span(input), - }, - }; - - FieldsUnnamed { - paren_token, - unnamed: output_fields, - } -} - -fn to_delim_span(s: &impl Spanned) -> DelimSpan { - let mut group = Group::new(Delimiter::None, TokenStream::new()); - group.set_span(s.span()); - group.delim_span() -} diff --git a/components/salsa-macros/src/lib.rs b/components/salsa-macros/src/lib.rs index d87412a8..af041970 100644 --- a/components/salsa-macros/src/lib.rs +++ b/components/salsa-macros/src/lib.rs @@ -46,7 +46,6 @@ mod debug; mod debug_with_db; mod input; mod interned; -mod jar; mod options; mod salsa_struct; mod tracked; @@ -60,21 +59,11 @@ pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream { accumulator::accumulator(args, input) } -#[proc_macro_attribute] -pub fn jar(args: TokenStream, input: TokenStream) -> TokenStream { - jar::jar(args, input) -} - #[proc_macro_attribute] pub fn db(args: TokenStream, input: TokenStream) -> TokenStream { db::db(args, input) } -#[proc_macro_attribute] -pub fn db_view(args: TokenStream, input: TokenStream) -> TokenStream { - db_view::db_view(args, input) -} - #[proc_macro_attribute] pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream { interned::interned(args, input) diff --git a/src/accumulator.rs b/src/accumulator.rs index 6bc4db89..058dc822 100644 --- a/src/accumulator.rs +++ b/src/accumulator.rs @@ -19,7 +19,7 @@ use crate::{ pub trait Accumulator: Jar { const DEBUG_NAME: &'static str; - type Data: Clone + Debug; + type Data: Clone + Debug + Send + Sync; } pub struct AccumulatorJar { diff --git a/src/database.rs b/src/database.rs index 67399b60..99443efe 100644 --- a/src/database.rs +++ b/src/database.rs @@ -5,6 +5,7 @@ use parking_lot::Mutex; use crate::{storage::DatabaseGen, Durability, Event}; +#[salsa_macros::db] pub trait Database: DatabaseGen { /// This function is invoked at key points in the salsa /// runtime. It permits the database to be customized and to @@ -37,21 +38,6 @@ pub trait Database: DatabaseGen { } } -/// The database view trait allows you to define your own views on the database. -/// This lets you add extra context beyond what is stored in the salsa database itself. -pub trait DatabaseView: Database { - /// Registers this database view in the database. - /// This is normally invoked automatically by tracked functions that require a given view. - fn add_view_to_db(&self); -} - -impl DatabaseView for Db { - fn add_view_to_db(&self) { - let upcasts = self.views_of_self(); - upcasts.add::(|t| t, |t| t); - } -} - /// Indicates a database that also supports parallel query /// evaluation. All of Salsa's base query support is capable of /// parallel execution, but for it to work, your query key/value types @@ -194,10 +180,6 @@ impl AttachedDatabase { } } -unsafe impl Send for AttachedDatabase where dyn Database: Sync {} - -unsafe impl Sync for AttachedDatabase where dyn Database: Sync {} - struct AttachedDb<'db, Db: ?Sized + Database> { db: &'db Db, previous: AttachedDatabase, diff --git a/src/function.rs b/src/function.rs index 60e890ca..5293ae58 100644 --- a/src/function.rs +++ b/src/function.rs @@ -42,10 +42,10 @@ pub trait Configuration: 'static { type SalsaStruct<'db>: SalsaStructInDb; /// The input to the function - type Input<'db>; + type Input<'db>: Send + Sync; /// The value computed by the function. - type Value<'db>: fmt::Debug; + type Value<'db>: fmt::Debug + Send + Sync; /// Determines whether this function can recover from being a participant in a cycle /// (and, if so, how). diff --git a/src/ingredient.rs b/src/ingredient.rs index 46fa381a..0032526c 100644 --- a/src/ingredient.rs +++ b/src/ingredient.rs @@ -4,8 +4,8 @@ use std::{ }; use crate::{ - cycle::CycleRecoveryStrategy, key::DependencyIndex, runtime::local_state::QueryOrigin, - storage::IngredientIndex, Database, DatabaseKeyIndex, Id, + cycle::CycleRecoveryStrategy, runtime::local_state::QueryOrigin, storage::IngredientIndex, + Database, DatabaseKeyIndex, Id, }; use super::Revision; @@ -18,7 +18,7 @@ pub trait Jar: Any { fn create_ingredients(&self, first_index: IngredientIndex) -> Vec>; } -pub trait Ingredient: Any + std::fmt::Debug { +pub trait Ingredient: Any + std::fmt::Debug + Send + Sync { /// Has the value for `input` in this ingredient changed after `revision`? fn maybe_changed_after<'db>( &'db self, diff --git a/src/input.rs b/src/input.rs index 162f4b04..f85873f3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,35 +1,31 @@ use std::{ + any::Any, fmt, sync::atomic::{AtomicU32, Ordering}, }; use crate::{ cycle::CycleRecoveryStrategy, - id::FromId, + id::{AsId, FromId}, ingredient::{fmt_index, Ingredient, IngredientRequiresReset}, - key::{DatabaseKeyIndex, DependencyIndex}, + key::DatabaseKeyIndex, runtime::{local_state::QueryOrigin, Runtime}, storage::IngredientIndex, Database, Revision, }; -pub trait InputId: FromId + 'static {} -impl InputId for T {} +pub trait Configuration: Any { + type Id: FromId + 'static + Send + Sync; +} -pub struct InputIngredient -where - Id: InputId, -{ +pub struct InputIngredient { ingredient_index: IngredientIndex, counter: AtomicU32, debug_name: &'static str, - _phantom: std::marker::PhantomData, + _phantom: std::marker::PhantomData, } -impl InputIngredient -where - Id: InputId, -{ +impl InputIngredient { pub fn new(index: IngredientIndex, debug_name: &'static str) -> Self { Self { ingredient_index: index, @@ -39,37 +35,34 @@ where } } - pub fn database_key_index(&self, id: Id) -> DatabaseKeyIndex { + pub fn database_key_index(&self, id: C::Id) -> DatabaseKeyIndex { DatabaseKeyIndex { ingredient_index: self.ingredient_index, key_index: id.as_id(), } } - pub fn new_input(&self, _runtime: &Runtime) -> Id { + pub fn new_input(&self, _runtime: &Runtime) -> C::Id { let next_id = self.counter.fetch_add(1, Ordering::Relaxed); - Id::from_id(crate::Id::from_u32(next_id)) + C::Id::from_id(crate::Id::from_u32(next_id)) } - pub fn new_singleton_input(&self, _runtime: &Runtime) -> Id { + pub fn new_singleton_input(&self, _runtime: &Runtime) -> C::Id { // 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); - Id::from_id(crate::Id::from_u32(0)) + C::Id::from_id(crate::Id::from_u32(0)) } - pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option { - (self.counter.load(Ordering::Relaxed) > 0).then(|| Id::from_id(crate::Id::from_u32(0))) + pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option { + (self.counter.load(Ordering::Relaxed) > 0).then(|| C::Id::from_id(crate::Id::from_u32(0))) } } -impl Ingredient for InputIngredient -where - Id: InputId, -{ +impl Ingredient for InputIngredient { fn ingredient_index(&self) -> IngredientIndex { self.ingredient_index } @@ -132,17 +125,11 @@ where } } -impl IngredientRequiresReset for InputIngredient -where - Id: InputId, -{ +impl IngredientRequiresReset for InputIngredient { const RESET_ON_NEW_REVISION: bool = false; } -impl std::fmt::Debug for InputIngredient -where - Id: InputId, -{ +impl std::fmt::Debug for InputIngredient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct(std::any::type_name::()) .field("index", &self.ingredient_index) diff --git a/src/input_field.rs b/src/input_field.rs index 44554034..8e9e0110 100644 --- a/src/input_field.rs +++ b/src/input_field.rs @@ -1,7 +1,7 @@ use crate::cycle::CycleRecoveryStrategy; use crate::id::{AsId, FromId}; use crate::ingredient::{fmt_index, Ingredient, IngredientRequiresReset}; -use crate::key::DependencyIndex; +use crate::input::Configuration; use crate::plumbing::transmute_lifetime; use crate::runtime::local_state::QueryOrigin; use crate::runtime::StampedValue; @@ -10,7 +10,9 @@ use crate::{Database, DatabaseKeyIndex, Durability, Id, Revision, Runtime}; use dashmap::mapref::entry::Entry; use dashmap::DashMap; use std::fmt; -use std::hash::Hash; + +pub trait InputFieldData: Send + Sync + 'static {} +impl InputFieldData for T {} /// Ingredient used to represent the fields of a `#[salsa::input]`. /// @@ -21,16 +23,16 @@ use std::hash::Hash; /// a shared reference, so some locking is required. /// Altogether this makes the implementation somewhat simpler than tracked /// structs. -pub struct InputFieldIngredient { +pub struct InputFieldIngredient { index: IngredientIndex, - map: DashMap>>, + map: DashMap>>, debug_name: &'static str, } -impl InputFieldIngredient +impl InputFieldIngredient where - K: Eq + Hash + AsId + 'static, - F: 'static, + C: Configuration, + F: InputFieldData, { pub fn new(index: IngredientIndex, debug_name: &'static str) -> Self { Self { @@ -43,7 +45,7 @@ where pub fn store_mut( &mut self, runtime: &Runtime, - key: K, + key: C::Id, value: F, durability: Durability, ) -> Option { @@ -62,7 +64,7 @@ where /// Set the field of a new input. /// /// This function panics if the field has ever been set before. - pub fn store_new(&self, runtime: &Runtime, key: K, value: F, durability: Durability) { + pub fn store_new(&self, runtime: &Runtime, key: C::Id, value: F, durability: Durability) { let revision = runtime.current_revision(); let stamped_value = Box::new(StampedValue { value, @@ -80,7 +82,7 @@ where } } - pub fn fetch<'db>(&'db self, runtime: &'db Runtime, key: K) -> &F { + pub fn fetch<'db>(&'db self, runtime: &'db Runtime, key: C::Id) -> &F { let StampedValue { value, durability, @@ -100,7 +102,7 @@ where unsafe { transmute_lifetime(self, value) } } - fn database_key_index(&self, key: K) -> DatabaseKeyIndex { + fn database_key_index(&self, key: C::Id) -> DatabaseKeyIndex { DatabaseKeyIndex { ingredient_index: self.index, key_index: key.as_id(), @@ -108,10 +110,10 @@ where } } -impl Ingredient for InputFieldIngredient +impl Ingredient for InputFieldIngredient where - K: FromId + 'static, - F: 'static, + C: Configuration, + F: InputFieldData, { fn ingredient_index(&self) -> IngredientIndex { self.index @@ -127,7 +129,7 @@ where input: Option, revision: Revision, ) -> bool { - let key = K::from_id(input.unwrap()); + let key = C::Id::from_id(input.unwrap()); self.map.get(&key).unwrap().changed_at > revision } @@ -164,16 +166,18 @@ where } } -impl IngredientRequiresReset for InputFieldIngredient +impl IngredientRequiresReset for InputFieldIngredient where - K: AsId, + C: Configuration, + F: InputFieldData, { const RESET_ON_NEW_REVISION: bool = false; } -impl std::fmt::Debug for InputFieldIngredient +impl std::fmt::Debug for InputFieldIngredient where - K: AsId, + C: Configuration, + F: InputFieldData, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct(std::any::type_name::()) diff --git a/src/interned.rs b/src/interned.rs index 04760c1e..741b5375 100644 --- a/src/interned.rs +++ b/src/interned.rs @@ -21,8 +21,10 @@ use super::Revision; pub trait Configuration: Sized + 'static { const DEBUG_NAME: &'static str; - type Data<'db>: InternedData; + /// The type of data being interned + type Data<'db>: InternedData + Send + Sync; + /// The end user struct type Struct<'db>: Copy; /// Create an end-user struct from the underlying raw pointer. diff --git a/src/jar.rs b/src/jar.rs deleted file mode 100644 index aaee6f85..00000000 --- a/src/jar.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ - storage::{HasJar, JarFromJars}, - Database, DbWithJar, -}; - -use super::routes::Routes; - -/// Representative trait of a salsa jar -/// -/// # Safety -/// -/// `init_jar` must fully initialize the jar -pub unsafe trait Jar: Sized { - type DynDb: ?Sized + HasJar + Database; - - /// Initializes the jar at `place` - /// - /// # Safety - /// - /// `place` must be a valid pointer to this jar - unsafe fn init_jar(place: *mut Self, routes: &mut Routes) - where - DB: JarFromJars + DbWithJar; -} diff --git a/src/lib.rs b/src/lib.rs index 70d0c304..916e251a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ mod views; pub use self::cancelled::Cancelled; pub use self::cycle::Cycle; pub use self::database::Database; -pub use self::database::DatabaseView; pub use self::database::ParallelDatabase; pub use self::database::Snapshot; pub use self::durability::Durability; @@ -43,7 +42,6 @@ pub use salsa_macros::accumulator; pub use salsa_macros::db; pub use salsa_macros::input; pub use salsa_macros::interned; -pub use salsa_macros::jar; pub use salsa_macros::tracked; pub use salsa_macros::DebugWithDb; pub use salsa_macros::Update; diff --git a/src/setter.rs b/src/setter.rs index 8128b139..75d0a341 100644 --- a/src/setter.rs +++ b/src/setter.rs @@ -1,25 +1,26 @@ use crate::id::AsId; -use crate::input_field::InputFieldIngredient; +use crate::input::Configuration; +use crate::input_field::{InputFieldData, InputFieldIngredient}; use crate::{Durability, Runtime}; use std::hash::Hash; #[must_use] -pub struct Setter<'setter, K, F> { +pub struct Setter<'setter, C: Configuration, F: InputFieldData> { runtime: &'setter mut Runtime, - key: K, - ingredient: &'setter mut InputFieldIngredient, + key: C::Id, + ingredient: &'setter mut InputFieldIngredient, durability: Durability, } -impl<'setter, K, F> Setter<'setter, K, F> +impl<'setter, C, F> Setter<'setter, C, F> where - K: Eq + Hash + AsId + 'static, - F: 'static, + C: Configuration, + F: InputFieldData, { pub fn new( runtime: &'setter mut Runtime, - key: K, - ingredient: &'setter mut InputFieldIngredient, + key: C::Id, + ingredient: &'setter mut InputFieldIngredient, ) -> Self { Setter { runtime, diff --git a/src/storage.rs b/src/storage.rs index 3edb3088..5842e470 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -25,7 +25,7 @@ pub fn views(db: &Db) -> &Views { /// /// This trait is meant to be implemented by our procedural macro. /// We need to document any non-obvious conditions that it satisfies. -pub unsafe trait DatabaseGen: Any + Send + Sync { +pub unsafe trait DatabaseGen: Any { /// Upcast to a `dyn Database`. /// /// Only required because upcasts not yet stabilized (*grr*). @@ -80,9 +80,9 @@ pub unsafe trait DatabaseGen: Any + Send + Sync { /// /// The `storage` field must be an owned field of /// the implementing struct. -pub unsafe trait HasStorage: Database + Sized + Any + Send + Sync { +pub unsafe trait HasStorage: Database + Sized + Any { fn storage(&self) -> &Storage; - fn storage_mut(&self) -> &mut Storage; + fn storage_mut(&mut self) -> &mut Storage; } unsafe impl DatabaseGen for T { @@ -98,13 +98,6 @@ unsafe impl DatabaseGen for T { &self.storage().shared.upcasts } - fn views_of_self(&self) -> &ViewsOf - where - Self: Sized + Database, - { - &self.storage().shared.upcasts - } - fn nonce(&self) -> Nonce { self.storage().shared.nonce } @@ -388,10 +381,19 @@ where cached_data: std::sync::OnceLock<(Nonce, *const I)>, } +unsafe impl Sync for IngredientCache where I: Ingredient + Sync {} + impl IngredientCache where I: Ingredient, { + /// Create a new cache + pub const fn new() -> Self { + Self { + cached_data: std::sync::OnceLock::new(), + } + } + /// Get a reference to the ingredient in the database. /// If the ingredient is not already in the cache, it will be created. pub fn get_or_create<'s>( diff --git a/src/tracked_struct.rs b/src/tracked_struct.rs index 458123be..a749cdf5 100644 --- a/src/tracked_struct.rs +++ b/src/tracked_struct.rs @@ -31,13 +31,13 @@ pub trait Configuration: Jar + Sized + 'static { const FIELD_DEBUG_NAMES: &'static [&'static str]; /// A (possibly empty) tuple of the fields for this struct. - type Fields<'db>; + type Fields<'db>: Send + Sync; /// A array of [`Revision`][] values, one per each of the value fields. /// When a struct is re-recreated in a new revision, the corresponding /// entries for each field are updated to the new revision if their /// values have changed (or if the field is marked as `#[no_eq]`). - type Revisions; + type Revisions: Send + Sync; type Struct<'db>: Copy; diff --git a/src/views.rs b/src/views.rs index 5a444d87..3e59dd4f 100644 --- a/src/views.rs +++ b/src/views.rs @@ -68,7 +68,7 @@ impl Views { } /// Add a new upcast from `Db` to `T`, given the upcasting function `func`. - fn add( + pub fn add( &self, func: fn(&Db) -> &DbView, func_mut: fn(&mut Db) -> &mut DbView,