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
|
// 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
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