mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-12-24 12:58:37 +00:00
make query_group macro procedural
Switch to a procedural implementation of the `query_group!` macro, residing in the `components/salsa_macros` subcrate. Allow the user to override the invoked function via `salsa::invoke(...)` and the name of the generated query type via `salsa::query_type(...)`. In all tests, replace the `salsa::query_group! { ... }` invocations with the new attribute-style `#[salsa::query_group]` macro, and change them to the new naming scheme for query types (`...Query`). Update README, examples, and documentation.
This commit is contained in:
parent
cd454e986e
commit
93c30a953d
35 changed files with 768 additions and 782 deletions
|
@ -16,6 +16,7 @@ lock_api = "0.1.4"
|
|||
indexmap = "1.0.1"
|
||||
log = "0.4.5"
|
||||
smallvec = "0.6.5"
|
||||
salsa_macros = { version = "0.9", path = "components/salsa_macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
diff = "0.1.0"
|
||||
|
|
18
components/salsa_macros/Cargo.toml
Normal file
18
components/salsa_macros/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "salsa_macros"
|
||||
version = "0.9.1"
|
||||
authors = ["Niko Matsakis <niko@alum.mit.edu>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
repository = "https://github.com/salsa-rs/salsa"
|
||||
description = "Procedural macros for the salsa crate"
|
||||
readme = "README.md"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
heck = "0.3"
|
||||
proc-macro2 = "0.4"
|
||||
quote = "0.6"
|
||||
syn = { version = "0.15", features = ["full", "extra-traits"] }
|
1
components/salsa_macros/LICENSE-APACHE
Symbolic link
1
components/salsa_macros/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../LICENSE-APACHE
|
1
components/salsa_macros/LICENSE-MIT
Symbolic link
1
components/salsa_macros/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../LICENSE-MIT
|
1
components/salsa_macros/README.md
Symbolic link
1
components/salsa_macros/README.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../README.md
|
369
components/salsa_macros/src/lib.rs
Normal file
369
components/salsa_macros/src/lib.rs
Normal file
|
@ -0,0 +1,369 @@
|
|||
//! This crate provides salsa's macros and attributes.
|
||||
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use heck::CamelCase;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
use syn::{parse_macro_input, AttributeArgs, FnArg, Ident, ItemTrait, ReturnType, TraitItem};
|
||||
|
||||
/// The decorator that defines a salsa "query group" trait. This is a
|
||||
/// trait that defines everything that a block of queries need to
|
||||
/// execute, as well as defining the queries themselves that are
|
||||
/// exported for others to use.
|
||||
///
|
||||
/// This macro declares the "prototype" for a group of queries. It will
|
||||
/// expand into a trait and a set of structs, one per query.
|
||||
///
|
||||
/// For each query, you give the name of the accessor method to invoke
|
||||
/// the query (e.g., `my_query`, below), as well as its parameter
|
||||
/// types and the output type. You also give the name for a query type
|
||||
/// (e.g., `MyQuery`, below) that represents the query, and optionally
|
||||
/// other details, such as its storage.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The simplest example is something like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[salsa::query_group]
|
||||
/// trait TypeckDatabase {
|
||||
/// #[salsa::XXX] // see below for legal attributes
|
||||
/// fn my_query(&self, input: u32) -> u64;
|
||||
///
|
||||
/// /// Queries can have any number of inputs (including zero); if there
|
||||
/// /// is not exactly one input, then the key type will be
|
||||
/// /// a tuple of the input types, so in this case `(u32, f32)`.
|
||||
/// fn other_query(input1: u32, input2: f32) -> u64;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here is a list of legal `salsa::XXX` attributes:
|
||||
///
|
||||
/// - Storage attributes: control how the query data is stored and set. These
|
||||
/// are described in detail in the section below.
|
||||
/// - `#[salsa::input]`
|
||||
/// - `#[salsa::memoized]`
|
||||
/// - `#[salsa::volatile]`
|
||||
/// - `#[salsa::dependencies]`
|
||||
/// - Query execution:
|
||||
/// - `#[salsa::invoke(path::to::my_fn)]` -- for a non-input, this
|
||||
/// indicates the function to call when a query must be
|
||||
/// recomputed. The default is to call a function in the same
|
||||
/// module with the same name as the query.
|
||||
/// - `#[query_type(MyQueryTypeName)]` specifies the name of the
|
||||
/// dummy struct created fo the query. Default is the name of the
|
||||
/// query, in camel case, plus the word "Query" (e.g.,
|
||||
/// `MyQueryQuery` and `OtherQueryQuery` in the examples above).
|
||||
///
|
||||
/// # Storage attributes
|
||||
///
|
||||
/// Here are the possible storage values for each query. The default
|
||||
/// is `storage memoized`.
|
||||
///
|
||||
/// ## Input queries
|
||||
///
|
||||
/// Specifying `storage input` will give you an **input
|
||||
/// query**. Unlike derived queries, whose value is given by a
|
||||
/// function, input queries are explicitly set by doing
|
||||
/// `db.query(QueryType).set(key, value)` (where `QueryType` is the
|
||||
/// `type` specified for the query). Accessing a value that has not
|
||||
/// yet been set will panic. Each time you invoke `set`, we assume the
|
||||
/// value has changed, and so we will potentially re-execute derived
|
||||
/// queries that read (transitively) from this input.
|
||||
///
|
||||
/// ## Derived queries
|
||||
///
|
||||
/// Derived queries are specified by a function.
|
||||
///
|
||||
/// - `#[salsa::memoized]` (the default) -- The result is memoized
|
||||
/// between calls. If the inputs have changed, we will recompute
|
||||
/// the value, but then compare against the old memoized value,
|
||||
/// which can significantly reduce the amount of recomputation
|
||||
/// required in new revisions. This does require that the value
|
||||
/// implements `Eq`.
|
||||
/// - `#[salsa::volatile]` -- indicates that the inputs are not fully
|
||||
/// captured by salsa. The result will be recomputed once per revision.
|
||||
/// - `#[salsa::dependencies]` -- does not cache the value, so it will
|
||||
/// be recomputed every time it is needed. We do track the inputs, however,
|
||||
/// so if they have not changed, then things that rely on this query
|
||||
/// may be known not to have changed.
|
||||
///
|
||||
/// ## Attribute combinations
|
||||
///
|
||||
/// Some attributes are mutually exclusive. For example, it is an error to add
|
||||
/// multiple storage specifiers:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[salsa::query_group]
|
||||
/// trait CodegenDatabase {
|
||||
/// #[salsa::input]
|
||||
/// #[salsa::memoized]
|
||||
/// fn my_query(&self, input: u32) -> u64;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It is also an error to annotate a function to `invoke` on an `input` query:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[salsa::query_group]
|
||||
/// trait CodegenDatabase {
|
||||
/// #[salsa::input]
|
||||
/// #[salsa::invoke(typeck::my_query)]
|
||||
/// fn my_query(&self, input: u32) -> u64;
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn query_group(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let _args = parse_macro_input!(args as AttributeArgs);
|
||||
let input = parse_macro_input!(input as ItemTrait);
|
||||
// println!("args: {:#?}", args);
|
||||
// println!("input: {:#?}", input);
|
||||
|
||||
let trait_vis = input.vis;
|
||||
let trait_name = input.ident;
|
||||
let _generics = input.generics.clone();
|
||||
|
||||
// Decompose the trait into the corresponding queries.
|
||||
let mut queries = vec![];
|
||||
for item in input.items {
|
||||
match item {
|
||||
TraitItem::Method(method) => {
|
||||
let mut storage = QueryStorage::Memoized;
|
||||
let mut invoke = None;
|
||||
let mut query_type = Ident::new(
|
||||
&format!("{}Query", method.sig.ident.to_string().to_camel_case()),
|
||||
Span::call_site(),
|
||||
);
|
||||
let mut num_storages = 0;
|
||||
|
||||
// Extract attributes.
|
||||
let mut attrs = vec![];
|
||||
for attr in method.attrs {
|
||||
// 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) {
|
||||
attrs.push(attr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep the salsa attributes around.
|
||||
let name = attr.path.segments[1].ident.to_string();
|
||||
let tts = attr.tts.into();
|
||||
match name.as_str() {
|
||||
"memoized" => {
|
||||
storage = QueryStorage::Memoized;
|
||||
num_storages += 1;
|
||||
}
|
||||
"volatile" => {
|
||||
storage = QueryStorage::Volatile;
|
||||
num_storages += 1;
|
||||
}
|
||||
"dependencies" => {
|
||||
storage = QueryStorage::Dependencies;
|
||||
num_storages += 1;
|
||||
}
|
||||
"input" => {
|
||||
storage = QueryStorage::Input;
|
||||
num_storages += 1;
|
||||
}
|
||||
"invoke" => {
|
||||
invoke = Some(parse_macro_input!(tts as Parenthesized<syn::Path>).0);
|
||||
}
|
||||
"query_type" => {
|
||||
query_type = parse_macro_input!(tts as Parenthesized<Ident>).0;
|
||||
}
|
||||
_ => panic!("unknown salsa attribute `{}`", name),
|
||||
}
|
||||
}
|
||||
|
||||
// Check attribute combinations.
|
||||
if num_storages > 1 {
|
||||
panic!("multiple storage attributes specified");
|
||||
}
|
||||
if invoke.is_some() && storage == QueryStorage::Input {
|
||||
panic!("#[salsa::invoke] cannot be set on #[salsa::input] queries");
|
||||
}
|
||||
|
||||
// Extract keys.
|
||||
let mut iter = method.sig.decl.inputs.iter();
|
||||
match iter.next() {
|
||||
Some(FnArg::SelfRef(sr)) if sr.mutability.is_none() => (),
|
||||
_ => panic!(
|
||||
"first argument of query `{}` must be `&self` or `&mut self`",
|
||||
method.sig.ident
|
||||
),
|
||||
}
|
||||
let mut keys = vec![];
|
||||
for arg in iter {
|
||||
match *arg {
|
||||
FnArg::Captured(ref arg) => {
|
||||
keys.push(arg.ty.clone());
|
||||
}
|
||||
ref a => panic!("unsupported argument `{:?}` of `{}`", a, method.sig.ident),
|
||||
}
|
||||
}
|
||||
|
||||
// Extract value.
|
||||
let value = match method.sig.decl.output {
|
||||
ReturnType::Type(_, ref ty) => ty.as_ref().clone(),
|
||||
ref r => panic!(
|
||||
"unsupported return type `{:?}` of `{}`",
|
||||
r, method.sig.ident
|
||||
),
|
||||
};
|
||||
|
||||
queries.push(Query {
|
||||
query_type,
|
||||
fn_name: method.sig.ident.clone(),
|
||||
attrs,
|
||||
storage,
|
||||
keys,
|
||||
value,
|
||||
invoke,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the trait itself.
|
||||
let mut output = {
|
||||
let mut queries_as_tokens = proc_macro2::TokenStream::new();
|
||||
for query in &queries {
|
||||
let key_names: &Vec<_> = &(0..query.keys.len())
|
||||
.map(|i| Ident::new(&format!("key{}", i), Span::call_site()))
|
||||
.collect();
|
||||
let keys = &query.keys;
|
||||
let value = &query.value;
|
||||
let fn_name = &query.fn_name;
|
||||
let qt = &query.query_type;
|
||||
let attrs = &query.attrs;
|
||||
let expanded = quote! {
|
||||
#(#attrs)*
|
||||
fn #fn_name(&self, #(#key_names: #keys),*) -> #value {
|
||||
<Self as salsa::plumbing::GetQueryTable<#qt>>::get_query_table(self).get((#(#key_names),*))
|
||||
}
|
||||
};
|
||||
queries_as_tokens.extend(expanded);
|
||||
}
|
||||
|
||||
let attrs = &input.attrs;
|
||||
let qts = queries.iter().map(|q| &q.query_type);
|
||||
let bounds = &input.supertraits;
|
||||
quote! {
|
||||
#(#attrs)*
|
||||
#trait_vis trait #trait_name : #(salsa::plumbing::GetQueryTable<#qts> +)* #bounds {
|
||||
#queries_as_tokens
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Emit the query types.
|
||||
for query in queries {
|
||||
let qt = &query.query_type;
|
||||
let storage = Ident::new(
|
||||
match query.storage {
|
||||
QueryStorage::Memoized => "MemoizedStorage",
|
||||
QueryStorage::Volatile => "VolatileStorage",
|
||||
QueryStorage::Dependencies => "DependencyStorage",
|
||||
QueryStorage::Input => "InputStorage",
|
||||
},
|
||||
Span::call_site(),
|
||||
);
|
||||
let keys = &query.keys;
|
||||
let value = &query.value;
|
||||
|
||||
// Emit the query struct and implement the Query trait on it.
|
||||
output.extend(quote! {
|
||||
#[derive(Default, Debug)]
|
||||
#trait_vis struct #qt;
|
||||
|
||||
impl<DB> salsa::Query<DB> for #qt
|
||||
where
|
||||
DB: #trait_name,
|
||||
{
|
||||
type Key = (#(#keys),*);
|
||||
type Value = #value;
|
||||
type Storage = salsa::plumbing::#storage<DB, Self>;
|
||||
}
|
||||
});
|
||||
|
||||
// Implement the QueryFunction trait for all queries except inputs.
|
||||
if query.storage != QueryStorage::Input {
|
||||
let span = query.fn_name.span();
|
||||
let key_names: &Vec<_> = &(0..query.keys.len())
|
||||
.map(|i| Ident::new(&format!("key{}", i), Span::call_site()))
|
||||
.collect();
|
||||
let key_pattern = if query.keys.len() == 1 {
|
||||
quote! { #(#key_names),* }
|
||||
} else {
|
||||
quote! { (#(#key_names),*) }
|
||||
};
|
||||
let invoke = match &query.invoke {
|
||||
Some(i) => i.into_token_stream(),
|
||||
None => query.fn_name.into_token_stream(),
|
||||
};
|
||||
output.extend(quote_spanned! {span=>
|
||||
impl<DB> salsa::plumbing::QueryFunction<DB> for #qt
|
||||
where
|
||||
DB: #trait_name,
|
||||
{
|
||||
fn execute(db: &DB, #key_pattern: <Self as salsa::Query<DB>>::Key)
|
||||
-> <Self as salsa::Query<DB>>::Value {
|
||||
#invoke(db, #(#key_names),*)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn is_salsa_attr_path(path: &syn::Path) -> bool {
|
||||
path.segments
|
||||
.first()
|
||||
.map(|s| s.value().ident != "salsa")
|
||||
.unwrap_or(true)
|
||||
|| path.segments.len() != 2
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Query {
|
||||
fn_name: Ident,
|
||||
attrs: Vec<syn::Attribute>,
|
||||
query_type: Ident,
|
||||
storage: QueryStorage,
|
||||
keys: Vec<syn::Type>,
|
||||
value: syn::Type,
|
||||
invoke: Option<syn::Path>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum QueryStorage {
|
||||
Memoized,
|
||||
Volatile,
|
||||
Dependencies,
|
||||
Input,
|
||||
}
|
||||
|
||||
struct Parenthesized<T>(pub T);
|
||||
|
||||
impl<T> syn::parse::Parse for Parenthesized<T>
|
||||
where
|
||||
T: syn::parse::Parse,
|
||||
{
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
syn::parenthesized!(content in input);
|
||||
content.parse::<T>().map(Parenthesized)
|
||||
}
|
||||
}
|
|
@ -1,23 +1,16 @@
|
|||
use crate::compiler;
|
||||
use std::sync::Arc;
|
||||
|
||||
salsa::query_group! {
|
||||
pub trait ClassTableDatabase: compiler::CompilerDatabase {
|
||||
/// Get the fields.
|
||||
fn fields(class: DefId) -> Arc<Vec<DefId>> {
|
||||
type Fields;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub trait ClassTableDatabase: compiler::CompilerDatabase {
|
||||
/// Get the fields.
|
||||
fn fields(&self, class: DefId) -> Arc<Vec<DefId>>;
|
||||
|
||||
/// Get the list of all classes
|
||||
fn all_classes() -> Arc<Vec<DefId>> {
|
||||
type AllClasses;
|
||||
}
|
||||
/// Get the list of all classes
|
||||
fn all_classes(&self) -> Arc<Vec<DefId>>;
|
||||
|
||||
/// Get the list of all fields
|
||||
fn all_fields() -> Arc<Vec<DefId>> {
|
||||
type AllFields;
|
||||
}
|
||||
}
|
||||
/// Get the list of all fields
|
||||
fn all_fields(&self) -> Arc<Vec<DefId>>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
|
|
|
@ -39,9 +39,9 @@ impl salsa::Database for DatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for DatabaseImpl {
|
||||
impl class_table::ClassTableDatabase {
|
||||
fn all_classes() for class_table::AllClasses;
|
||||
fn all_fields() for class_table::AllFields;
|
||||
fn fields() for class_table::Fields;
|
||||
fn all_classes() for class_table::AllClassesQuery;
|
||||
fn all_fields() for class_table::AllFieldsQuery;
|
||||
fn fields() for class_table::FieldsQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,34 +12,33 @@ the queries from A and adds a few more. (These relationships must form
|
|||
a DAG at present, but that is due to Rust's restrictions around
|
||||
supertraits, which are likely to be lifted.)
|
||||
|
||||
Each query group is defined via an invocation of `salsa::query_group!`
|
||||
macro. This is the invocation used in `hello_world`:
|
||||
Each query group is defined via a trait with the
|
||||
`#[salsa::query_group]` decorator attached to it. `salsa::query_group`
|
||||
is a procedural macro that will process the trait -- it will produce
|
||||
not only the trait you specified, but also various additional types
|
||||
you can later use and name.
|
||||
|
||||
```rust
|
||||
salsa::query_group! {
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
fn input_string(key: ()) -> Arc<String> {
|
||||
type InputString;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
#[salsa::input]
|
||||
#[salsa::query_type(InputString)]
|
||||
fn input_string(&self, key: ()) -> Arc<String>;
|
||||
|
||||
fn length(key: ()) -> usize {
|
||||
type Length;
|
||||
}
|
||||
}
|
||||
fn length(&self, key: ()) -> usize;
|
||||
}
|
||||
```
|
||||
|
||||
This invocation will in fact expand to a number of things you can
|
||||
later use and name. First and foremost is the **query group trait**,
|
||||
here called `HelloWorldDatabase`. As the name suggests, this trait
|
||||
will ultimately be implemented by the **database**, which is the
|
||||
struct in your application that contains the store for all queries and
|
||||
any other global state that persists beyond a single query execution.
|
||||
In writing your application, though, we never work with a concrete
|
||||
database struct: instead we work against a generic struct through
|
||||
traits, thus capturing the subset of functionality that we actually
|
||||
need.
|
||||
Each query group trait represents a self-contained block of queries
|
||||
that can invoke each other and so forth. Your final database may
|
||||
implement many such traits, thus combining many groups of queries into
|
||||
the final program. Query groups are thus kind of analogous to Rust
|
||||
crates: they represent a kind of "library" of queries that your final
|
||||
program can use. Since we don't know the full set of queries that our
|
||||
code may be combined with, when implementing a query group we don't
|
||||
with a concrete database struct: instead we work against a generic
|
||||
struct through traits, thus capturing the subset of functionality that
|
||||
we actually need.
|
||||
|
||||
The `HelloWorldDatabase` trait has one supertrait:
|
||||
`salsa::Database`. If we were defining more query groups in our
|
||||
|
@ -59,19 +58,19 @@ the "fn body" is obviously not real Rust syntax. Rather, it's just
|
|||
used to specify a few bits of metadata about the query. We'll see how
|
||||
to define the fn body in the next step.
|
||||
|
||||
**For each query.** For each query, we must **always** define a `type`
|
||||
(e.g., `type InputString;`). The macro will define a type with this
|
||||
name alongside the trait: you can use this name later to specify which
|
||||
query you are talking about. This is needed for some of the more
|
||||
advanced methods (we'll discuss them later).
|
||||
**For each query.** For each query, the procedural macro will emit a
|
||||
"query type", which is a kind of dummy struct that can be used to
|
||||
refer to the query (we'll see an example of referencing this struct
|
||||
later). For a query `foo_bar`, the struct is by default named
|
||||
`FooBarQuery` -- but that name can be overridden with the
|
||||
`#[salsa::query_type]` attribute. In our example, we override the
|
||||
query type for `input_string` to be `InputString` but left `length`
|
||||
alone (so it defaults to `LengthQuery`).
|
||||
|
||||
You can also optionally define the **storage** for a query via a
|
||||
declaration like `storage <s>;`. The most common kind of storage is
|
||||
either *memoized* (the default) or *input*. An *input* is a special
|
||||
sort of query that is not defined by a function: rather, it gets its
|
||||
values via explicit `set` operations (we'll see them later). In our
|
||||
case, we define one input query (`input_string`) and one memoized
|
||||
query (`length`).
|
||||
You can also use the `#[salsa::input]` attribute to designate
|
||||
the "inputs" to the system. The values for input queries are not
|
||||
generated via a function but rather by explicit `set` operations,
|
||||
as we'll see later. They are the starting points for your computation.
|
||||
|
||||
### Step 2: Define the query functions
|
||||
|
||||
|
@ -79,9 +78,9 @@ Once you've defined your query group, you have to give the function
|
|||
definition for every non-input query. In our case, that is the query
|
||||
`length`. To do this, you simply define a function with the
|
||||
appropriate name in the same module as the query group; if you would
|
||||
prefer to use a different name or location, you write `use fn
|
||||
path::to::other_fn;` in the query definition to tell us where to find
|
||||
it.
|
||||
prefer to use a different name or location, you add an attribute like
|
||||
`#[salsa::invoke(path::to::other_fn)]` in the query definition to tell
|
||||
us where to find it.
|
||||
|
||||
The query function for `length` looks like:
|
||||
|
||||
|
|
|
@ -12,34 +12,24 @@ use std::sync::Arc;
|
|||
// the queries from A and adds a few more. (These relationships must form
|
||||
// a DAG at present, but that is due to Rust's restrictions around
|
||||
// supertraits, which are likely to be lifted.)
|
||||
salsa::query_group! {
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
// For each query, we give the name, input type (here, `()`)
|
||||
// and the output type `Arc<String>`. Inside the "fn body" we
|
||||
// give some other configuration.
|
||||
fn input_string(key: ()) -> Arc<String> {
|
||||
// The type we will generate to represent this query.
|
||||
type InputString;
|
||||
#[salsa::query_group]
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
// For each query, we give the name, input type (here, `()`)
|
||||
// and the output type `Arc<String>`. We can use attributes to
|
||||
// give other configuration:
|
||||
//
|
||||
// - `salsa::input` indicates that this is an "input" to the system,
|
||||
// which must be explicitly set.
|
||||
// - `salsa::query_type` controls the name of the dummy struct
|
||||
// that represents this query. We'll see it referenced
|
||||
// later. The default would have been `InputStringQuery`.
|
||||
#[salsa::input]
|
||||
#[salsa::query_type(InputString)]
|
||||
fn input_string(&self, key: ()) -> Arc<String>;
|
||||
|
||||
// Specify the queries' "storage" -- in this case, this is
|
||||
// an *input query*, which means that its value changes
|
||||
// only when it is explicitly *set* (see the `main`
|
||||
// function below).
|
||||
storage input;
|
||||
}
|
||||
|
||||
// This is a *derived query*, meaning its value is specified by
|
||||
// a function (see Step 2, below).
|
||||
fn length(key: ()) -> usize {
|
||||
type Length;
|
||||
|
||||
// No explicit storage defaults to `storage memoized;`
|
||||
//
|
||||
// The function that defines this query is (by default) a
|
||||
// function with the same name as the query in the
|
||||
// containing module (e.g., `length`).
|
||||
}
|
||||
}
|
||||
// This is a *derived query*, meaning its value is specified by
|
||||
// a function (see Step 2, below).
|
||||
fn length(&self, key: ()) -> usize;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -86,7 +76,7 @@ salsa::database_storage! {
|
|||
struct DatabaseStorage for DatabaseStruct {
|
||||
impl HelloWorldDatabase {
|
||||
fn input_string() for InputString;
|
||||
fn length() for Length;
|
||||
fn length() for LengthQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
294
src/lib.rs
294
src/lib.rs
|
@ -474,293 +474,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A macro that helps in defining the "context trait" of a given
|
||||
/// module. This is a trait that defines everything that a block of
|
||||
/// queries need to execute, as well as defining the queries
|
||||
/// themselves that are exported for others to use.
|
||||
///
|
||||
/// This macro declares the "prototype" for a group of queries. It will
|
||||
/// expand into a trait and a set of structs, one per query.
|
||||
///
|
||||
/// For each query, you give the name of the accessor method to invoke
|
||||
/// the query (e.g., `my_query`, below), as well as its parameter
|
||||
/// types and the output type. You also give the name for a query type
|
||||
/// (e.g., `MyQuery`, below) that represents the query, and optionally
|
||||
/// other details, such as its storage.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The simplest example is something like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// trait TypeckDatabase {
|
||||
/// query_group! {
|
||||
/// /// Comments or other attributes can go here
|
||||
/// fn my_query(input: u32) -> u64 {
|
||||
/// type MyQuery;
|
||||
/// storage memoized; // optional, this is the default
|
||||
/// use fn path::to::fn; // optional, default is `my_query`
|
||||
/// }
|
||||
///
|
||||
/// /// Queries can have any number of inputs; the key type will be
|
||||
/// /// a tuple of the input types, so in this case `(u32, f32)`.
|
||||
/// fn other_query(input1: u32, input2: f32) -> u64 {
|
||||
/// type OtherQuery;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Storage modes
|
||||
///
|
||||
/// Here are the possible storage values for each query. The default
|
||||
/// is `storage memoized`.
|
||||
///
|
||||
/// ## Input queries
|
||||
///
|
||||
/// Specifying `storage input` will give you an **input
|
||||
/// query**. Unlike derived queries, whose value is given by a
|
||||
/// function, input queries are explicit set by doing
|
||||
/// `db.query(QueryType).set(key, value)` (where `QueryType` is the
|
||||
/// `type` specified for the query). Accessing a value that has not
|
||||
/// yet been set will panic. Each time you invoke `set`, we assume the
|
||||
/// value has changed, and so we will potentially re-execute derived
|
||||
/// queries that read (transitively) from this input.
|
||||
///
|
||||
/// ## Derived queries
|
||||
///
|
||||
/// Derived queries are specified by a function.
|
||||
///
|
||||
/// - `storage memoized` -- The result is memoized
|
||||
/// between calls. If the inputs have changed, we will recompute
|
||||
/// the value, but then compare against the old memoized value,
|
||||
/// which can significantly reduce the amount of recomputation
|
||||
/// required in new revisions. This does require that the value
|
||||
/// implements `Eq`.
|
||||
/// - `storage volatile` -- indicates that the inputs are not fully
|
||||
/// captured by salsa. The result will be recomputed once per revision.
|
||||
/// - `storage dependencies` -- does not cache the value, so it will
|
||||
/// be recomputed every time it is needed. We do track the inputs, however,
|
||||
/// so if they have not changed, then things that rely on this query
|
||||
/// may be known not to have changed.
|
||||
#[macro_export]
|
||||
macro_rules! query_group {
|
||||
(
|
||||
$(#[$attr:meta])* $v:vis trait $name:ident { $($t:tt)* }
|
||||
) => {
|
||||
$crate::query_group! {
|
||||
attr[$(#[$attr])*];
|
||||
headers[$v, $name, ];
|
||||
tokens[{ $($t)* }];
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$attr:meta])* $v:vis trait $name:ident : $($t:tt)*
|
||||
) => {
|
||||
$crate::query_group! {
|
||||
attr[$(#[$attr])*];
|
||||
headers[$v, $name, ];
|
||||
tokens[$($t)*];
|
||||
}
|
||||
};
|
||||
|
||||
// Base case: found the trait body
|
||||
(
|
||||
attr[$($trait_attr:tt)*];
|
||||
headers[$v:vis, $query_trait:ident, $($header:tt)*];
|
||||
tokens[{
|
||||
$(
|
||||
$(#[$method_attr:meta])*
|
||||
fn $method_name:ident($($key_name:ident: $key_ty:ty),* $(,)*) -> $value_ty:ty {
|
||||
type $QueryType:ident;
|
||||
$(storage $storage:tt;)* // FIXME(rust-lang/rust#48075) should be `?`
|
||||
$(use fn $fn_path:path;)* // FIXME(rust-lang/rust#48075) should be `?`
|
||||
}
|
||||
)*
|
||||
}];
|
||||
) => {
|
||||
$($trait_attr)* $v trait $query_trait: $($crate::plumbing::GetQueryTable<$QueryType> +)* $($header)* {
|
||||
$(
|
||||
$(#[$method_attr])*
|
||||
fn $method_name(&self, $($key_name: $key_ty),*) -> $value_ty {
|
||||
<Self as $crate::plumbing::GetQueryTable<$QueryType>>::get_query_table(self)
|
||||
.get(($($key_name),*))
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
$(
|
||||
#[derive(Default, Debug)]
|
||||
$v struct $QueryType;
|
||||
|
||||
impl<DB> $crate::Query<DB> for $QueryType
|
||||
where
|
||||
DB: $query_trait,
|
||||
{
|
||||
type Key = ($($key_ty),*);
|
||||
type Value = $value_ty;
|
||||
type Storage = $crate::query_group! { @storage_ty[DB, Self, $($storage)*] };
|
||||
}
|
||||
|
||||
$crate::query_group! {
|
||||
@query_fn[
|
||||
storage($($storage)*);
|
||||
method_name($method_name);
|
||||
fn_path($($fn_path)*);
|
||||
db_trait($query_trait);
|
||||
query_type($QueryType);
|
||||
key($($key_name: $key_ty),*);
|
||||
]
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
||||
(
|
||||
@query_fn[
|
||||
storage(input);
|
||||
method_name($method_name:ident);
|
||||
fn_path();
|
||||
$($rest:tt)*
|
||||
]
|
||||
) => {
|
||||
// do nothing for `storage input`, presuming they did not write an explicit `use fn`
|
||||
};
|
||||
|
||||
(
|
||||
@query_fn[
|
||||
storage(input);
|
||||
method_name($method_name:ident);
|
||||
fn_path($fn_path:path);
|
||||
$($rest:tt)*
|
||||
]
|
||||
) => {
|
||||
// error for `storage input` with an explicit `use fn`
|
||||
compile_error! {
|
||||
"cannot have `storage input` combined with `use fn`"
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
@query_fn[
|
||||
storage($($storage:ident)*);
|
||||
method_name($method_name:ident);
|
||||
fn_path();
|
||||
$($rest:tt)*
|
||||
]
|
||||
) => {
|
||||
// default to `use fn $method_name`
|
||||
$crate::query_group! {
|
||||
@query_fn[
|
||||
storage($($storage)*);
|
||||
method_name($method_name);
|
||||
fn_path($method_name);
|
||||
$($rest)*
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Handle fns of one argument: once parenthesized patterns are stable on beta,
|
||||
// we can remove this special case.
|
||||
(
|
||||
@query_fn[
|
||||
storage($($storage:ident)*);
|
||||
method_name($method_name:ident);
|
||||
fn_path($fn_path:path);
|
||||
db_trait($DbTrait:path);
|
||||
query_type($QueryType:ty);
|
||||
key($key_name:ident: $key_ty:ty);
|
||||
]
|
||||
) => {
|
||||
impl<DB> $crate::plumbing::QueryFunction<DB> for $QueryType
|
||||
where DB: $DbTrait
|
||||
{
|
||||
fn execute(db: &DB, $key_name: <Self as $crate::Query<DB>>::Key)
|
||||
-> <Self as $crate::Query<DB>>::Value
|
||||
{
|
||||
$fn_path(db, $key_name)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle fns of N arguments: once parenthesized patterns are stable on beta,
|
||||
// we can use this code for all cases.
|
||||
(
|
||||
@query_fn[
|
||||
storage($($storage:ident)*);
|
||||
method_name($method_name:ident);
|
||||
fn_path($fn_path:path);
|
||||
db_trait($DbTrait:path);
|
||||
query_type($QueryType:ty);
|
||||
key($($key_name:ident: $key_ty:ty),*);
|
||||
]
|
||||
) => {
|
||||
impl<DB> $crate::plumbing::QueryFunction<DB> for $QueryType
|
||||
where DB: $DbTrait
|
||||
{
|
||||
fn execute(db: &DB, ($($key_name),*): <Self as $crate::Query<DB>>::Key)
|
||||
-> <Self as $crate::Query<DB>>::Value
|
||||
{
|
||||
$fn_path(db, $($key_name),*)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Recursive case: found some more part of the trait header.
|
||||
// Keep pulling out tokens until we find the body.
|
||||
(
|
||||
attr[$($attr:tt)*];
|
||||
headers[$($headers:tt)*];
|
||||
tokens[$token:tt $($tokens:tt)*];
|
||||
) => {
|
||||
$crate::query_group! {
|
||||
attr[$($attr)*];
|
||||
headers[$($headers)* $token];
|
||||
tokens[$($tokens)*];
|
||||
}
|
||||
};
|
||||
|
||||
// Generate storage type
|
||||
(
|
||||
// Default case:
|
||||
@storage_ty[$DB:ident, $Self:ident, ]
|
||||
) => {
|
||||
$crate::query_group! { @storage_ty[$DB, $Self, memoized] }
|
||||
};
|
||||
|
||||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, memoized]
|
||||
) => {
|
||||
$crate::plumbing::MemoizedStorage<$DB, $Self>
|
||||
};
|
||||
|
||||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, volatile]
|
||||
) => {
|
||||
$crate::plumbing::VolatileStorage<$DB, $Self>
|
||||
};
|
||||
|
||||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, dependencies]
|
||||
) => {
|
||||
$crate::plumbing::DependencyStorage<$DB, $Self>
|
||||
};
|
||||
|
||||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, input]
|
||||
) => {
|
||||
$crate::plumbing::InputStorage<DB, Self>
|
||||
};
|
||||
|
||||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, $storage:tt]
|
||||
) => {
|
||||
compile_error! {
|
||||
"invalid storage specification"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro generates the "query storage" that goes into your database.
|
||||
/// It requires you to list all of the query groups that you need as well
|
||||
/// as the queries within those groups. The format looks like so:
|
||||
|
@ -917,3 +630,10 @@ macro_rules! database_storage {
|
|||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// Re-export the procedural macros.
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate salsa_macros;
|
||||
#[doc(hidden)]
|
||||
pub use salsa_macros::*;
|
||||
|
|
|
@ -12,32 +12,23 @@ impl salsa::Database for DatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for DatabaseImpl {
|
||||
impl Database {
|
||||
fn memoized_a() for MemoizedA;
|
||||
fn memoized_b() for MemoizedB;
|
||||
fn volatile_a() for VolatileA;
|
||||
fn volatile_b() for VolatileB;
|
||||
fn memoized_a() for MemoizedAQuery;
|
||||
fn memoized_b() for MemoizedBQuery;
|
||||
fn volatile_a() for VolatileAQuery;
|
||||
fn volatile_b() for VolatileBQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
salsa::query_group! {
|
||||
trait Database: salsa::Database {
|
||||
// `a` and `b` depend on each other and form a cycle
|
||||
fn memoized_a() -> () {
|
||||
type MemoizedA;
|
||||
}
|
||||
fn memoized_b() -> () {
|
||||
type MemoizedB;
|
||||
}
|
||||
fn volatile_a() -> () {
|
||||
type VolatileA;
|
||||
storage volatile;
|
||||
}
|
||||
fn volatile_b() -> () {
|
||||
type VolatileB;
|
||||
storage volatile;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait Database: salsa::Database {
|
||||
// `a` and `b` depend on each other and form a cycle
|
||||
fn memoized_a(&self) -> ();
|
||||
fn memoized_b(&self) -> ();
|
||||
#[salsa::volatile]
|
||||
fn volatile_a(&self) -> ();
|
||||
#[salsa::volatile]
|
||||
fn volatile_b(&self) -> ();
|
||||
}
|
||||
|
||||
fn memoized_a(db: &impl Database) -> () {
|
||||
|
|
|
@ -16,13 +16,13 @@ impl salsa::Database for DatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for DatabaseImpl {
|
||||
impl group::GcDatabase {
|
||||
fn min() for group::Min;
|
||||
fn max() for group::Max;
|
||||
fn use_triangular() for group::UseTriangular;
|
||||
fn fibonacci() for group::Fibonacci;
|
||||
fn triangular() for group::Triangular;
|
||||
fn compute() for group::Compute;
|
||||
fn compute_all() for group::ComputeAll;
|
||||
fn min() for group::MinQuery;
|
||||
fn max() for group::MaxQuery;
|
||||
fn use_triangular() for group::UseTriangularQuery;
|
||||
fn fibonacci() for group::FibonacciQuery;
|
||||
fn triangular() for group::TriangularQuery;
|
||||
fn compute() for group::ComputeQuery;
|
||||
fn compute_all() for group::ComputeAllQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,19 @@ fn compute_one() {
|
|||
let mut db = db::DatabaseImpl::default();
|
||||
|
||||
// Will compute fibonacci(5)
|
||||
db.query_mut(UseTriangular).set(5, false);
|
||||
db.query_mut(UseTriangularQuery).set(5, false);
|
||||
db.compute(5);
|
||||
|
||||
db.salsa_runtime().next_revision();
|
||||
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (),
|
||||
Fibonacci => (0, 1, 2, 3, 4, 5),
|
||||
Compute => (5),
|
||||
UseTriangular => (5),
|
||||
Min => (),
|
||||
Max => (),
|
||||
TriangularQuery => (),
|
||||
FibonacciQuery => (0, 1, 2, 3, 4, 5),
|
||||
ComputeQuery => (5),
|
||||
UseTriangularQuery => (5),
|
||||
MinQuery => (),
|
||||
MaxQuery => (),
|
||||
}
|
||||
|
||||
// Memoized, but will compute fibonacci(5) again
|
||||
|
@ -39,12 +39,12 @@ fn compute_one() {
|
|||
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (),
|
||||
Fibonacci => (5),
|
||||
Compute => (5),
|
||||
UseTriangular => (5),
|
||||
Min => (),
|
||||
Max => (),
|
||||
TriangularQuery => (),
|
||||
FibonacciQuery => (5),
|
||||
ComputeQuery => (5),
|
||||
UseTriangularQuery => (5),
|
||||
MinQuery => (),
|
||||
MaxQuery => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,11 +53,11 @@ fn compute_switch() {
|
|||
let mut db = db::DatabaseImpl::default();
|
||||
|
||||
// Will compute fibonacci(5)
|
||||
db.query_mut(UseTriangular).set(5, false);
|
||||
db.query_mut(UseTriangularQuery).set(5, false);
|
||||
assert_eq!(db.compute(5), 5);
|
||||
|
||||
// Change to triangular mode
|
||||
db.query_mut(UseTriangular).set(5, true);
|
||||
db.query_mut(UseTriangularQuery).set(5, true);
|
||||
|
||||
// Now computes triangular(5)
|
||||
assert_eq!(db.compute(5), 15);
|
||||
|
@ -66,12 +66,12 @@ fn compute_switch() {
|
|||
// are not relevant to the most recent value of `Compute`
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (0, 1, 2, 3, 4, 5),
|
||||
Fibonacci => (0, 1, 2, 3, 4, 5),
|
||||
Compute => (5),
|
||||
UseTriangular => (5),
|
||||
Min => (),
|
||||
Max => (),
|
||||
TriangularQuery => (0, 1, 2, 3, 4, 5),
|
||||
FibonacciQuery => (0, 1, 2, 3, 4, 5),
|
||||
ComputeQuery => (5),
|
||||
UseTriangularQuery => (5),
|
||||
MinQuery => (),
|
||||
MaxQuery => (),
|
||||
}
|
||||
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
|
@ -79,12 +79,12 @@ fn compute_switch() {
|
|||
// Now we just have `Triangular` and not `Fibonacci`
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (0, 1, 2, 3, 4, 5),
|
||||
Fibonacci => (),
|
||||
Compute => (5),
|
||||
UseTriangular => (5),
|
||||
Min => (),
|
||||
Max => (),
|
||||
TriangularQuery => (0, 1, 2, 3, 4, 5),
|
||||
FibonacciQuery => (),
|
||||
ComputeQuery => (5),
|
||||
UseTriangularQuery => (5),
|
||||
MinQuery => (),
|
||||
MaxQuery => (),
|
||||
}
|
||||
|
||||
// Now run `compute` *again* in next revision.
|
||||
|
@ -95,12 +95,12 @@ fn compute_switch() {
|
|||
// We keep triangular, but just the outermost one.
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (5),
|
||||
Fibonacci => (),
|
||||
Compute => (5),
|
||||
UseTriangular => (5),
|
||||
Min => (),
|
||||
Max => (),
|
||||
TriangularQuery => (5),
|
||||
FibonacciQuery => (),
|
||||
ComputeQuery => (5),
|
||||
UseTriangularQuery => (5),
|
||||
MinQuery => (),
|
||||
MaxQuery => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,11 +110,11 @@ fn compute_all() {
|
|||
let mut db = db::DatabaseImpl::default();
|
||||
|
||||
for i in 0..6 {
|
||||
db.query_mut(UseTriangular).set(i, (i % 2) != 0);
|
||||
db.query_mut(UseTriangularQuery).set(i, (i % 2) != 0);
|
||||
}
|
||||
|
||||
db.query_mut(Min).set((), 0);
|
||||
db.query_mut(Max).set((), 6);
|
||||
db.query_mut(MinQuery).set((), 0);
|
||||
db.query_mut(MaxQuery).set((), 6);
|
||||
|
||||
db.compute_all();
|
||||
db.salsa_runtime().next_revision();
|
||||
|
@ -123,42 +123,42 @@ fn compute_all() {
|
|||
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (1, 3, 5),
|
||||
Fibonacci => (0, 2, 4),
|
||||
Compute => (0, 1, 2, 3, 4, 5),
|
||||
ComputeAll => (()),
|
||||
UseTriangular => (0, 1, 2, 3, 4, 5),
|
||||
Min => (()),
|
||||
Max => (()),
|
||||
TriangularQuery => (1, 3, 5),
|
||||
FibonacciQuery => (0, 2, 4),
|
||||
ComputeQuery => (0, 1, 2, 3, 4, 5),
|
||||
ComputeAllQuery => (()),
|
||||
UseTriangularQuery => (0, 1, 2, 3, 4, 5),
|
||||
MinQuery => (()),
|
||||
MaxQuery => (()),
|
||||
}
|
||||
|
||||
// Reduce the range to exclude index 5.
|
||||
db.query_mut(Max).set((), 5);
|
||||
db.query_mut(MaxQuery).set((), 5);
|
||||
db.compute_all();
|
||||
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (1, 3, 5),
|
||||
Fibonacci => (0, 2, 4),
|
||||
Compute => (0, 1, 2, 3, 4, 5),
|
||||
ComputeAll => (()),
|
||||
UseTriangular => (0, 1, 2, 3, 4, 5),
|
||||
Min => (()),
|
||||
Max => (()),
|
||||
TriangularQuery => (1, 3, 5),
|
||||
FibonacciQuery => (0, 2, 4),
|
||||
ComputeQuery => (0, 1, 2, 3, 4, 5),
|
||||
ComputeAllQuery => (()),
|
||||
UseTriangularQuery => (0, 1, 2, 3, 4, 5),
|
||||
MinQuery => (()),
|
||||
MaxQuery => (()),
|
||||
}
|
||||
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
|
||||
// We no longer used `Compute(5)` and `Triangular(5)`; note that
|
||||
// `UseTriangular(5)` is not collected, as it is an input.
|
||||
// `UseTriangularQuery(5)` is not collected, as it is an input.
|
||||
assert_keys! {
|
||||
db,
|
||||
Triangular => (1, 3),
|
||||
Fibonacci => (0, 2, 4),
|
||||
Compute => (0, 1, 2, 3, 4),
|
||||
ComputeAll => (()),
|
||||
UseTriangular => (0, 1, 2, 3, 4, 5),
|
||||
Min => (()),
|
||||
Max => (()),
|
||||
TriangularQuery => (1, 3),
|
||||
FibonacciQuery => (0, 2, 4),
|
||||
ComputeQuery => (0, 1, 2, 3, 4),
|
||||
ComputeAllQuery => (()),
|
||||
UseTriangularQuery => (0, 1, 2, 3, 4, 5),
|
||||
MinQuery => (()),
|
||||
MaxQuery => (()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::db;
|
||||
use crate::group::{Fibonacci, GcDatabase};
|
||||
use crate::group::{FibonacciQuery, GcDatabase};
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, SweepStrategy};
|
||||
|
||||
|
@ -9,7 +9,7 @@ fn sweep_default() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime().next_revision();
|
||||
|
@ -20,7 +20,7 @@ fn sweep_default() {
|
|||
// fibonacci is a constant, so it will not be invalidated,
|
||||
// hence we keep 3 and 5 but remove the rest.
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
let mut k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let mut k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
k.sort();
|
||||
assert_eq!(k, vec![3, 5]);
|
||||
|
||||
|
@ -31,7 +31,7 @@ fn sweep_default() {
|
|||
|
||||
// Same but we discard values this time.
|
||||
db.sweep_all(SweepStrategy::default().discard_values());
|
||||
let mut k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let mut k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
k.sort();
|
||||
assert_eq!(k, vec![3, 5]);
|
||||
|
||||
|
|
|
@ -1,38 +1,23 @@
|
|||
use crate::log::HasLog;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait GcDatabase: salsa::Database + HasLog {
|
||||
fn min() -> usize {
|
||||
type Min;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait GcDatabase: salsa::Database + HasLog {
|
||||
#[salsa::input]
|
||||
fn min(&self) -> usize;
|
||||
|
||||
fn max() -> usize {
|
||||
type Max;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::input]
|
||||
fn max(&self) -> usize;
|
||||
|
||||
fn use_triangular(key: usize) -> bool {
|
||||
type UseTriangular;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::input]
|
||||
fn use_triangular(&self, key: usize) -> bool;
|
||||
|
||||
fn fibonacci(key: usize) -> usize {
|
||||
type Fibonacci;
|
||||
}
|
||||
fn fibonacci(&self, key: usize) -> usize;
|
||||
|
||||
fn triangular(key: usize) -> usize {
|
||||
type Triangular;
|
||||
}
|
||||
fn triangular(&self, key: usize) -> usize;
|
||||
|
||||
fn compute(key: usize) -> usize {
|
||||
type Compute;
|
||||
}
|
||||
fn compute(&self, key: usize) -> usize;
|
||||
|
||||
fn compute_all() -> Vec<usize> {
|
||||
type ComputeAll;
|
||||
}
|
||||
}
|
||||
fn compute_all(&self) -> Vec<usize>;
|
||||
}
|
||||
|
||||
fn fibonacci(db: &impl GcDatabase, key: usize) -> usize {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::db;
|
||||
use crate::group::{Fibonacci, GcDatabase};
|
||||
use crate::group::{FibonacciQuery, GcDatabase};
|
||||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::{Database, SweepStrategy};
|
||||
|
||||
|
@ -13,7 +13,7 @@ fn one_rev() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
// Everything was used in this revision, so
|
||||
|
@ -28,7 +28,7 @@ fn two_rev_nothing() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime().next_revision();
|
||||
|
@ -37,7 +37,7 @@ fn two_rev_nothing() {
|
|||
// everything gets collected.
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 0);
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fn two_rev_one_use() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime().next_revision();
|
||||
|
@ -58,7 +58,7 @@ fn two_rev_one_use() {
|
|||
// hence we keep `fibonacci(5)` but remove 0..=4.
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k, vec![5]);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ fn two_rev_two_uses() {
|
|||
|
||||
db.fibonacci(5);
|
||||
|
||||
let k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
assert_eq!(k.len(), 6);
|
||||
|
||||
db.salsa_runtime().next_revision();
|
||||
|
@ -80,7 +80,7 @@ fn two_rev_two_uses() {
|
|||
// hence we keep 3 and 5 but remove the rest.
|
||||
db.sweep_all(SweepStrategy::default());
|
||||
|
||||
let mut k: Vec<_> = db.query(Fibonacci).keys();
|
||||
let mut k: Vec<_> = db.query(FibonacciQuery).keys();
|
||||
k.sort();
|
||||
assert_eq!(k, vec![3, 5]);
|
||||
}
|
||||
|
|
|
@ -2,17 +2,12 @@ use crate::implementation::{TestContext, TestContextImpl};
|
|||
use salsa::debug::DebugQueryTable;
|
||||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait ConstantsDatabase: TestContext {
|
||||
fn constants_input(key: char) -> usize {
|
||||
type ConstantsInput;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait ConstantsDatabase: TestContext {
|
||||
#[salsa::input]
|
||||
fn constants_input(&self, key: char) -> usize;
|
||||
|
||||
fn constants_add(keys: (char, char)) -> usize {
|
||||
type ConstantsAdd;
|
||||
}
|
||||
}
|
||||
fn constants_add(&self, keys: (char, char)) -> usize;
|
||||
}
|
||||
|
||||
fn constants_add(db: &impl ConstantsDatabase, (key1, key2): (char, char)) -> usize {
|
||||
|
@ -24,8 +19,8 @@ fn constants_add(db: &impl ConstantsDatabase, (key1, key2): (char, char)) -> usi
|
|||
#[should_panic]
|
||||
fn invalidate_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
db.query_mut(ConstantsInput).set_constant('a', 44);
|
||||
db.query_mut(ConstantsInput).set_constant('a', 66);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 44);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 66);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -34,13 +29,13 @@ fn invalidate_constant_1() {
|
|||
let db = &mut TestContextImpl::default();
|
||||
|
||||
// Not constant:
|
||||
db.query_mut(ConstantsInput).set('a', 44);
|
||||
db.query_mut(ConstantsInputQuery).set('a', 44);
|
||||
|
||||
// Becomes constant:
|
||||
db.query_mut(ConstantsInput).set_constant('a', 44);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 44);
|
||||
|
||||
// Invalidates:
|
||||
db.query_mut(ConstantsInput).set_constant('a', 66);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 66);
|
||||
}
|
||||
|
||||
/// Test that invoking `set` on a constant is an error, even if you
|
||||
|
@ -49,54 +44,54 @@ fn invalidate_constant_1() {
|
|||
#[should_panic]
|
||||
fn set_after_constant_same_value() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
db.query_mut(ConstantsInput).set_constant('a', 44);
|
||||
db.query_mut(ConstantsInput).set('a', 44);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 44);
|
||||
db.query_mut(ConstantsInputQuery).set('a', 44);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(ConstantsInput).set('a', 22);
|
||||
db.query_mut(ConstantsInput).set('b', 44);
|
||||
db.query_mut(ConstantsInputQuery).set('a', 22);
|
||||
db.query_mut(ConstantsInputQuery).set('b', 44);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 66);
|
||||
assert!(!db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(!db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(ConstantsInput).set_constant('a', 22);
|
||||
db.query_mut(ConstantsInput).set_constant('b', 44);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 22);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('b', 44);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 66);
|
||||
assert!(db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_constant() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(ConstantsInput).set_constant('a', 22);
|
||||
db.query_mut(ConstantsInput).set('b', 44);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 22);
|
||||
db.query_mut(ConstantsInputQuery).set('b', 44);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 66);
|
||||
assert!(!db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(!db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn becomes_constant_with_change() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(ConstantsInput).set('a', 22);
|
||||
db.query_mut(ConstantsInput).set('b', 44);
|
||||
db.query_mut(ConstantsInputQuery).set('a', 22);
|
||||
db.query_mut(ConstantsInputQuery).set('b', 44);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 66);
|
||||
assert!(!db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(!db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
|
||||
db.query_mut(ConstantsInput).set_constant('a', 23);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('a', 23);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 67);
|
||||
assert!(!db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(!db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
|
||||
db.query_mut(ConstantsInput).set_constant('b', 45);
|
||||
db.query_mut(ConstantsInputQuery).set_constant('b', 45);
|
||||
assert_eq!(db.constants_add(('a', 'b')), 68);
|
||||
assert!(db.query(ConstantsAdd).is_constant(('a', 'b')));
|
||||
assert!(db.query(ConstantsAddQuery).is_constant(('a', 'b')));
|
||||
}
|
||||
|
|
|
@ -41,28 +41,28 @@ impl TestContextImpl {
|
|||
salsa::database_storage! {
|
||||
pub(crate) struct TestContextImplStorage for TestContextImpl {
|
||||
impl constants::ConstantsDatabase {
|
||||
fn constants_input() for constants::ConstantsInput;
|
||||
fn constants_derived() for constants::ConstantsAdd;
|
||||
fn constants_input() for constants::ConstantsInputQuery;
|
||||
fn constants_derived() for constants::ConstantsAddQuery;
|
||||
}
|
||||
|
||||
impl memoized_dep_inputs::MemoizedDepInputsContext {
|
||||
fn dep_memoized2() for memoized_dep_inputs::Memoized2;
|
||||
fn dep_memoized1() for memoized_dep_inputs::Memoized1;
|
||||
fn dep_derived1() for memoized_dep_inputs::Derived1;
|
||||
fn dep_input1() for memoized_dep_inputs::Input1;
|
||||
fn dep_input2() for memoized_dep_inputs::Input2;
|
||||
fn dep_memoized2() for memoized_dep_inputs::DepMemoized2Query;
|
||||
fn dep_memoized1() for memoized_dep_inputs::DepMemoized1Query;
|
||||
fn dep_derived1() for memoized_dep_inputs::DepDerived1Query;
|
||||
fn dep_input1() for memoized_dep_inputs::DepInput1Query;
|
||||
fn dep_input2() for memoized_dep_inputs::DepInput2Query;
|
||||
}
|
||||
|
||||
impl memoized_inputs::MemoizedInputsContext {
|
||||
fn max() for memoized_inputs::Max;
|
||||
fn input1() for memoized_inputs::Input1;
|
||||
fn input2() for memoized_inputs::Input2;
|
||||
fn max() for memoized_inputs::MaxQuery;
|
||||
fn input1() for memoized_inputs::Input1Query;
|
||||
fn input2() for memoized_inputs::Input2Query;
|
||||
}
|
||||
|
||||
impl memoized_volatile::MemoizedVolatileContext {
|
||||
fn memoized2() for memoized_volatile::Memoized2;
|
||||
fn memoized1() for memoized_volatile::Memoized1;
|
||||
fn volatile() for memoized_volatile::Volatile;
|
||||
fn memoized2() for memoized_volatile::Memoized2Query;
|
||||
fn memoized1() for memoized_volatile::Memoized1Query;
|
||||
fn volatile() for memoized_volatile::VolatileQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
use crate::implementation::{TestContext, TestContextImpl};
|
||||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait MemoizedDepInputsContext: TestContext {
|
||||
fn dep_memoized2() -> usize {
|
||||
type Memoized2;
|
||||
}
|
||||
fn dep_memoized1() -> usize {
|
||||
type Memoized1;
|
||||
}
|
||||
fn dep_derived1() -> usize {
|
||||
type Derived1;
|
||||
storage dependencies;
|
||||
}
|
||||
fn dep_input1() -> usize {
|
||||
type Input1;
|
||||
storage input;
|
||||
}
|
||||
fn dep_input2() -> usize {
|
||||
type Input2;
|
||||
storage input;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait MemoizedDepInputsContext: TestContext {
|
||||
fn dep_memoized2(&self) -> usize;
|
||||
fn dep_memoized1(&self) -> usize;
|
||||
#[salsa::dependencies]
|
||||
fn dep_derived1(&self) -> usize;
|
||||
#[salsa::input]
|
||||
fn dep_input1(&self) -> usize;
|
||||
#[salsa::input]
|
||||
fn dep_input2(&self) -> usize;
|
||||
}
|
||||
|
||||
fn dep_memoized2(db: &impl MemoizedDepInputsContext) -> usize {
|
||||
|
@ -43,7 +32,7 @@ fn dep_derived1(db: &impl MemoizedDepInputsContext) -> usize {
|
|||
fn revalidate() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(Input1).set((), 0);
|
||||
db.query_mut(DepInput1Query).set((), 0);
|
||||
|
||||
// Initial run starts from Memoized2:
|
||||
let v = db.dep_memoized2();
|
||||
|
@ -53,19 +42,19 @@ fn revalidate() {
|
|||
// After that, we first try to validate Memoized1 but wind up
|
||||
// running Memoized2. Note that we don't try to validate
|
||||
// Derived1, so it is invoked by Memoized1.
|
||||
db.query_mut(Input1).set((), 44);
|
||||
db.query_mut(DepInput1Query).set((), 44);
|
||||
let v = db.dep_memoized2();
|
||||
assert_eq!(v, 44);
|
||||
db.assert_log(&["Memoized1 invoked", "Derived1 invoked", "Memoized2 invoked"]);
|
||||
|
||||
// Here validation of Memoized1 succeeds so Memoized2 never runs.
|
||||
db.query_mut(Input1).set((), 45);
|
||||
db.query_mut(DepInput1Query).set((), 45);
|
||||
let v = db.dep_memoized2();
|
||||
assert_eq!(v, 44);
|
||||
db.assert_log(&["Memoized1 invoked", "Derived1 invoked"]);
|
||||
|
||||
// Here, a change to input2 doesn't affect us, so nothing runs.
|
||||
db.query_mut(Input2).set((), 45);
|
||||
db.query_mut(DepInput2Query).set((), 45);
|
||||
let v = db.dep_memoized2();
|
||||
assert_eq!(v, 44);
|
||||
db.assert_log(&[]);
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
use crate::implementation::{TestContext, TestContextImpl};
|
||||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait MemoizedInputsContext: TestContext {
|
||||
fn max() -> usize {
|
||||
type Max;
|
||||
}
|
||||
fn input1() -> usize {
|
||||
type Input1;
|
||||
storage input;
|
||||
}
|
||||
fn input2() -> usize {
|
||||
type Input2;
|
||||
storage input;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait MemoizedInputsContext: TestContext {
|
||||
fn max(&self) -> usize;
|
||||
#[salsa::input]
|
||||
fn input1(&self) -> usize;
|
||||
#[salsa::input]
|
||||
fn input2(&self) -> usize;
|
||||
}
|
||||
|
||||
fn max(db: &impl MemoizedInputsContext) -> usize {
|
||||
|
@ -26,8 +19,8 @@ fn max(db: &impl MemoizedInputsContext) -> usize {
|
|||
fn revalidate() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(Input1).set((), 0);
|
||||
db.query_mut(Input2).set((), 0);
|
||||
db.query_mut(Input1Query).set((), 0);
|
||||
db.query_mut(Input2Query).set((), 0);
|
||||
|
||||
let v = db.max();
|
||||
assert_eq!(v, 0);
|
||||
|
@ -37,7 +30,7 @@ fn revalidate() {
|
|||
assert_eq!(v, 0);
|
||||
db.assert_log(&[]);
|
||||
|
||||
db.query_mut(Input1).set((), 44);
|
||||
db.query_mut(Input1Query).set((), 44);
|
||||
db.assert_log(&[]);
|
||||
|
||||
let v = db.max();
|
||||
|
@ -48,11 +41,11 @@ fn revalidate() {
|
|||
assert_eq!(v, 44);
|
||||
db.assert_log(&[]);
|
||||
|
||||
db.query_mut(Input1).set((), 44);
|
||||
db.query_mut(Input1Query).set((), 44);
|
||||
db.assert_log(&[]);
|
||||
db.query_mut(Input2).set((), 66);
|
||||
db.query_mut(Input2Query).set((), 66);
|
||||
db.assert_log(&[]);
|
||||
db.query_mut(Input1).set((), 64);
|
||||
db.query_mut(Input1Query).set((), 64);
|
||||
db.assert_log(&[]);
|
||||
|
||||
let v = db.max();
|
||||
|
@ -70,14 +63,14 @@ fn revalidate() {
|
|||
fn set_after_no_change() {
|
||||
let db = &mut TestContextImpl::default();
|
||||
|
||||
db.query_mut(Input2).set((), 0);
|
||||
db.query_mut(Input2Query).set((), 0);
|
||||
|
||||
db.query_mut(Input1).set((), 44);
|
||||
db.query_mut(Input1Query).set((), 44);
|
||||
let v = db.max();
|
||||
assert_eq!(v, 44);
|
||||
db.assert_log(&["Max invoked"]);
|
||||
|
||||
db.query_mut(Input1).set((), 44);
|
||||
db.query_mut(Input1Query).set((), 44);
|
||||
let v = db.max();
|
||||
assert_eq!(v, 44);
|
||||
db.assert_log(&["Max invoked"]);
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
use crate::implementation::{TestContext, TestContextImpl};
|
||||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait MemoizedVolatileContext: TestContext {
|
||||
// Queries for testing a "volatile" value wrapped by
|
||||
// memoization.
|
||||
fn memoized2() -> usize {
|
||||
type Memoized2;
|
||||
}
|
||||
fn memoized1() -> usize {
|
||||
type Memoized1;
|
||||
}
|
||||
fn volatile() -> usize {
|
||||
type Volatile;
|
||||
storage volatile;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait MemoizedVolatileContext: TestContext {
|
||||
// Queries for testing a "volatile" value wrapped by
|
||||
// memoization.
|
||||
fn memoized2(&self) -> usize;
|
||||
fn memoized1(&self) -> usize;
|
||||
#[salsa::volatile]
|
||||
fn volatile(&self) -> usize;
|
||||
}
|
||||
|
||||
fn memoized2(db: &impl MemoizedVolatileContext) -> usize {
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
salsa::query_group! {
|
||||
trait MyDatabase: salsa::Database {
|
||||
fn my_query(key: ()) -> () {
|
||||
type MyQuery;
|
||||
use fn another_module::another_name;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait MyDatabase: salsa::Database {
|
||||
#[salsa::invoke(another_module::another_name)]
|
||||
fn my_query(&self, key: ()) -> ();
|
||||
}
|
||||
|
||||
mod another_module {
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
use salsa::{Database, ParallelDatabase, Snapshot};
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
|
||||
salsa::query_group! {
|
||||
trait PanicSafelyDatabase: salsa::Database {
|
||||
fn one() -> usize {
|
||||
type One;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait PanicSafelyDatabase: salsa::Database {
|
||||
#[salsa::input]
|
||||
fn one(&self) -> usize;
|
||||
|
||||
fn panic_safely() -> () {
|
||||
type PanicSafely;
|
||||
}
|
||||
}
|
||||
fn panic_safely(&self) -> ();
|
||||
}
|
||||
|
||||
fn panic_safely(db: &impl PanicSafelyDatabase) -> () {
|
||||
|
@ -40,8 +35,8 @@ impl salsa::ParallelDatabase for DatabaseStruct {
|
|||
salsa::database_storage! {
|
||||
struct DatabaseStorage for DatabaseStruct {
|
||||
impl PanicSafelyDatabase {
|
||||
fn one() for One;
|
||||
fn panic_safely() for PanicSafely;
|
||||
fn one() for OneQuery;
|
||||
fn panic_safely() for PanicSafelyQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +54,7 @@ fn should_panic_safely() {
|
|||
assert!(result.is_err());
|
||||
|
||||
// Set `db.one` to 1 and assert ok
|
||||
db.query_mut(One).set((), 1);
|
||||
db.query_mut(OneQuery).set((), 1);
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| db.panic_safely()));
|
||||
assert!(result.is_ok())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::setup::{
|
||||
CancelationFlag, Canceled, Input, Knobs, ParDatabase, ParDatabaseImpl, WithValue,
|
||||
CancelationFlag, Canceled, InputQuery, Knobs, ParDatabase, ParDatabaseImpl, WithValue,
|
||||
};
|
||||
use salsa::{Database, ParallelDatabase};
|
||||
|
||||
|
@ -34,10 +34,10 @@ fn in_par_get_set_cancellation_immediate() {
|
|||
check_cancelation(|flag| {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(Input).set('d', 0);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
db.query_mut(InputQuery).set('d', 0);
|
||||
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
|
@ -56,7 +56,7 @@ fn in_par_get_set_cancellation_immediate() {
|
|||
db.wait_for(1);
|
||||
|
||||
// Try to set the input. This will signal cancellation.
|
||||
db.query_mut(Input).set('d', 1000);
|
||||
db.query_mut(InputQuery).set('d', 1000);
|
||||
|
||||
// This should re-compute the value (even though no input has changed).
|
||||
let thread2 = std::thread::spawn({
|
||||
|
@ -77,10 +77,10 @@ fn in_par_get_set_cancellation_transitive() {
|
|||
check_cancelation(|flag| {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(Input).set('d', 0);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
db.query_mut(InputQuery).set('d', 0);
|
||||
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
|
@ -99,7 +99,7 @@ fn in_par_get_set_cancellation_transitive() {
|
|||
db.wait_for(1);
|
||||
|
||||
// Try to set the input. This will signal cancellation.
|
||||
db.query_mut(Input).set('d', 1000);
|
||||
db.query_mut(InputQuery).set('d', 1000);
|
||||
|
||||
// This should re-compute the value (even though no input has changed).
|
||||
let thread2 = std::thread::spawn({
|
||||
|
@ -119,7 +119,7 @@ fn no_back_dating_in_cancellation() {
|
|||
check_cancelation(|flag| {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 1);
|
||||
db.query_mut(InputQuery).set('a', 1);
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
move || {
|
||||
|
@ -136,7 +136,7 @@ fn no_back_dating_in_cancellation() {
|
|||
db.wait_for(1);
|
||||
|
||||
// Set unrelated input to bump revision
|
||||
db.query_mut(Input).set('b', 2);
|
||||
db.query_mut(InputQuery).set('b', 2);
|
||||
|
||||
// Here we should recompuet the whole chain again, clearing the cancellation
|
||||
// state. If we get `usize::max()` here, it is a bug!
|
||||
|
@ -144,8 +144,8 @@ fn no_back_dating_in_cancellation() {
|
|||
|
||||
assert_canceled!(flag, thread1);
|
||||
|
||||
db.query_mut(Input).set('a', 3);
|
||||
db.query_mut(Input).set('a', 4);
|
||||
db.query_mut(InputQuery).set('a', 3);
|
||||
db.query_mut(InputQuery).set('a', 4);
|
||||
assert_eq!(db.sum3("ab"), 6);
|
||||
})
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ fn no_back_dating_in_cancellation() {
|
|||
fn transitive_cancellation() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 1);
|
||||
db.query_mut(InputQuery).set('a', 1);
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
move || {
|
||||
|
@ -176,7 +176,7 @@ fn transitive_cancellation() {
|
|||
|
||||
db.wait_for(1);
|
||||
|
||||
db.query_mut(Input).set('b', 2);
|
||||
db.query_mut(InputQuery).set('b', 2);
|
||||
|
||||
// Check that when we call `sum3_drop_sum` we don't wind up having
|
||||
// to actually re-execute it, because the result of `sum2` winds
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::setup::{Input, ParDatabase, ParDatabaseImpl};
|
||||
use crate::setup::{InputQuery, ParDatabase, ParDatabaseImpl};
|
||||
use crate::signal::Signal;
|
||||
use salsa::{Database, ParallelDatabase};
|
||||
use std::sync::Arc;
|
||||
|
@ -10,7 +10,7 @@ use std::sync::Arc;
|
|||
fn in_par_get_set_cancellation() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 1);
|
||||
db.query_mut(InputQuery).set('a', 1);
|
||||
|
||||
let signal = Arc::new(Signal::default());
|
||||
|
||||
|
@ -50,7 +50,7 @@ fn in_par_get_set_cancellation() {
|
|||
signal.wait_for(1);
|
||||
|
||||
// This will block until thread1 drops the revision lock.
|
||||
db.query_mut(Input).set('a', 2);
|
||||
db.query_mut(InputQuery).set('a', 2);
|
||||
|
||||
db.input('a')
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::setup::{Input, ParDatabase, ParDatabaseImpl};
|
||||
use crate::setup::{InputQuery, ParDatabase, ParDatabaseImpl};
|
||||
use salsa::{Database, ParallelDatabase};
|
||||
|
||||
/// Test two `sum` queries (on distinct keys) executing in different
|
||||
|
@ -7,12 +7,12 @@ use salsa::{Database, ParallelDatabase};
|
|||
fn in_par_two_independent_queries() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(Input).set('d', 200);
|
||||
db.query_mut(Input).set('e', 020);
|
||||
db.query_mut(Input).set('f', 002);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
db.query_mut(InputQuery).set('d', 200);
|
||||
db.query_mut(InputQuery).set('e', 020);
|
||||
db.query_mut(InputQuery).set('f', 002);
|
||||
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::setup::{Input, ParDatabase, ParDatabaseImpl};
|
||||
use crate::setup::{InputQuery, ParDatabase, ParDatabaseImpl};
|
||||
use salsa::{Database, ParallelDatabase};
|
||||
|
||||
/// Test where a read and a set are racing with one another.
|
||||
|
@ -7,9 +7,9 @@ use salsa::{Database, ParallelDatabase};
|
|||
fn in_par_get_set_race() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
|
||||
let thread1 = std::thread::spawn({
|
||||
let db = db.snapshot();
|
||||
|
@ -20,7 +20,7 @@ fn in_par_get_set_race() {
|
|||
});
|
||||
|
||||
let thread2 = std::thread::spawn(move || {
|
||||
db.query_mut(Input).set('a', 1000);
|
||||
db.query_mut(InputQuery).set('a', 1000);
|
||||
db.sum("a")
|
||||
});
|
||||
|
||||
|
|
|
@ -5,41 +5,26 @@ use salsa::Snapshot;
|
|||
use std::cell::Cell;
|
||||
use std::sync::Arc;
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait ParDatabase: Knobs + salsa::ParallelDatabase {
|
||||
fn input(key: char) -> usize {
|
||||
type Input;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait ParDatabase: Knobs + salsa::ParallelDatabase {
|
||||
#[salsa::input]
|
||||
fn input(&self, key: char) -> usize;
|
||||
|
||||
fn sum(key: &'static str) -> usize {
|
||||
type Sum;
|
||||
}
|
||||
fn sum(&self, key: &'static str) -> usize;
|
||||
|
||||
/// Invokes `sum`
|
||||
fn sum2(key: &'static str) -> usize {
|
||||
type Sum2;
|
||||
}
|
||||
/// Invokes `sum`
|
||||
fn sum2(&self, key: &'static str) -> usize;
|
||||
|
||||
/// Invokes `sum` but doesn't really care about the result.
|
||||
fn sum2_drop_sum(key: &'static str) -> usize {
|
||||
type Sum2Drop;
|
||||
}
|
||||
/// Invokes `sum` but doesn't really care about the result.
|
||||
fn sum2_drop_sum(&self, key: &'static str) -> usize;
|
||||
|
||||
/// Invokes `sum2`
|
||||
fn sum3(key: &'static str) -> usize {
|
||||
type Sum3;
|
||||
}
|
||||
/// Invokes `sum2`
|
||||
fn sum3(&self, key: &'static str) -> usize;
|
||||
|
||||
/// Invokes `sum2_drop_sum`
|
||||
fn sum3_drop_sum(key: &'static str) -> usize {
|
||||
type Sum3Drop;
|
||||
}
|
||||
/// Invokes `sum2_drop_sum`
|
||||
fn sum3_drop_sum(&self, key: &'static str) -> usize;
|
||||
|
||||
fn snapshot_me() -> () {
|
||||
type SnapshotMe;
|
||||
}
|
||||
}
|
||||
fn snapshot_me(&self) -> ();
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
|
@ -252,13 +237,13 @@ impl Knobs for ParDatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for ParDatabaseImpl {
|
||||
impl ParDatabase {
|
||||
fn input() for Input;
|
||||
fn sum() for Sum;
|
||||
fn sum2() for Sum2;
|
||||
fn sum2_drop_sum() for Sum2Drop;
|
||||
fn sum3() for Sum3;
|
||||
fn sum3_drop_sum() for Sum3Drop;
|
||||
fn snapshot_me() for SnapshotMe;
|
||||
fn input() for InputQuery;
|
||||
fn sum() for SumQuery;
|
||||
fn sum2() for Sum2Query;
|
||||
fn sum2_drop_sum() for Sum2DropSumQuery;
|
||||
fn sum3() for Sum3Query;
|
||||
fn sum3_drop_sum() for Sum3DropSumQuery;
|
||||
fn snapshot_me() for SnapshotMeQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,21 +12,14 @@ const N_READER_OPS: usize = 100;
|
|||
struct Canceled;
|
||||
type Cancelable<T> = Result<T, Canceled>;
|
||||
|
||||
salsa::query_group! {
|
||||
trait StressDatabase: salsa::Database {
|
||||
fn a(key: usize) -> usize {
|
||||
type A;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait StressDatabase: salsa::Database {
|
||||
#[salsa::input]
|
||||
fn a(&self, key: usize) -> usize;
|
||||
|
||||
fn b(key: usize) -> Cancelable<usize> {
|
||||
type B;
|
||||
}
|
||||
fn b(&self, key: usize) -> Cancelable<usize>;
|
||||
|
||||
fn c(key: usize) -> Cancelable<usize> {
|
||||
type C;
|
||||
}
|
||||
}
|
||||
fn c(&self, key: usize) -> Cancelable<usize>;
|
||||
}
|
||||
|
||||
fn b(db: &impl StressDatabase, key: usize) -> Cancelable<usize> {
|
||||
|
@ -62,9 +55,9 @@ impl salsa::ParallelDatabase for StressDatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for StressDatabaseImpl {
|
||||
impl StressDatabase {
|
||||
fn a() for A;
|
||||
fn b() for B;
|
||||
fn c() for C;
|
||||
fn a() for AQuery;
|
||||
fn b() for BQuery;
|
||||
fn c() for CQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +150,7 @@ impl WriteOp {
|
|||
fn execute(self, db: &mut StressDatabaseImpl) {
|
||||
match self {
|
||||
WriteOp::SetA(key, value) => {
|
||||
db.query_mut(A).set(key, value);
|
||||
db.query_mut(AQuery).set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,13 +172,13 @@ impl ReadOp {
|
|||
},
|
||||
ReadOp::Gc(query, strategy) => match query {
|
||||
Query::A => {
|
||||
db.query(A).sweep(strategy);
|
||||
db.query(AQuery).sweep(strategy);
|
||||
}
|
||||
Query::B => {
|
||||
db.query(B).sweep(strategy);
|
||||
db.query(BQuery).sweep(strategy);
|
||||
}
|
||||
Query::C => {
|
||||
db.query(C).sweep(strategy);
|
||||
db.query(CQuery).sweep(strategy);
|
||||
}
|
||||
},
|
||||
ReadOp::GcAll(strategy) => {
|
||||
|
@ -199,7 +192,7 @@ impl ReadOp {
|
|||
fn stress_test() {
|
||||
let mut db = StressDatabaseImpl::default();
|
||||
for i in 0..10 {
|
||||
db.query_mut(A).set(i, i);
|
||||
db.query_mut(AQuery).set(i, i);
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::setup::{Input, Knobs, ParDatabase, ParDatabaseImpl, WithValue};
|
||||
use crate::setup::{InputQuery, Knobs, ParDatabase, ParDatabaseImpl, WithValue};
|
||||
use salsa::{Database, ParallelDatabase};
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
|
||||
|
@ -10,9 +10,9 @@ use std::panic::{self, AssertUnwindSafe};
|
|||
fn true_parallel_different_keys() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
|
||||
// Thread 1 will signal stage 1 when it enters and wait for stage 2.
|
||||
let thread1 = std::thread::spawn({
|
||||
|
@ -50,9 +50,9 @@ fn true_parallel_different_keys() {
|
|||
fn true_parallel_same_keys() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 100);
|
||||
db.query_mut(Input).set('b', 010);
|
||||
db.query_mut(Input).set('c', 001);
|
||||
db.query_mut(InputQuery).set('a', 100);
|
||||
db.query_mut(InputQuery).set('b', 010);
|
||||
db.query_mut(InputQuery).set('c', 001);
|
||||
|
||||
// Thread 1 will wait_for a barrier in the start of `sum`
|
||||
let thread1 = std::thread::spawn({
|
||||
|
@ -91,7 +91,7 @@ fn true_parallel_same_keys() {
|
|||
fn true_parallel_propagate_panic() {
|
||||
let mut db = ParDatabaseImpl::default();
|
||||
|
||||
db.query_mut(Input).set('a', 1);
|
||||
db.query_mut(InputQuery).set('a', 1);
|
||||
|
||||
// `thread1` will wait_for a barrier in the start of `sum`. Once it can
|
||||
// continue, it will panic.
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
fn input() -> String {
|
||||
type Input;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
#[salsa::input]
|
||||
fn input(&self) -> String;
|
||||
|
||||
fn length() -> usize {
|
||||
type Length;
|
||||
}
|
||||
fn length(&self) -> usize;
|
||||
|
||||
fn double_length() -> usize {
|
||||
type DoubleLength;
|
||||
}
|
||||
}
|
||||
fn double_length(&self) -> usize;
|
||||
}
|
||||
|
||||
fn length(db: &impl HelloWorldDatabase) -> usize {
|
||||
|
@ -41,9 +34,9 @@ impl salsa::Database for DatabaseStruct {
|
|||
salsa::database_storage! {
|
||||
struct DatabaseStorage for DatabaseStruct {
|
||||
impl HelloWorldDatabase {
|
||||
fn input() for Input;
|
||||
fn length() for Length;
|
||||
fn double_length() for DoubleLength;
|
||||
fn input() for InputQuery;
|
||||
fn length() for LengthQuery;
|
||||
fn double_length() for DoubleLengthQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +44,9 @@ salsa::database_storage! {
|
|||
#[test]
|
||||
fn normal() {
|
||||
let mut db = DatabaseStruct::default();
|
||||
db.query_mut(Input).set((), format!("Hello, world"));
|
||||
db.query_mut(InputQuery).set((), format!("Hello, world"));
|
||||
assert_eq!(db.double_length(), 24);
|
||||
db.query_mut(Input).set((), format!("Hello, world!"));
|
||||
db.query_mut(InputQuery).set((), format!("Hello, world!"));
|
||||
assert_eq!(db.double_length(), 26);
|
||||
}
|
||||
|
||||
|
@ -67,7 +60,7 @@ fn use_without_set() {
|
|||
#[test]
|
||||
fn using_set_unchecked_on_input() {
|
||||
let mut db = DatabaseStruct::default();
|
||||
db.query_mut(Input)
|
||||
db.query_mut(InputQuery)
|
||||
.set_unchecked((), format!("Hello, world"));
|
||||
assert_eq!(db.double_length(), 24);
|
||||
}
|
||||
|
@ -75,12 +68,12 @@ fn using_set_unchecked_on_input() {
|
|||
#[test]
|
||||
fn using_set_unchecked_on_input_after() {
|
||||
let mut db = DatabaseStruct::default();
|
||||
db.query_mut(Input).set((), format!("Hello, world"));
|
||||
db.query_mut(InputQuery).set((), format!("Hello, world"));
|
||||
assert_eq!(db.double_length(), 24);
|
||||
|
||||
// If we use `set_unchecked`, we don't notice that `double_length`
|
||||
// is out of date. Oh well, don't do that.
|
||||
db.query_mut(Input)
|
||||
db.query_mut(InputQuery)
|
||||
.set_unchecked((), format!("Hello, world!"));
|
||||
assert_eq!(db.double_length(), 24);
|
||||
}
|
||||
|
@ -91,7 +84,7 @@ fn using_set_unchecked() {
|
|||
|
||||
// Use `set_unchecked` to intentionally set the wrong value,
|
||||
// demonstrating that the code never runs.
|
||||
db.query_mut(Length).set_unchecked((), 24);
|
||||
db.query_mut(LengthQuery).set_unchecked((), 24);
|
||||
|
||||
assert_eq!(db.double_length(), 48);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ pub struct DatabaseImpl {
|
|||
salsa::database_storage! {
|
||||
pub struct DatabaseImplStorage for DatabaseImpl {
|
||||
impl queries::Database {
|
||||
fn memoized() for queries::Memoized;
|
||||
fn volatile() for queries::Volatile;
|
||||
fn memoized() for queries::MemoizedQuery;
|
||||
fn volatile() for queries::VolatileQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,11 @@ pub(crate) trait Counter: salsa::Database {
|
|||
fn increment(&self) -> usize;
|
||||
}
|
||||
|
||||
salsa::query_group! {
|
||||
pub(crate) trait Database: Counter {
|
||||
fn memoized() -> usize {
|
||||
type Memoized;
|
||||
}
|
||||
fn volatile() -> usize {
|
||||
type Volatile;
|
||||
storage volatile;
|
||||
}
|
||||
}
|
||||
#[salsa::query_group]
|
||||
pub(crate) trait Database: Counter {
|
||||
fn memoized(&self) -> usize;
|
||||
#[salsa::volatile]
|
||||
fn volatile(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Because this query is memoized, we only increment the counter
|
||||
|
|
|
@ -1,28 +1,17 @@
|
|||
use salsa::Database;
|
||||
|
||||
salsa::query_group! {
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
fn input(a: u32, b: u32) -> u32 {
|
||||
type Input;
|
||||
storage input;
|
||||
}
|
||||
#[salsa::query_group]
|
||||
trait HelloWorldDatabase: salsa::Database {
|
||||
#[salsa::input]
|
||||
fn input(&self, a: u32, b: u32) -> u32;
|
||||
|
||||
fn none() -> u32 {
|
||||
type None;
|
||||
}
|
||||
fn none(&self) -> u32;
|
||||
|
||||
fn one(k: u32) -> u32 {
|
||||
type One;
|
||||
}
|
||||
fn one(&self, k: u32) -> u32;
|
||||
|
||||
fn two(a: u32, b: u32) -> u32 {
|
||||
type Two;
|
||||
}
|
||||
fn two(&self, a: u32, b: u32) -> u32;
|
||||
|
||||
fn trailing(a: u32, b: u32,) -> u32 {
|
||||
type Trailing;
|
||||
}
|
||||
}
|
||||
fn trailing(&self, a: u32, b: u32) -> u32;
|
||||
}
|
||||
|
||||
fn none(_db: &impl HelloWorldDatabase) -> u32 {
|
||||
|
@ -55,11 +44,11 @@ impl salsa::Database for DatabaseStruct {
|
|||
salsa::database_storage! {
|
||||
struct DatabaseStorage for DatabaseStruct {
|
||||
impl HelloWorldDatabase {
|
||||
fn input() for Input;
|
||||
fn none() for None;
|
||||
fn one() for One;
|
||||
fn two() for Two;
|
||||
fn trailing() for Trailing;
|
||||
fn input() for InputQuery;
|
||||
fn none() for NoneQuery;
|
||||
fn one() for OneQuery;
|
||||
fn two() for TwoQuery;
|
||||
fn trailing() for TrailingQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +58,7 @@ fn execute() {
|
|||
let mut db = DatabaseStruct::default();
|
||||
|
||||
// test what happens with inputs:
|
||||
db.query_mut(Input).set((1, 2), 3);
|
||||
db.query_mut(InputQuery).set((1, 2), 3);
|
||||
assert_eq!(db.input(1, 2), 3);
|
||||
|
||||
assert_eq!(db.none(), 22);
|
||||
|
|
Loading…
Reference in a new issue