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
later on you can use more than one to break up your system into
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
the inputs/queries you will be using. The query struct will contain
the storage for all of the inputs/queries and may also contain

View file

@ -1,5 +1,5 @@
use crate::compiler;
use salsa::{query_definition, query_prototype};
use salsa::query_prototype;
use std::sync::Arc;
query_prototype! {
@ -24,20 +24,20 @@ query_prototype! {
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct DefId(usize);
query_definition! {
pub AllClasses(_: &impl ClassTableDatabase, (): ()) -> Arc<Vec<DefId>> {
impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for AllClasses {
fn execute(_: &DB, (): ()) -> Arc<Vec<DefId>> {
Arc::new(vec![DefId(0), DefId(10)]) // dummy impl
}
}
query_definition! {
pub Fields(_: &impl ClassTableDatabase, class: DefId) -> Arc<Vec<DefId>> {
impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for Fields {
fn execute(_: &DB, class: DefId) -> Arc<Vec<DefId>> {
Arc::new(vec![DefId(class.0 + 1), DefId(class.0 + 2)]) // dummy impl
}
}
query_definition! {
pub AllFields(db: &impl ClassTableDatabase, (): ()) -> Arc<Vec<DefId>> {
impl<DB: ClassTableDatabase> salsa::QueryFunction<DB> for AllFields {
fn execute(db: &DB, (): ()) -> Arc<Vec<DefId>> {
Arc::new(
db.all_classes(())
.iter()
@ -45,8 +45,7 @@ query_definition! {
.flat_map(|def_id| {
let fields = db.fields(def_id);
(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
path to a type that will define the query (`InputString`). It doesn't
give many other details: those are specified in the query definition
that comes later.
that comes later. XXX out of date
```rust
salsa::query_prototype! {
trait HelloWorldDatabase: salsa::Database {
fn input_string() for InputString;
fn length() for Length;
fn input_string(key: ()) -> Arc<String> {
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
`salsa::query_definition` macro. For an **input query**, such as

View file

@ -13,7 +13,9 @@ salsa::query_prototype! {
trait HelloWorldDatabase: salsa::Database {
fn input_string(key: ()) -> Arc<String> {
type InputString;
storage input;
}
fn length(key: ()) -> usize {
type Length;
}
@ -23,21 +25,14 @@ salsa::query_prototype! {
///////////////////////////////////////////////////////////////////////////
// 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
// it is defined with a function that -- given the key -- computes the
// value. This function is supplied with a context (an `&impl
// HelloWorldDatabase`) that gives access to other queries. The runtime
// will track which queries you use so that we can incrementally
// update memoized results.
salsa::query_definition! {
Length(db: &impl HelloWorldDatabase, _key: ()) -> usize {
impl<DB: HelloWorldDatabase> salsa::QueryFunction<DB> for Length {
fn execute(db: &DB, _key: ()) -> usize {
// Read the 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
/// themselves that are exported for others to use.
///
/// This macro declares the "prototype" for a single query. This will
/// expand into a `fn` item. This prototype specifies name of the
/// method (in the example below, that would be `my_query`) and
/// connects it to query definition type (`MyQuery`, in the example
/// below). These typically have the same name but a distinct
/// capitalization convention. Note that the actual input/output type
/// of the query are given only in the query definition (see the
/// `query_definition` macro for more details).
/// 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 input/output
/// types. 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
///
@ -225,26 +225,10 @@ impl DefaultKey for () {
/// trait TypeckDatabase {
/// query_prototype! {
/// /// Comments or other attributes can go here
/// fn my_query() for MyQuery;
/// }
/// }
/// ```
///
/// 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;
/// fn my_query(input: u32) -> u64 {
/// type MyQuery;
/// storage memoized; // optional, this is the default
/// }
/// }
/// }
/// ```
@ -273,25 +257,40 @@ macro_rules! query_prototype {
// Base case: found the trait body
(
attr[$($trait_attr:tt)*];
headers[$v:vis, $name:ident, $($header:tt)*];
headers[$v:vis, $query_trait:ident, $($header:tt)*];
tokens[{
$(
$(#[$method_attr:meta])*
fn $method_name:ident($key_name:ident: $key:ty) -> $value:ty {
type $QueryType:ty;
fn $method_name:ident($key_name:ident: $key_ty:ty) -> $value_ty: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])*
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)
.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.
@ -307,143 +306,13 @@ macro_rules! query_prototype {
tokens[$($tokens)*];
}
};
}
/// Creates a **Query Definition** type. This defines the input (key)
/// 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.
// Generate storage type
(
@filter_attrs {
input { #[storage(memoized)] $($input:tt)* };
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
// Default case:
@storage_ty[$DB:ident, $Self:ident, ]
) => {
$crate::query_definition! {
@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] }
$crate::query_prototype! { @storage_ty[$DB, $Self, memoized] }
};
(
@ -464,52 +333,10 @@ macro_rules! query_definition {
$crate::dependencies::DependencyStorage<$DB, $Self>
};
// Accept a "field-like" query definition (input)
(
@filter_attrs {
input {
$v:vis $name:ident: Map<$key_ty:ty, $value_ty:ty>;
};
storage { default };
other_attrs { $($attrs:tt)* };
}
@storage_ty[$DB:ident, $Self:ident, input]
) => {
#[derive(Default, Debug)]
$($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 { };
}
}
$crate::input::InputStorage<DB, Self>
};
}

View file

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

View file

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

View file

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

View file

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

View file

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