#![allow(missing_docs)] | |
use crate::debug::TableEntry; | |
use crate::durability::Durability; | |
use crate::Cycle; | |
use crate::Database; | |
use crate::Query; | |
use crate::QueryTable; | |
use crate::QueryTableMut; | |
use std::borrow::Borrow; | |
use std::fmt::Debug; | |
use std::hash::Hash; | |
use triomphe::Arc; | |
pub use crate::derived::DependencyStorage; | |
pub use crate::derived::MemoizedStorage; | |
pub use crate::input::InputStorage; | |
pub use crate::interned::InternedStorage; | |
pub use crate::interned::LookupInternedStorage; | |
pub use crate::{revision::Revision, DatabaseKeyIndex, QueryDb, Runtime}; | |
/// Defines various associated types. An impl of this | |
/// should be generated for your query-context type automatically by | |
/// the `database_storage` macro, so you shouldn't need to mess | |
/// with this trait directly. | |
pub trait DatabaseStorageTypes: Database { | |
/// Defines the "storage type", where all the query data is kept. | |
/// This type is defined by the `database_storage` macro. | |
type DatabaseStorage: Default; | |
} | |
/// Internal operations that the runtime uses to operate on the database. | |
pub trait DatabaseOps { | |
/// Upcast this type to a `dyn Database`. | |
fn ops_database(&self) -> &dyn Database; | |
/// Gives access to the underlying salsa runtime. | |
fn ops_salsa_runtime(&self) -> &Runtime; | |
/// Gives access to the underlying salsa runtime. | |
fn ops_salsa_runtime_mut(&mut self) -> &mut Runtime; | |
/// Formats a database key index in a human readable fashion. | |
fn fmt_index( | |
&self, | |
index: DatabaseKeyIndex, | |
fmt: &mut std::fmt::Formatter<'_>, | |
) -> std::fmt::Result; | |
/// True if the computed value for `input` may have changed since `revision`. | |
fn maybe_changed_after(&self, input: DatabaseKeyIndex, revision: Revision) -> bool; | |
/// Find the `CycleRecoveryStrategy` for a given input. | |
fn cycle_recovery_strategy(&self, input: DatabaseKeyIndex) -> CycleRecoveryStrategy; | |
/// Executes the callback for each kind of query. | |
fn for_each_query(&self, op: &mut dyn FnMut(&dyn QueryStorageMassOps)); | |
} | |
/// Internal operations performed on the query storage as a whole | |
/// (note that these ops do not need to know the identity of the | |
/// query, unlike `QueryStorageOps`). | |
pub trait QueryStorageMassOps { | |
fn purge(&self); | |
} | |
pub trait DatabaseKey: Clone + Debug + Eq + Hash {} | |
pub trait QueryFunction: Query { | |
/// See `CycleRecoveryStrategy` | |
const CYCLE_STRATEGY: CycleRecoveryStrategy; | |
fn execute(db: &<Self as QueryDb<'_>>::DynDb, key: Self::Key) -> Self::Value; | |
fn cycle_fallback( | |
db: &<Self as QueryDb<'_>>::DynDb, | |
cycle: &Cycle, | |
key: &Self::Key, | |
) -> Self::Value { | |
let _ = (db, cycle, key); | |
panic!( | |
"query `{:?}` doesn't support cycle fallback", | |
Self::default() | |
) | |
} | |
} | |
/// Cycle recovery strategy: Is this query capable of recovering from | |
/// a cycle that results from executing the function? If so, how? | |
#[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
pub enum CycleRecoveryStrategy { | |
/// Cannot recover from cycles: panic. | |
/// | |
/// This is the default. It is also what happens if a cycle | |
/// occurs and the queries involved have different recovery | |
/// strategies. | |
/// | |
/// In the case of a failure due to a cycle, the panic | |
/// value will be XXX (FIXME). | |
Panic, | |
/// Recovers from cycles by storing a sentinel value. | |
/// | |
/// This value is computed by the `QueryFunction::cycle_fallback` | |
/// function. | |
Fallback, | |
} | |
/// Create a query table, which has access to the storage for the query | |
/// and offers methods like `get`. | |
pub fn get_query_table<'me, Q>(db: &'me <Q as QueryDb<'me>>::DynDb) -> QueryTable<'me, Q> | |
where | |
Q: Query + 'me, | |
Q::Storage: QueryStorageOps<Q>, | |
{ | |
let group_storage: &Q::GroupStorage = HasQueryGroup::group_storage(db); | |
let query_storage: &Q::Storage = Q::query_storage(group_storage); | |
QueryTable::new(db, query_storage) | |
} | |
/// Create a mutable query table, which has access to the storage | |
/// for the query and offers methods like `set`. | |
pub fn get_query_table_mut<'me, Q>(db: &'me mut <Q as QueryDb<'me>>::DynDb) -> QueryTableMut<'me, Q> | |
where | |
Q: Query, | |
{ | |
let (group_storage, runtime) = HasQueryGroup::group_storage_mut(db); | |
let query_storage = Q::query_storage_mut(group_storage); | |
QueryTableMut::new(runtime, &**query_storage) | |
} | |
pub trait QueryGroup: Sized { | |
type GroupStorage; | |
/// Dyn version of the associated database trait. | |
type DynDb: ?Sized + Database + HasQueryGroup<Self>; | |
} | |
/// Trait implemented by a database for each group that it supports. | |
/// `S` and `K` are the types for *group storage* and *group key*, respectively. | |
pub trait HasQueryGroup<G>: Database | |
where | |
G: QueryGroup, | |
{ | |
/// Access the group storage struct from the database. | |
fn group_storage(&self) -> &G::GroupStorage; | |
/// Access the group storage struct from the database. | |
/// Also returns a ref to the `Runtime`, since otherwise | |
/// the database is borrowed and one cannot get access to it. | |
fn group_storage_mut(&mut self) -> (&G::GroupStorage, &mut Runtime); | |
} | |
// ANCHOR:QueryStorageOps | |
pub trait QueryStorageOps<Q> | |
where | |
Self: QueryStorageMassOps, | |
Q: Query, | |
{ | |
// ANCHOR_END:QueryStorageOps | |
/// See CycleRecoveryStrategy | |
const CYCLE_STRATEGY: CycleRecoveryStrategy; | |
fn new(group_index: u16) -> Self; | |
/// Format a database key index in a suitable way. | |
fn fmt_index( | |
&self, | |
db: &<Q as QueryDb<'_>>::DynDb, | |
index: DatabaseKeyIndex, | |
fmt: &mut std::fmt::Formatter<'_>, | |
) -> std::fmt::Result; | |
// ANCHOR:maybe_changed_after | |
/// True if the value of `input`, which must be from this query, may have | |
/// changed after the given revision ended. | |
/// | |
/// This function should only be invoked with a revision less than the current | |
/// revision. | |
fn maybe_changed_after( | |
&self, | |
db: &<Q as QueryDb<'_>>::DynDb, | |
input: DatabaseKeyIndex, | |
revision: Revision, | |
) -> bool; | |
// ANCHOR_END:maybe_changed_after | |
fn cycle_recovery_strategy(&self) -> CycleRecoveryStrategy { | |
Self::CYCLE_STRATEGY | |
} | |
// ANCHOR:fetch | |
/// Execute the query, returning the result (often, the result | |
/// will be memoized). This is the "main method" for | |
/// queries. | |
/// | |
/// Returns `Err` in the event of a cycle, meaning that computing | |
/// the value for this `key` is recursively attempting to fetch | |
/// itself. | |
fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Q::Value; | |
// ANCHOR_END:fetch | |
/// Returns the durability associated with a given key. | |
fn durability(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Durability; | |
/// Get the (current) set of the entries in the query storage | |
fn entries<C>(&self, db: &<Q as QueryDb<'_>>::DynDb) -> C | |
where | |
C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>; | |
} | |
/// An optional trait that is implemented for "user mutable" storage: | |
/// that is, storage whose value is not derived from other storage but | |
/// is set independently. | |
pub trait InputQueryStorageOps<Q> | |
where | |
Q: Query, | |
{ | |
fn set(&self, runtime: &mut Runtime, key: &Q::Key, new_value: Q::Value, durability: Durability); | |
} | |
/// An optional trait that is implemented for "user mutable" storage: | |
/// that is, storage whose value is not derived from other storage but | |
/// is set independently. | |
pub trait LruQueryStorageOps { | |
fn set_lru_capacity(&self, new_capacity: usize); | |
} | |
pub trait DerivedQueryStorageOps<Q> | |
where | |
Q: Query, | |
{ | |
fn invalidate<S>(&self, runtime: &mut Runtime, key: &S) | |
where | |
S: Eq + Hash, | |
Q::Key: Borrow<S>; | |
} | |
pub type CycleParticipants = Arc<Vec<DatabaseKeyIndex>>; |