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
This commit is contained in:
Aleksey Kladov 2019-04-30 23:42:17 +03:00
parent e7223f480a
commit fe295c1b6e
2 changed files with 82 additions and 10 deletions

View file

@ -35,7 +35,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
// Leave non-salsa attributes untouched. These are // Leave non-salsa attributes untouched. These are
// attributes that don't start with `salsa::` or don't have // attributes that don't start with `salsa::` or don't have
// exactly two segments in their path. // exactly two segments in their path.
if is_salsa_attr_path(&attr.path) { if is_not_salsa_attr_path(&attr.path) {
attrs.push(attr); attrs.push(attr);
continue; continue;
} }
@ -70,6 +70,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
"query_type" => { "query_type" => {
query_type = parse_macro_input!(tts as Parenthesized<Ident>).0; query_type = parse_macro_input!(tts as Parenthesized<Ident>).0;
} }
"transparent" => {
storage = QueryStorage::Transparent;
num_storages += 1;
}
_ => panic!("unknown salsa attribute `{}`", name), _ => 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; 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! { query_fn_definitions.extend(quote! {
fn #fn_name(&self, #(#key_names: #keys),*) -> #value { fn #fn_name(&self, #(#key_names: #keys),*) -> #value {
<Self as salsa::plumbing::GetQueryTable<#qt>>::get_query_table(self).get((#(#key_names),*)) <Self as salsa::plumbing::GetQueryTable<#qt>>::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 } => { QueryStorage::InternedLookup { intern_query_type } => {
quote!(salsa::plumbing::LookupInternedStorage<#db, Self, #intern_query_type>) quote!(salsa::plumbing::LookupInternedStorage<#db, Self, #intern_query_type>)
} }
QueryStorage::Transparent => continue,
}; };
let keys = &query.keys; let keys = &query.keys;
let value = &query.value; let value = &query.value;
@ -380,10 +397,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
} else { } else {
quote! { (#(#key_names),*) } quote! { (#(#key_names),*) }
}; };
let invoke = match &query.invoke { let invoke = query.invoke_tt();
Some(i) => i.into_token_stream(),
None => query.fn_name.clone().into_token_stream(),
};
output.extend(quote_spanned! {span=> output.extend(quote_spanned! {span=>
impl<DB> salsa::plumbing::QueryFunction<DB> for #qt impl<DB> salsa::plumbing::QueryFunction<DB> for #qt
where where
@ -427,7 +441,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
}); });
let mut for_each_ops = proc_macro2::TokenStream::new(); 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! { for_each_ops.extend(quote! {
op(&self.#fn_name); op(&self.#fn_name);
}); });
@ -484,7 +501,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
output.into() output.into()
} }
fn is_salsa_attr_path(path: &syn::Path) -> bool { fn is_not_salsa_attr_path(path: &syn::Path) -> bool {
path.segments path.segments
.first() .first()
.map(|s| s.value().ident != "salsa") .map(|s| s.value().ident != "salsa")
@ -503,6 +520,15 @@ struct Query {
invoke: Option<syn::Path>, invoke: Option<syn::Path>,
} }
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)] #[derive(Debug, Clone, PartialEq, Eq)]
enum QueryStorage { enum QueryStorage {
Memoized, Memoized,
@ -511,14 +537,16 @@ enum QueryStorage {
Input, Input,
Interned, Interned,
InternedLookup { intern_query_type: Ident }, InternedLookup { intern_query_type: Ident },
Transparent,
} }
impl QueryStorage { impl QueryStorage {
fn needs_query_function(&self) -> bool { fn needs_query_function(&self) -> bool {
match self { match self {
QueryStorage::Input | QueryStorage::Interned | QueryStorage::InternedLookup { .. } => { | QueryStorage::Input
false | QueryStorage::Interned
} | QueryStorage::InternedLookup { .. }
| QueryStorage::Transparent => false,
QueryStorage::Memoized | QueryStorage::Volatile | QueryStorage::Dependencies => true, QueryStorage::Memoized | QueryStorage::Volatile | QueryStorage::Dependencies => true,
} }
} }

44
tests/transparent.rs Normal file
View file

@ -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<Database>,
}
impl salsa::Database for Database {
fn salsa_runtime(&self) -> &salsa::Runtime<Database> {
&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);
}