diff --git a/components/salsa-macro-rules/src/setup_tracked_fn.rs b/components/salsa-macro-rules/src/setup_tracked_fn.rs index f3e53d97c..34e71a796 100644 --- a/components/salsa-macro-rules/src/setup_tracked_fn.rs +++ b/components/salsa-macro-rules/src/setup_tracked_fn.rs @@ -66,6 +66,8 @@ macro_rules! setup_tracked_fn { 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. @@ -139,7 +141,7 @@ macro_rules! setup_tracked_fn { file: file!(), line: line!(), }; - const DEBUG_NAME: &'static str = concat!(stringify!($fn_name), "::interned_arguments"); + const DEBUG_NAME: &'static str = concat!($(stringify!($self_ty), "::",)? stringify!($fn_name), "::interned_arguments"); type Fields<$db_lt> = ($($interned_input_ty),*); @@ -185,7 +187,7 @@ macro_rules! setup_tracked_fn { file: file!(), line: line!(), }; - const DEBUG_NAME: &'static str = stringify!($fn_name); + const DEBUG_NAME: &'static str = concat!($(stringify!($self_ty), "::", )? stringify!($fn_name)); type DbView = dyn $Db; diff --git a/components/salsa-macros/src/accumulator.rs b/components/salsa-macros/src/accumulator.rs index 531ba83f4..866674bf6 100644 --- a/components/salsa-macros/src/accumulator.rs +++ b/components/salsa-macros/src/accumulator.rs @@ -46,6 +46,7 @@ impl AllowedOptions for Accumulator { const ID: bool = false; const REVISIONS: bool = false; const HEAP_SIZE: bool = false; + const SELF_TY: bool = false; } struct StructMacro { diff --git a/components/salsa-macros/src/input.rs b/components/salsa-macros/src/input.rs index 17df4267c..71799f2b0 100644 --- a/components/salsa-macros/src/input.rs +++ b/components/salsa-macros/src/input.rs @@ -66,6 +66,8 @@ impl crate::options::AllowedOptions for InputStruct { const REVISIONS: bool = false; const HEAP_SIZE: bool = false; + + const SELF_TY: bool = false; } impl SalsaStructAllowedOptions for InputStruct { diff --git a/components/salsa-macros/src/interned.rs b/components/salsa-macros/src/interned.rs index 27229167e..606701c6f 100644 --- a/components/salsa-macros/src/interned.rs +++ b/components/salsa-macros/src/interned.rs @@ -66,6 +66,8 @@ impl crate::options::AllowedOptions for InternedStruct { const REVISIONS: bool = true; const HEAP_SIZE: bool = false; + + const SELF_TY: bool = false; } impl SalsaStructAllowedOptions for InternedStruct { diff --git a/components/salsa-macros/src/options.rs b/components/salsa-macros/src/options.rs index e26c0163f..00ac4331a 100644 --- a/components/salsa-macros/src/options.rs +++ b/components/salsa-macros/src/options.rs @@ -105,6 +105,10 @@ pub(crate) struct Options { /// If this is `Some`, the value is the provided `heap_size` function. pub heap_size_fn: Option, + /// The `self_ty = ` option is used to set the the self type of the tracked impl for tracked + /// functions. This is merely used to refine the query name. + pub self_ty: Option, + /// Remember the `A` parameter, which plays no role after parsing. phantom: PhantomData, } @@ -130,6 +134,7 @@ impl Default for Options { id: Default::default(), revisions: Default::default(), heap_size_fn: Default::default(), + self_ty: Default::default(), } } } @@ -153,6 +158,7 @@ pub(crate) trait AllowedOptions { const ID: bool; const REVISIONS: bool; const HEAP_SIZE: bool; + const SELF_TY: bool; } type Equals = syn::Token![=]; @@ -416,6 +422,22 @@ impl syn::parse::Parse for Options { "`heap_size` option not allowed here", )); } + } else if ident == "self_ty" { + if A::SELF_TY { + let _eq = Equals::parse(input)?; + let ty = syn::Type::parse(input)?; + if let Some(old) = options.self_ty.replace(ty) { + return Err(syn::Error::new( + old.span(), + "option `self_ty` provided twice", + )); + } + } else { + return Err(syn::Error::new( + ident.span(), + "`self_ty` option not allowed here", + )); + } } else { return Err(syn::Error::new( ident.span(), @@ -433,3 +455,82 @@ impl syn::parse::Parse for Options { Ok(options) } } +impl quote::ToTokens for Options { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + returns, + no_eq, + debug, + no_lifetime, + singleton, + specify, + non_update_return_type, + db_path, + cycle_fn, + cycle_initial, + cycle_result, + data, + lru, + constructor_name, + id, + revisions, + heap_size_fn, + self_ty, + phantom: _, + } = self; + if let Some(returns) = returns { + tokens.extend(quote::quote! { returns(#returns), }); + }; + if no_eq.is_some() { + tokens.extend(quote::quote! { no_eq, }); + } + if debug.is_some() { + tokens.extend(quote::quote! { debug, }); + } + if no_lifetime.is_some() { + tokens.extend(quote::quote! { no_lifetime, }); + } + if singleton.is_some() { + tokens.extend(quote::quote! { singleton, }); + } + if specify.is_some() { + tokens.extend(quote::quote! { specify, }); + } + if non_update_return_type.is_some() { + tokens.extend(quote::quote! { unsafe(non_update_return_type), }); + } + if let Some(db_path) = db_path { + tokens.extend(quote::quote! { db = #db_path, }); + } + if let Some(cycle_fn) = cycle_fn { + tokens.extend(quote::quote! { cycle_fn = #cycle_fn, }); + } + if let Some(cycle_initial) = cycle_initial { + tokens.extend(quote::quote! { cycle_initial = #cycle_initial, }); + } + if let Some(cycle_result) = cycle_result { + tokens.extend(quote::quote! { cycle_result = #cycle_result, }); + } + if let Some(data) = data { + tokens.extend(quote::quote! { data = #data, }); + } + if let Some(lru) = lru { + tokens.extend(quote::quote! { lru = #lru, }); + } + if let Some(constructor_name) = constructor_name { + tokens.extend(quote::quote! { constructor = #constructor_name, }); + } + if let Some(id) = id { + tokens.extend(quote::quote! { id = #id, }); + } + if let Some(revisions) = revisions { + tokens.extend(quote::quote! { revisions = #revisions, }); + } + if let Some(heap_size_fn) = heap_size_fn { + tokens.extend(quote::quote! { heap_size_fn = #heap_size_fn, }); + } + if let Some(self_ty) = self_ty { + tokens.extend(quote::quote! { self_ty = #self_ty, }); + } + } +} diff --git a/components/salsa-macros/src/tracked_fn.rs b/components/salsa-macros/src/tracked_fn.rs index 7d0380fa2..f2f078934 100644 --- a/components/salsa-macros/src/tracked_fn.rs +++ b/components/salsa-macros/src/tracked_fn.rs @@ -59,6 +59,8 @@ impl crate::options::AllowedOptions for TrackedFn { const REVISIONS: bool = false; const HEAP_SIZE: bool = true; + + const SELF_TY: bool = true; } struct Macro { @@ -199,6 +201,10 @@ impl Macro { } else { quote! {} }; + let self_ty = match &self.args.self_ty { + Some(ty) => quote! { self_ty: #ty, }, + None => quote! {}, + }; Ok(crate::debug::dump_tokens( fn_name, @@ -224,6 +230,7 @@ impl Macro { lru: #lru, return_mode: #return_mode, assert_return_type_is_update: { #assert_return_type_is_update }, + #self_ty unused_names: [ #zalsa, #Configuration, diff --git a/components/salsa-macros/src/tracked_impl.rs b/components/salsa-macros/src/tracked_impl.rs index fbbf93c3a..9ad9c2c43 100644 --- a/components/salsa-macros/src/tracked_impl.rs +++ b/components/salsa-macros/src/tracked_impl.rs @@ -69,7 +69,7 @@ impl Macro { return Ok(()); }; - let self_ty = &impl_item.self_ty; + let self_ty = &*impl_item.self_ty; let Some(tracked_attr_index) = fn_item.attrs.iter().position(|a| self.is_tracked_attr(a)) else { @@ -83,11 +83,20 @@ impl Macro { let mut change = ChangeSelfPath::new(self_ty, trait_); change.visit_impl_item_fn_mut(fn_item); - let salsa_tracked_attr = fn_item.attrs.remove(tracked_attr_index); - let args: FnArgs = match &salsa_tracked_attr.meta { + let mut salsa_tracked_attr = fn_item.attrs.remove(tracked_attr_index); + let mut args: FnArgs = match &salsa_tracked_attr.meta { syn::Meta::Path(..) => Default::default(), _ => salsa_tracked_attr.parse_args()?, }; + if args.self_ty.is_none() { + // If the user did not specify a self_ty, we use the impl's self_ty + args.self_ty = Some(self_ty.clone()); + } + salsa_tracked_attr.meta = syn::Meta::List(syn::MetaList { + path: salsa_tracked_attr.path().clone(), + delimiter: syn::MacroDelimiter::Paren(syn::token::Paren::default()), + tokens: quote! {#args}, + }); let InnerTrait = self.hygiene.ident("InnerTrait"); let inner_fn_name = self.hygiene.ident(&fn_item.sig.ident.to_string()); diff --git a/components/salsa-macros/src/tracked_struct.rs b/components/salsa-macros/src/tracked_struct.rs index c3d2b9211..fe077da53 100644 --- a/components/salsa-macros/src/tracked_struct.rs +++ b/components/salsa-macros/src/tracked_struct.rs @@ -62,6 +62,8 @@ impl crate::options::AllowedOptions for TrackedStruct { const REVISIONS: bool = false; const HEAP_SIZE: bool = false; + + const SELF_TY: bool = false; } impl SalsaStructAllowedOptions for TrackedStruct { diff --git a/tests/tracked_assoc_fn.rs b/tests/tracked_assoc_fn.rs index c1f1ca677..18e6b953d 100644 --- a/tests/tracked_assoc_fn.rs +++ b/tests/tracked_assoc_fn.rs @@ -80,6 +80,6 @@ fn debug_name() { assert_eq!(output.field(&db), 88); db.assert_logs(expect![[r#" [ - "salsa_event(WillExecute { database_key: tracked_trait_fn_(Id(0)) })", + "salsa_event(WillExecute { database_key: MyOutput < 'db >::tracked_trait_fn_(Id(0)) })", ]"#]]); } diff --git a/tests/tracked_method.rs b/tests/tracked_method.rs index 4fc283a64..d5953deaa 100644 --- a/tests/tracked_method.rs +++ b/tests/tracked_method.rs @@ -54,6 +54,6 @@ fn debug_name() { assert_eq!(object.tracked_trait_fn(&db), 88); db.assert_logs(expect![[r#" [ - "salsa_event(WillExecute { database_key: tracked_trait_fn_(Id(0)) })", + "salsa_event(WillExecute { database_key: MyInput::tracked_trait_fn_(Id(0)) })", ]"#]]); }