make query_prototype also define queries, remove query_definition

This commit is contained in:
Niko Matsakis 2018-10-05 05:51:18 -04:00
parent cd5622c6de
commit 0ee6f3884d
10 changed files with 101 additions and 289 deletions

View file

@ -34,7 +34,7 @@ Using salsa is as easy as 1, 2, 3...
and queries you will need. We'll start with one such trait, but and queries you will need. We'll start with one such trait, but
later on you can use more than one to break up your system into later on you can use more than one to break up your system into
components (or spread your code across crates). components (or spread your code across crates).
2. **Implement the queries** using the `query_definition!` macro. 2. **Implement the query functions** where appropriate.
3. Define the **database struct**, which contains the storage for all 3. Define the **database struct**, which contains the storage for all
the inputs/queries you will be using. The query struct will contain the inputs/queries you will be using. The query struct will contain
the storage for all of the inputs/queries and may also contain the storage for all of the inputs/queries and may also contain

View file

@ -1,5 +1,5 @@
use crate::compiler; use crate::compiler;
use salsa::{query_definition, query_prototype}; use salsa::query_prototype;
use std::sync::Arc; use std::sync::Arc;
query_prototype! { query_prototype! {
@ -24,20 +24,20 @@ query_prototype! {
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct DefId(usize); pub struct DefId(usize);
query_definition! { impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for AllClasses {
pub AllClasses(_: &impl ClassTableDatabase, (): ()) -> Arc<Vec<DefId>> { fn execute(_: &DB, (): ()) -> Arc<Vec<DefId>> {
Arc::new(vec![DefId(0), DefId(10)]) // dummy impl Arc::new(vec![DefId(0), DefId(10)]) // dummy impl
} }
} }
query_definition! { impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for Fields {
pub Fields(_: &impl ClassTableDatabase, class: DefId) -> Arc<Vec<DefId>> { fn execute(_: &DB, class: DefId) -> Arc<Vec<DefId>> {
Arc::new(vec![DefId(class.0 + 1), DefId(class.0 + 2)]) // dummy impl Arc::new(vec![DefId(class.0 + 1), DefId(class.0 + 2)]) // dummy impl
} }
} }
query_definition! { impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for AllFields {
pub AllFields(db: &impl ClassTableDatabase, (): ()) -> Arc<Vec<DefId>> { fn execute(db: &DB, (): ()) -> Arc<Vec<DefId>> {
Arc::new( Arc::new(
db.all_classes(()) db.all_classes(())
.iter() .iter()
@ -45,8 +45,7 @@ query_definition! {
.flat_map(|def_id| { .flat_map(|def_id| {
let fields = db.fields(def_id); let fields = db.fields(def_id);
(0..fields.len()).map(move |i| fields[i]) (0..fields.len()).map(move |i| fields[i])
}) }).collect(),
.collect()
) )
} }
} }

View file

@ -20,18 +20,24 @@ example. It defines exactly two queries: `input_string` and
names of the queries as methods (e.g., `input_string()`) and also a names of the queries as methods (e.g., `input_string()`) and also a
path to a type that will define the query (`InputString`). It doesn't path to a type that will define the query (`InputString`). It doesn't
give many other details: those are specified in the query definition give many other details: those are specified in the query definition
that comes later. that comes later. XXX out of date
```rust ```rust
salsa::query_prototype! { salsa::query_prototype! {
trait HelloWorldDatabase: salsa::Database { trait HelloWorldDatabase: salsa::Database {
fn input_string() for InputString; fn input_string(key: ()) -> Arc<String> {
fn length() for Length; type InputString;
storage input;
}
fn length(key: ()) -> usize {
type Length;
}
} }
} }
``` ```
### Step 2: Define the queries ### Step 2: Define the query bodies
The actual query definitions are made using the The actual query definitions are made using the
`salsa::query_definition` macro. For an **input query**, such as `salsa::query_definition` macro. For an **input query**, such as

View file

@ -13,7 +13,9 @@ salsa::query_prototype! {
trait HelloWorldDatabase: salsa::Database { trait HelloWorldDatabase: salsa::Database {
fn input_string(key: ()) -> Arc<String> { fn input_string(key: ()) -> Arc<String> {
type InputString; type InputString;
storage input;
} }
fn length(key: ()) -> usize { fn length(key: ()) -> usize {
type Length; type Length;
} }
@ -23,21 +25,14 @@ salsa::query_prototype! {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Step 2. Define the queries. // Step 2. Define the queries.
// Define an **input query**. Like all queries, it is a map from a key
// (of type `()`) to a value (of type `Arc<String>`). All values begin
// as `Default::default` but you can assign them new values.
salsa::query_definition! {
InputString: Map<(), Arc<String>>;
}
// Define a **function query**. It too has a key and value type, but // Define a **function query**. It too has a key and value type, but
// it is defined with a function that -- given the key -- computes the // it is defined with a function that -- given the key -- computes the
// value. This function is supplied with a context (an `&impl // value. This function is supplied with a context (an `&impl
// HelloWorldDatabase`) that gives access to other queries. The runtime // HelloWorldDatabase`) that gives access to other queries. The runtime
// will track which queries you use so that we can incrementally // will track which queries you use so that we can incrementally
// update memoized results. // update memoized results.
salsa::query_definition! { impl<DB: HelloWorldDatabase> salsa::QueryFunction<DB> for Length {
Length(db: &impl HelloWorldDatabase, _key: ()) -> usize { fn execute(db: &DB, _key: ()) -> usize {
// Read the input string: // Read the input string:
let input_string = db.input_string(()); let input_string = db.input_string(());

View file

@ -208,14 +208,14 @@ impl DefaultKey for () {
/// queries need to execute, as well as defining the queries /// queries need to execute, as well as defining the queries
/// themselves that are exported for others to use. /// themselves that are exported for others to use.
/// ///
/// This macro declares the "prototype" for a single query. This will /// This macro declares the "prototype" for a group of queries. It will
/// expand into a `fn` item. This prototype specifies name of the /// expand into a trait and a set of structs, one per query.
/// method (in the example below, that would be `my_query`) and ///
/// connects it to query definition type (`MyQuery`, in the example /// For each query, you give the name of the accessor method to invoke
/// below). These typically have the same name but a distinct /// the query (e.g., `my_query`, below), as well as its input/output
/// capitalization convention. Note that the actual input/output type /// types. You also give the name for a query type (e.g., `MyQuery`,
/// of the query are given only in the query definition (see the /// below) that represents the query, and optionally other details,
/// `query_definition` macro for more details). /// such as its storage.
/// ///
/// ### Examples /// ### Examples
/// ///
@ -225,26 +225,10 @@ impl DefaultKey for () {
/// trait TypeckDatabase { /// trait TypeckDatabase {
/// query_prototype! { /// query_prototype! {
/// /// Comments or other attributes can go here /// /// Comments or other attributes can go here
/// fn my_query() for MyQuery; /// fn my_query(input: u32) -> u64 {
/// } /// type MyQuery;
/// } /// storage memoized; // optional, this is the default
/// ``` /// }
///
/// This just expands to something like:
///
/// ```ignore
/// fn my_query(&self) -> QueryTable<'_, Self, $query_type>;
/// ```
///
/// This permits us to invoke the query via `query.my_query().of(key)`.
///
/// You can also include more than one query if you prefer:
///
/// ```ignore
/// trait TypeckDatabase {
/// query_prototype! {
/// fn my_query() for MyQuery;
/// fn my_other_query() for MyOtherQuery;
/// } /// }
/// } /// }
/// ``` /// ```
@ -273,25 +257,40 @@ macro_rules! query_prototype {
// Base case: found the trait body // Base case: found the trait body
( (
attr[$($trait_attr:tt)*]; attr[$($trait_attr:tt)*];
headers[$v:vis, $name:ident, $($header:tt)*]; headers[$v:vis, $query_trait:ident, $($header:tt)*];
tokens[{ tokens[{
$( $(
$(#[$method_attr:meta])* $(#[$method_attr:meta])*
fn $method_name:ident($key_name:ident: $key:ty) -> $value:ty { fn $method_name:ident($key_name:ident: $key_ty:ty) -> $value_ty:ty {
type $QueryType:ty; type $QueryType:ident;
$(storage $storage:ident;)* // FIXME(rust-lang/rust#48075) should be `?`
} }
)* )*
}]; }];
) => { ) => {
$($trait_attr)* $v trait $name: $($crate::GetQueryTable<$QueryType> +)* $($header)* { $($trait_attr)* $v trait $query_trait: $($crate::GetQueryTable<$QueryType> +)* $($header)* {
$( $(
$(#[$method_attr])* $(#[$method_attr])*
fn $method_name(&self, key: $key) -> $value { fn $method_name(&self, key: $key_ty) -> $value_ty {
<Self as $crate::GetQueryTable<$QueryType>>::get_query_table(self) <Self as $crate::GetQueryTable<$QueryType>>::get_query_table(self)
.get(key) .get(key)
} }
)* )*
} }
$(
#[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_prototype! { @storage_ty[DB, Self, $($storage)*] };
}
)*
}; };
// Recursive case: found some more part of the trait header. // Recursive case: found some more part of the trait header.
@ -307,143 +306,13 @@ macro_rules! query_prototype {
tokens[$($tokens)*]; tokens[$($tokens)*];
} }
}; };
}
/// Creates a **Query Definition** type. This defines the input (key) // Generate storage type
/// of the query, the output key (value), and the query context trait
/// that the query requires.
///
/// Example:
///
/// ```ignore
/// query_definition! {
/// pub MyQuery(db: &impl MyDatabase, key: MyKey) -> MyValue {
/// ... // fn body specifies what happens when query is invoked
/// }
/// }
/// ```
///
/// Here, the query context trait would be `MyDatabase` -- this
/// should be a trait containing all the other queries that the
/// definition needs to invoke (as well as any other methods that you
/// may want).
///
/// The `MyKey` type is the **key** to the query -- it must be Clone,
/// Debug, Hash, Eq, and Send, as specified in the `Query` trait.
///
/// The `MyKey` type is the **value** to the query -- it too must be
/// Clone, Debug, Hash, Eq, and Send, as specified in the `Query`
/// trait.
#[macro_export]
macro_rules! query_definition {
// Step 1. Filtering the attributes to look for the special ones
// we consume.
( (
@filter_attrs { // Default case:
input { #[storage(memoized)] $($input:tt)* }; @storage_ty[$DB:ident, $Self:ident, ]
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
) => { ) => {
$crate::query_definition! { $crate::query_prototype! { @storage_ty[$DB, $Self, memoized] }
@filter_attrs {
input { $($input)* };
storage { memoized };
other_attrs { $($other_attrs)* };
}
}
};
(
@filter_attrs {
input { #[storage(volatile)] $($input:tt)* };
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
) => {
$crate::query_definition! {
@filter_attrs {
input { $($input)* };
storage { volatile };
other_attrs { $($other_attrs)* };
}
}
};
(
@filter_attrs {
input { #[storage(dependencies)] $($input:tt)* };
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
) => {
$crate::query_definition! {
@filter_attrs {
input { $($input)* };
storage { dependencies };
other_attrs { $($other_attrs)* };
}
}
};
(
@filter_attrs {
input { #[$attr:meta] $($input:tt)* };
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
) => {
$crate::query_definition! {
@filter_attrs {
input { $($input)* };
storage { $storage };
other_attrs { $($other_attrs)* #[$attr] };
}
}
};
// Accept a "fn-like" query definition
(
@filter_attrs {
input {
$v:vis $name:ident(
$db:tt : &impl $query_trait:path,
$key:tt : $key_ty:ty $(,)*
) -> $value_ty:ty {
$($body:tt)*
}
};
storage { $storage:tt };
other_attrs { $($attrs:tt)* };
}
) => {
#[derive(Default, Debug)]
$($attrs)*
$v struct $name;
impl<DB> $crate::Query<DB> for $name
where
DB: $query_trait,
{
type Key = $key_ty;
type Value = $value_ty;
type Storage = $crate::query_definition! { @storage_ty[DB, Self, $storage] };
}
impl<DB> $crate::QueryFunction<DB> for $name
where
DB: $query_trait,
{
fn execute($db: &DB, $key: $key_ty) -> $value_ty {
$($body)*
}
}
};
(
@storage_ty[$DB:ident, $Self:ident, default]
) => {
$crate::query_definition! { @storage_ty[$DB, $Self, memoized] }
}; };
( (
@ -464,52 +333,10 @@ macro_rules! query_definition {
$crate::dependencies::DependencyStorage<$DB, $Self> $crate::dependencies::DependencyStorage<$DB, $Self>
}; };
// Accept a "field-like" query definition (input)
( (
@filter_attrs { @storage_ty[$DB:ident, $Self:ident, input]
input {
$v:vis $name:ident: Map<$key_ty:ty, $value_ty:ty>;
};
storage { default };
other_attrs { $($attrs:tt)* };
}
) => { ) => {
#[derive(Default, Debug)] $crate::input::InputStorage<DB, Self>
$($attrs)*
$v struct $name;
impl<DB> $crate::Query<DB> for $name
where
DB: $crate::Database
{
type Key = $key_ty;
type Value = $value_ty;
type Storage = $crate::input::InputStorage<DB, Self>;
}
};
// Various legal start states:
(
# $($tokens:tt)*
) => {
$crate::query_definition! {
@filter_attrs {
input { # $($tokens)* };
storage { default };
other_attrs { };
}
}
};
(
$v:vis $name:ident $($tokens:tt)*
) => {
$crate::query_definition! {
@filter_attrs {
input { $v $name $($tokens)* };
storage { default };
other_attrs { };
}
}
}; };
} }

View file

@ -33,35 +33,35 @@ salsa::query_prototype! {
} }
fn volatile_a(key: ()) -> () { fn volatile_a(key: ()) -> () {
type VolatileA; type VolatileA;
storage volatile;
} }
fn volatile_b(key: ()) -> () { fn volatile_b(key: ()) -> () {
type VolatileB; type VolatileB;
storage volatile;
} }
} }
} }
salsa::query_definition! { impl<DB: Database> salsa::QueryFunction<DB> for MemoizedA {
crate MemoizedA(db: &impl Database, (): ()) -> () { fn execute(db: &DB, (): ()) -> () {
db.memoized_b(()) db.memoized_b(())
} }
} }
salsa::query_definition! { impl<DB: Database> salsa::QueryFunction<DB> for MemoizedB {
crate MemoizedB(db: &impl Database, (): ()) -> () { fn execute(db: &DB, (): ()) -> () {
db.memoized_a(()) db.memoized_a(())
} }
} }
salsa::query_definition! { impl<DB: Database> salsa::QueryFunction<DB> for VolatileA {
#[storage(volatile)] fn execute(db: &DB, (): ()) -> () {
crate VolatileA(db: &impl Database, (): ()) -> () {
db.volatile_b(()) db.volatile_b(())
} }
} }
salsa::query_definition! { impl<DB: Database> salsa::QueryFunction<DB> for VolatileB {
#[storage(volatile)] fn execute(db: &DB, (): ()) -> () {
crate VolatileB(db: &impl Database, (): ()) -> () {
db.volatile_a(()) db.volatile_a(())
} }
} }

View file

@ -11,46 +11,40 @@ salsa::query_prototype! {
} }
fn dep_derived1(key: ()) -> usize { fn dep_derived1(key: ()) -> usize {
type Derived1; type Derived1;
storage dependencies;
} }
fn dep_input1(key: ()) -> usize { fn dep_input1(key: ()) -> usize {
type Input1; type Input1;
storage input;
} }
fn dep_input2(key: ()) -> usize { fn dep_input2(key: ()) -> usize {
type Input2; type Input2;
storage input;
} }
} }
} }
salsa::query_definition! { impl<DB: MemoizedDepInputsContext> salsa::QueryFunction<DB> for Memoized2 {
crate Memoized2(db: &impl MemoizedDepInputsContext, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.log().add("Memoized2 invoked"); db.log().add("Memoized2 invoked");
db.dep_memoized1(()) db.dep_memoized1(())
} }
} }
salsa::query_definition! { impl<DB: MemoizedDepInputsContext> salsa::QueryFunction<DB> for Memoized1 {
crate Memoized1(db: &impl MemoizedDepInputsContext, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.log().add("Memoized1 invoked"); db.log().add("Memoized1 invoked");
db.dep_derived1(()) * 2 db.dep_derived1(()) * 2
} }
} }
salsa::query_definition! { impl<DB: MemoizedDepInputsContext> salsa::QueryFunction<DB> for Derived1 {
#[storage(dependencies)] fn execute(db: &DB, (): ()) -> usize {
crate Derived1(db: &impl MemoizedDepInputsContext, (): ()) -> usize {
db.log().add("Derived1 invoked"); db.log().add("Derived1 invoked");
db.dep_input1(()) / 2 db.dep_input1(()) / 2
} }
} }
salsa::query_definition! {
crate Input1: Map<(), usize>;
}
salsa::query_definition! {
crate Input2: Map<(), usize>;
}
#[test] #[test]
fn revalidate() { fn revalidate() {
let db = &TestContextImpl::default(); let db = &TestContextImpl::default();

View file

@ -8,31 +8,22 @@ salsa::query_prototype! {
} }
fn input1(key: ()) -> usize { fn input1(key: ()) -> usize {
type Input1; type Input1;
storage input;
} }
fn input2(key: ()) -> usize { fn input2(key: ()) -> usize {
type Input2; type Input2;
storage input;
} }
} }
} }
salsa::query_definition! { impl<DB: MemoizedInputsContext> salsa::QueryFunction<DB> for Max {
crate Max(db: &impl MemoizedInputsContext, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.log().add("Max invoked"); db.log().add("Max invoked");
std::cmp::max( std::cmp::max(db.input1(()), db.input2(()))
db.input1(()),
db.input2(()),
)
} }
} }
salsa::query_definition! {
crate Input1: Map<(), usize>;
}
salsa::query_definition! {
crate Input2: Map<(), usize>;
}
#[test] #[test]
fn revalidate() { fn revalidate() {
let db = &TestContextImpl::default(); let db = &TestContextImpl::default();

View file

@ -13,28 +13,28 @@ salsa::query_prototype! {
} }
fn volatile(key: ()) -> usize { fn volatile(key: ()) -> usize {
type Volatile; type Volatile;
storage volatile;
} }
} }
} }
salsa::query_definition! { impl<DB: MemoizedVolatileContext> salsa::QueryFunction<DB> for Memoized2 {
crate Memoized2(db: &impl MemoizedVolatileContext, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.log().add("Memoized2 invoked"); db.log().add("Memoized2 invoked");
db.memoized1(()) db.memoized1(())
} }
} }
salsa::query_definition! { impl<DB: MemoizedVolatileContext> salsa::QueryFunction<DB> for Memoized1 {
crate Memoized1(db: &impl MemoizedVolatileContext, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.log().add("Memoized1 invoked"); db.log().add("Memoized1 invoked");
let v = db.volatile(()); let v = db.volatile(());
v / 2 v / 2
} }
} }
salsa::query_definition! { impl<DB: MemoizedVolatileContext> salsa::QueryFunction<DB> for Volatile {
#[storage(volatile)] fn execute(db: &DB, (): ()) -> usize {
crate Volatile(db: &impl MemoizedVolatileContext, (): ()) -> usize {
db.log().add("Volatile invoked"); db.log().add("Volatile invoked");
db.clock().increment() db.clock().increment()
} }

View file

@ -9,23 +9,23 @@ salsa::query_prototype! {
} }
fn volatile(key: ()) -> usize { fn volatile(key: ()) -> usize {
type Volatile; type Volatile;
storage volatile;
} }
} }
} }
salsa::query_definition! { /// Because this query is memoized, we only increment the counter
/// Because this query is memoized, we only increment the counter /// the first time it is invoked.
/// the first time it is invoked. impl<DB: Database> salsa::QueryFunction<DB> for Memoized {
crate Memoized(db: &impl Database, (): ()) -> usize { fn execute(db: &DB, (): ()) -> usize {
db.increment() db.increment()
} }
} }
salsa::query_definition! { /// Because this query is volatile, each time it is invoked,
/// Because this query is volatile, each time it is invoked, /// we will increment the counter.
/// we will increment the counter. impl<DB: Database> salsa::QueryFunction<DB> for Volatile {
#[storage(volatile)] fn execute(db: &DB, (): ()) -> usize {
crate Volatile(db: &impl Database, (): ()) -> usize {
db.increment() db.increment()
} }
} }