From fe295c1b6e3bc0747fca8124c3066b13c60e4833 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 30 Apr 2019 23:42:17 +0300 Subject: [PATCH] Add transparent query type Transparent queries are not really queries: they are just plain uncached functions without any backing storage. Making a query transparent can be useful to figure out if caching it at all is a win --- components/salsa-macros/src/query_group.rs | 48 +++++++++++++++++----- tests/transparent.rs | 44 ++++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 tests/transparent.rs diff --git a/components/salsa-macros/src/query_group.rs b/components/salsa-macros/src/query_group.rs index 2fce2505..7f5d2b97 100644 --- a/components/salsa-macros/src/query_group.rs +++ b/components/salsa-macros/src/query_group.rs @@ -35,7 +35,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream // Leave non-salsa attributes untouched. These are // attributes that don't start with `salsa::` or don't have // exactly two segments in their path. - if is_salsa_attr_path(&attr.path) { + if is_not_salsa_attr_path(&attr.path) { attrs.push(attr); continue; } @@ -70,6 +70,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream "query_type" => { query_type = parse_macro_input!(tts as Parenthesized).0; } + "transparent" => { + storage = QueryStorage::Transparent; + num_storages += 1; + } _ => panic!("unknown salsa attribute `{}`", name), } } @@ -196,6 +200,18 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream fn #fn_name(&self, #(#key_names: #keys),*) -> #value; }); + // Special case: transparent queries don't create actual storage, + // just inline the definition + if let QueryStorage::Transparent = query.storage { + let invoke = query.invoke_tt(); + query_fn_definitions.extend(quote! { + fn #fn_name(&self, #(#key_names: #keys),*) -> #value { + #invoke(self, #(#key_names),*) + } + }); + continue; + } + query_fn_definitions.extend(quote! { fn #fn_name(&self, #(#key_names: #keys),*) -> #value { >::get_query_table(self).get((#(#key_names),*)) @@ -337,6 +353,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream QueryStorage::InternedLookup { intern_query_type } => { quote!(salsa::plumbing::LookupInternedStorage<#db, Self, #intern_query_type>) } + QueryStorage::Transparent => continue, }; let keys = &query.keys; let value = &query.value; @@ -380,10 +397,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream } else { quote! { (#(#key_names),*) } }; - let invoke = match &query.invoke { - Some(i) => i.into_token_stream(), - None => query.fn_name.clone().into_token_stream(), - }; + let invoke = query.invoke_tt(); output.extend(quote_spanned! {span=> impl salsa::plumbing::QueryFunction for #qt where @@ -427,7 +441,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream }); let mut for_each_ops = proc_macro2::TokenStream::new(); - for Query { fn_name, .. } in &queries { + for Query { fn_name, .. } in queries + .iter() + .filter(|q| q.storage != QueryStorage::Transparent) + { for_each_ops.extend(quote! { op(&self.#fn_name); }); @@ -484,7 +501,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream output.into() } -fn is_salsa_attr_path(path: &syn::Path) -> bool { +fn is_not_salsa_attr_path(path: &syn::Path) -> bool { path.segments .first() .map(|s| s.value().ident != "salsa") @@ -503,6 +520,15 @@ struct Query { invoke: Option, } +impl Query { + fn invoke_tt(&self) -> proc_macro2::TokenStream { + match &self.invoke { + Some(i) => i.into_token_stream(), + None => self.fn_name.clone().into_token_stream(), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] enum QueryStorage { Memoized, @@ -511,14 +537,16 @@ enum QueryStorage { Input, Interned, InternedLookup { intern_query_type: Ident }, + Transparent, } impl QueryStorage { fn needs_query_function(&self) -> bool { match self { - QueryStorage::Input | QueryStorage::Interned | QueryStorage::InternedLookup { .. } => { - false - } + | QueryStorage::Input + | QueryStorage::Interned + | QueryStorage::InternedLookup { .. } + | QueryStorage::Transparent => false, QueryStorage::Memoized | QueryStorage::Volatile | QueryStorage::Dependencies => true, } } diff --git a/tests/transparent.rs b/tests/transparent.rs new file mode 100644 index 00000000..025ec03a --- /dev/null +++ b/tests/transparent.rs @@ -0,0 +1,44 @@ +//! Test that transparent (uncached) queries work + +#[salsa::query_group(QueryGroupStorage)] +trait QueryGroup { + #[salsa::input] + fn input(&self, x: u32) -> u32; + #[salsa::transparent] + fn wrap(&self, x: u32) -> u32; + fn get(&self, x: u32) -> u32; +} + +fn wrap(db: &impl QueryGroup, x: u32) -> u32 { + db.input(x) +} + +fn get(db: &impl QueryGroup, x: u32) -> u32 { + db.wrap(x) +} + + +#[salsa::database(QueryGroupStorage)] +#[derive(Default)] +struct Database { + runtime: salsa::Runtime, +} + +impl salsa::Database for Database { + fn salsa_runtime(&self) -> &salsa::Runtime { + &self.runtime + } +} + +#[test] +fn transparent_queries_work() { + let mut db = Database::default(); + + db.set_input(1, 10); + assert_eq!(db.get(1), 10); + assert_eq!(db.get(1), 10); + + db.set_input(1, 92); + assert_eq!(db.get(1), 92); + assert_eq!(db.get(1), 92); +}