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
// 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<Ident>).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 {
<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 } => {
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<DB> salsa::plumbing::QueryFunction<DB> 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<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)]
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,
}
}

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);
}