mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-22 21:05:11 +00:00
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:
parent
e7223f480a
commit
fe295c1b6e
2 changed files with 82 additions and 10 deletions
|
@ -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
44
tests/transparent.rs
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue