switch to new database design

Under this design, *all* databases are a
`DatabaseImpl<U>`, where the `U` implements
`UserData` (you can use `()` if there is none).

Code would default to `&dyn salsa::Database` but
if you want to give access to the userdata, you
can define a custom database trait
`MyDatabase: salsa::Databse` so long as you

* annotate `MyDatabase` trait definition of
  impls of `MyDatabase` with `#[salsa::db]`
* implement `MyDatabase` for `DatabaseImpl<U>`
  where `U` is your userdata (this could be a
  blanket impl, if you don't know the precise
  userdata type).

The `tests/common/mod.rs` shows the pattern.
This commit is contained in:
Niko Matsakis 2024-07-27 12:29:41 +00:00
parent 64556e9d28
commit daaa78056a
77 changed files with 491 additions and 1125 deletions

View file

@ -26,7 +26,7 @@ fn many_tracked_structs(criterion: &mut Criterion) {
criterion.bench_function("many_tracked_structs", |b| { criterion.bench_function("many_tracked_structs", |b| {
b.iter_batched_ref( b.iter_batched_ref(
|| { || {
let db = salsa::default_database(); let db = salsa::DatabaseImpl::new();
let input = Input::new(&db, 1_000); let input = Input::new(&db, 1_000);
let input2 = Input::new(&db, 1); let input2 = Input::new(&db, 1);

View file

@ -32,13 +32,6 @@ struct DbMacro {
impl DbMacro { impl DbMacro {
fn try_db(self, input: syn::Item) -> syn::Result<TokenStream> { fn try_db(self, input: syn::Item) -> syn::Result<TokenStream> {
match input { match input {
syn::Item::Struct(input) => {
let has_storage_impl = self.zalsa_database_impl(&input)?;
Ok(quote! {
#has_storage_impl
#input
})
}
syn::Item::Trait(mut input) => { syn::Item::Trait(mut input) => {
self.add_salsa_view_method(&mut input)?; self.add_salsa_view_method(&mut input)?;
Ok(quote! { Ok(quote! {
@ -53,54 +46,11 @@ impl DbMacro {
} }
_ => Err(syn::Error::new_spanned( _ => Err(syn::Error::new_spanned(
input, input,
"`db` must be applied to a struct, trait, or impl", "`db` must be applied to a trait or impl",
)), )),
} }
} }
fn find_storage_field(&self, input: &syn::ItemStruct) -> syn::Result<syn::Ident> {
let storage = "storage";
for field in input.fields.iter() {
if let Some(i) = &field.ident {
if i == storage {
return Ok(i.clone());
}
} else {
return Err(syn::Error::new_spanned(
field,
"database struct must be a braced struct (`{}`) with a field named `storage`",
));
}
}
Err(syn::Error::new_spanned(
&input.ident,
"database struct must be a braced struct (`{}`) with a field named `storage`",
))
}
fn zalsa_database_impl(&self, input: &syn::ItemStruct) -> syn::Result<TokenStream> {
let storage = self.find_storage_field(input)?;
let db = &input.ident;
let zalsa = self.hygiene.ident("zalsa");
Ok(quote! {
const _: () = {
use salsa::plumbing as #zalsa;
unsafe impl #zalsa::ZalsaDatabase for #db {
fn zalsa(&self) -> &dyn #zalsa::Zalsa {
&self.#storage
}
fn zalsa_mut(&mut self) -> &mut dyn #zalsa::Zalsa {
&mut self.#storage
}
}
};
})
}
fn add_salsa_view_method(&self, input: &mut syn::ItemTrait) -> syn::Result<()> { fn add_salsa_view_method(&self, input: &mut syn::ItemTrait) -> syn::Result<()> {
input.items.push(parse_quote! { input.items.push(parse_quote! {
#[doc(hidden)] #[doc(hidden)]

View file

@ -1,49 +1,48 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use salsa::UserData;
pub type CalcDatabaseImpl = salsa::DatabaseImpl<Calc>;
// ANCHOR: db_struct // ANCHOR: db_struct
#[derive(Default)] #[derive(Default)]
#[salsa::db] pub struct Calc {
pub(crate) struct Database {
storage: salsa::Storage<Self>,
// The logs are only used for testing and demonstrating reuse: // The logs are only used for testing and demonstrating reuse:
// logs: Arc<Mutex<Option<Vec<String>>>>,
logs: Option<Arc<Mutex<Vec<String>>>>,
} }
// ANCHOR_END: db_struct // ANCHOR_END: db_struct
impl Database { impl Calc {
/// Enable logging of each salsa event. /// Enable logging of each salsa event.
#[cfg(test)] #[cfg(test)]
pub fn enable_logging(self) -> Self { pub fn enable_logging(&self) {
assert!(self.logs.is_none()); let mut logs = self.logs.lock().unwrap();
Self { if logs.is_none() {
storage: self.storage, *logs = Some(vec![]);
logs: Some(Default::default()),
} }
} }
#[cfg(test)] #[cfg(test)]
pub fn take_logs(&mut self) -> Vec<String> { pub fn take_logs(&self) -> Vec<String> {
if let Some(logs) = &self.logs { let mut logs = self.logs.lock().unwrap();
std::mem::take(&mut *logs.lock().unwrap()) if let Some(logs) = &mut *logs {
std::mem::take(logs)
} else { } else {
panic!("logs not enabled"); vec![]
} }
} }
} }
// ANCHOR: db_impl // ANCHOR: db_impl
#[salsa::db] impl UserData for Calc {
impl salsa::Database for Database { fn salsa_event(db: &CalcDatabaseImpl, event: &dyn Fn() -> salsa::Event) {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event(); let event = event();
eprintln!("Event: {event:?}"); eprintln!("Event: {event:?}");
// Log interesting events, if logging is enabled // Log interesting events, if logging is enabled
if let Some(logs) = &self.logs { if let Some(logs) = &mut *db.logs.lock().unwrap() {
// don't log boring events // only log interesting events
if let salsa::EventKind::WillExecute { .. } = event.kind { if let salsa::EventKind::WillExecute { .. } = event.kind {
logs.lock().unwrap().push(format!("Event: {event:?}")); logs.push(format!("Event: {event:?}"));
} }
} }
} }

View file

@ -1,3 +1,4 @@
use db::CalcDatabaseImpl;
use ir::{Diagnostic, SourceProgram}; use ir::{Diagnostic, SourceProgram};
use salsa::Database as Db; use salsa::Database as Db;
@ -8,7 +9,7 @@ mod parser;
mod type_check; mod type_check;
pub fn main() { pub fn main() {
let db = db::Database::default(); let db: CalcDatabaseImpl = Default::default();
let source_program = SourceProgram::new(&db, String::new()); let source_program = SourceProgram::new(&db, String::new());
compile::compile(&db, source_program); compile::compile(&db, source_program);
let diagnostics = compile::compile::accumulated::<Diagnostic>(&db, source_program); let diagnostics = compile::compile::accumulated::<Diagnostic>(&db, source_program);

View file

@ -351,9 +351,11 @@ impl<'db> Parser<'_, 'db> {
/// Returns the statements and the diagnostics generated. /// Returns the statements and the diagnostics generated.
#[cfg(test)] #[cfg(test)]
fn parse_string(source_text: &str) -> String { fn parse_string(source_text: &str) -> String {
use salsa::Database as _; use salsa::Database;
crate::db::Database::default().attach(|db| { use crate::db::CalcDatabaseImpl;
CalcDatabaseImpl::default().attach(|db| {
// Create the source program // Create the source program
let source_program = SourceProgram::new(db, source_text.to_string()); let source_program = SourceProgram::new(db, source_text.to_string());

View file

@ -6,8 +6,6 @@ use derive_new::new;
use expect_test::expect; use expect_test::expect;
use salsa::Accumulator; use salsa::Accumulator;
#[cfg(test)] #[cfg(test)]
use salsa::Database as _;
#[cfg(test)]
use test_log::test; use test_log::test;
// ANCHOR: parse_statements // ANCHOR: parse_statements
@ -100,12 +98,13 @@ fn check_string(
expected_diagnostics: expect_test::Expect, expected_diagnostics: expect_test::Expect,
edits: &[(&str, expect_test::Expect, expect_test::Expect)], edits: &[(&str, expect_test::Expect, expect_test::Expect)],
) { ) {
use salsa::Setter; use salsa::{Database, Setter};
use crate::{db::Database, ir::SourceProgram, parser::parse_statements}; use crate::{db::CalcDatabaseImpl, ir::SourceProgram, parser::parse_statements};
// Create the database // Create the database
let mut db = Database::default().enable_logging(); let mut db = CalcDatabaseImpl::default();
db.enable_logging();
// Create the source program // Create the source program
let source_program = SourceProgram::new(&db, source_text.to_string()); let source_program = SourceProgram::new(&db, source_text.to_string());

View file

@ -8,13 +8,13 @@ use notify_debouncer_mini::{
notify::{RecommendedWatcher, RecursiveMode}, notify::{RecommendedWatcher, RecursiveMode},
DebounceEventResult, Debouncer, DebounceEventResult, Debouncer,
}; };
use salsa::{Accumulator, Setter}; use salsa::{Accumulator, DatabaseImpl, Setter, UserData};
// ANCHOR: main // ANCHOR: main
fn main() -> Result<()> { fn main() -> Result<()> {
// Create the channel to receive file change events. // Create the channel to receive file change events.
let (tx, rx) = unbounded(); let (tx, rx) = unbounded();
let mut db = Database::new(tx); let mut db = DatabaseImpl::with(LazyInput::new(tx));
let initial_file_path = std::env::args_os() let initial_file_path = std::env::args_os()
.nth(1) .nth(1)
@ -74,19 +74,15 @@ trait Db: salsa::Database {
fn input(&self, path: PathBuf) -> Result<File>; fn input(&self, path: PathBuf) -> Result<File>;
} }
#[salsa::db] struct LazyInput {
struct Database {
storage: salsa::Storage<Self>,
logs: Mutex<Vec<String>>, logs: Mutex<Vec<String>>,
files: DashMap<PathBuf, File>, files: DashMap<PathBuf, File>,
file_watcher: Mutex<Debouncer<RecommendedWatcher>>, file_watcher: Mutex<Debouncer<RecommendedWatcher>>,
} }
impl Database { impl LazyInput {
fn new(tx: Sender<DebounceEventResult>) -> Self { fn new(tx: Sender<DebounceEventResult>) -> Self {
let storage = Default::default();
Self { Self {
storage,
logs: Default::default(), logs: Default::default(),
files: DashMap::new(), files: DashMap::new(),
file_watcher: Mutex::new(new_debouncer(Duration::from_secs(1), tx).unwrap()), file_watcher: Mutex::new(new_debouncer(Duration::from_secs(1), tx).unwrap()),
@ -94,8 +90,18 @@ impl Database {
} }
} }
impl UserData for LazyInput {
fn salsa_event(db: &DatabaseImpl<Self>, event: &dyn Fn() -> salsa::Event) {
// don't log boring events
let event = event();
if let salsa::EventKind::WillExecute { .. } = event.kind {
db.logs.lock().unwrap().push(format!("{:?}", event));
}
}
}
#[salsa::db] #[salsa::db]
impl Db for Database { impl Db for DatabaseImpl<LazyInput> {
fn input(&self, path: PathBuf) -> Result<File> { fn input(&self, path: PathBuf) -> Result<File> {
let path = path let path = path
.canonicalize() .canonicalize()
@ -122,17 +128,6 @@ impl Db for Database {
} }
// ANCHOR_END: db // ANCHOR_END: db
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
// don't log boring events
let event = event();
if let salsa::EventKind::WillExecute { .. } = event.kind {
self.logs.lock().unwrap().push(format!("{:?}", event));
}
}
}
#[salsa::accumulator] #[salsa::accumulator]
struct Diagnostic(String); struct Diagnostic(String);

View file

@ -1,20 +1,24 @@
use crate::{local_state, storage::ZalsaDatabase, Durability, Event, Revision}; use std::{any::Any, panic::RefUnwindSafe};
use crate::{self as salsa, local_state, storage::Zalsa, Durability, Event, Revision, Storage};
/// The trait implemented by all Salsa databases.
/// You can create your own subtraits of this trait using the `#[salsa::db]` procedural macro.
///
/// # Safety conditions
///
/// This trait can only safely be implemented by Salsa's [`DatabaseImpl`][] type.
/// FIXME: Document better the unsafety conditions we guarantee.
#[salsa_macros::db] #[salsa_macros::db]
pub trait Database: ZalsaDatabase + AsDynDatabase { pub unsafe trait Database: AsDynDatabase + Any {
/// This function is invoked at key points in the salsa /// This function is invoked by the salsa runtime at various points during execution.
/// runtime. It permits the database to be customized and to /// You can customize what happens by implementing the [`UserData`][] trait.
/// inject logging or other custom behavior. /// By default, the event is logged at level debug using tracing facade.
///
/// By default, the event is logged at level debug using
/// the standard `log` facade.
/// ///
/// # Parameters /// # Parameters
/// ///
/// * `event`, a fn that, if called, will create the event that occurred /// * `event`, a fn that, if called, will create the event that occurred
fn salsa_event(&self, event: &dyn Fn() -> Event) { fn salsa_event(&self, event: &dyn Fn() -> Event);
tracing::debug!("salsa_event: {:?}", event())
}
/// A "synthetic write" causes the system to act *as though* some /// A "synthetic write" causes the system to act *as though* some
/// input of durability `durability` has changed. This is mostly /// input of durability `durability` has changed. This is mostly
@ -48,6 +52,13 @@ pub trait Database: ZalsaDatabase + AsDynDatabase {
{ {
local_state::attach(self, |_state| op(self)) local_state::attach(self, |_state| op(self))
} }
/// Plumbing methods.
#[doc(hidden)]
fn zalsa(&self) -> &dyn Zalsa;
#[doc(hidden)]
fn zalsa_mut(&mut self) -> &mut dyn Zalsa;
} }
/// Upcast to a `dyn Database`. /// Upcast to a `dyn Database`.
@ -83,3 +94,79 @@ impl dyn Database {
self.zalsa().views().try_view_as(self).unwrap() self.zalsa().views().try_view_as(self).unwrap()
} }
} }
/// Concrete implementation of the [`Database`][] trait.
/// Takes an optional type parameter `U` that allows you to thread your own data.
pub struct DatabaseImpl<U: UserData = ()> {
storage: Storage<U>,
}
impl<U: UserData + Default> Default for DatabaseImpl<U> {
fn default() -> Self {
Self::with(U::default())
}
}
impl DatabaseImpl<()> {
/// Create a new database with the given user data.
///
/// You can also use the [`Default`][] trait if your userdata implements it.
pub fn new() -> Self {
Self {
storage: Storage::with(()),
}
}
}
impl<U: UserData> DatabaseImpl<U> {
/// Create a new database with the given user data.
///
/// You can also use the [`Default`][] trait if your userdata implements it.
pub fn with(u: U) -> Self {
Self {
storage: Storage::with(u),
}
}
}
impl<U: UserData> std::ops::Deref for DatabaseImpl<U> {
type Target = U;
fn deref(&self) -> &U {
&self.storage.user_data()
}
}
impl<U: UserData + RefUnwindSafe> RefUnwindSafe for DatabaseImpl<U> {}
#[salsa_macros::db]
unsafe impl<U: UserData> Database for DatabaseImpl<U> {
fn zalsa(&self) -> &dyn Zalsa {
&self.storage
}
fn zalsa_mut(&mut self) -> &mut dyn Zalsa {
&mut self.storage
}
// Report a salsa event.
fn salsa_event(&self, event: &dyn Fn() -> Event) {
U::salsa_event(self, event)
}
}
pub trait UserData: Any + Sized {
/// Callback invoked by the [`Database`][] at key points during salsa execution.
/// By overriding this method, you can inject logging or other custom behavior.
///
/// By default, the event is logged at level debug using the `tracing` crate.
///
/// # Parameters
///
/// * `event` a fn that, if called, will return the event that occurred
fn salsa_event(_db: &DatabaseImpl<Self>, event: &dyn Fn() -> Event) {
tracing::debug!("salsa_event: {:?}", event())
}
}
impl UserData for () {}

View file

@ -1,6 +1,4 @@
use crate::{ use crate::{accumulator, hash::FxHashSet, local_state, Database, DatabaseKeyIndex, Id};
accumulator, hash::FxHashSet, local_state, storage::ZalsaDatabase as _, DatabaseKeyIndex, Id,
};
use super::{Configuration, IngredientImpl}; use super::{Configuration, IngredientImpl};

View file

@ -1,8 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
local_state::ActiveQueryGuard, runtime::StampedValue, storage::ZalsaDatabase, Cycle, Database, local_state::ActiveQueryGuard, runtime::StampedValue, Cycle, Database, Event, EventKind,
Event, EventKind,
}; };
use super::{memo::Memo, Configuration, IngredientImpl}; use super::{memo::Memo, Configuration, IngredientImpl};

View file

@ -3,8 +3,7 @@ use arc_swap::Guard;
use crate::{ use crate::{
local_state::{self, LocalState}, local_state::{self, LocalState},
runtime::StampedValue, runtime::StampedValue,
storage::ZalsaDatabase as _, AsDynDatabase as _, Database as _, Id,
AsDynDatabase as _, Id,
}; };
use super::{Configuration, IngredientImpl}; use super::{Configuration, IngredientImpl};

View file

@ -4,8 +4,8 @@ use crate::{
key::DatabaseKeyIndex, key::DatabaseKeyIndex,
local_state::{self, ActiveQueryGuard, EdgeKind, LocalState, QueryOrigin}, local_state::{self, ActiveQueryGuard, EdgeKind, LocalState, QueryOrigin},
runtime::StampedValue, runtime::StampedValue,
storage::{Zalsa, ZalsaDatabase as _}, storage::Zalsa,
AsDynDatabase as _, Id, Revision, AsDynDatabase as _, Database, Id, Revision,
}; };
use super::{memo::Memo, Configuration, IngredientImpl}; use super::{memo::Memo, Configuration, IngredientImpl};

View file

@ -2,7 +2,6 @@ use crossbeam::atomic::AtomicCell;
use crate::{ use crate::{
local_state::{self, QueryOrigin, QueryRevisions}, local_state::{self, QueryOrigin, QueryRevisions},
storage::ZalsaDatabase,
tracked_struct::TrackedStructInDb, tracked_struct::TrackedStructInDb,
AsDynDatabase as _, Database, DatabaseKeyIndex, Id, AsDynDatabase as _, Database, DatabaseKeyIndex, Id,
}; };

View file

@ -31,6 +31,8 @@ pub use self::cancelled::Cancelled;
pub use self::cycle::Cycle; pub use self::cycle::Cycle;
pub use self::database::AsDynDatabase; pub use self::database::AsDynDatabase;
pub use self::database::Database; pub use self::database::Database;
pub use self::database::DatabaseImpl;
pub use self::database::UserData;
pub use self::durability::Durability; pub use self::durability::Durability;
pub use self::event::Event; pub use self::event::Event;
pub use self::event::EventKind; pub use self::event::EventKind;
@ -50,23 +52,9 @@ pub use salsa_macros::interned;
pub use salsa_macros::tracked; pub use salsa_macros::tracked;
pub use salsa_macros::Update; pub use salsa_macros::Update;
pub fn default_database() -> impl Database {
use crate as salsa;
#[crate::db]
#[derive(Default)]
struct DefaultDatabase {
storage: Storage<Self>,
}
#[crate::db]
impl Database for DefaultDatabase {}
DefaultDatabase::default()
}
pub mod prelude { pub mod prelude {
pub use crate::Accumulator; pub use crate::Accumulator;
pub use crate::Database;
pub use crate::Setter; pub use crate::Setter;
} }
@ -82,6 +70,7 @@ pub mod plumbing {
pub use crate::cycle::CycleRecoveryStrategy; pub use crate::cycle::CycleRecoveryStrategy;
pub use crate::database::current_revision; pub use crate::database::current_revision;
pub use crate::database::Database; pub use crate::database::Database;
pub use crate::database::UserData;
pub use crate::function::should_backdate_value; pub use crate::function::should_backdate_value;
pub use crate::id::AsId; pub use crate::id::AsId;
pub use crate::id::FromId; pub use crate::id::FromId;
@ -102,7 +91,6 @@ pub mod plumbing {
pub use crate::storage::IngredientIndex; pub use crate::storage::IngredientIndex;
pub use crate::storage::Storage; pub use crate::storage::Storage;
pub use crate::storage::Zalsa; pub use crate::storage::Zalsa;
pub use crate::storage::ZalsaDatabase;
pub use crate::tracked_struct::TrackedStructInDb; pub use crate::tracked_struct::TrackedStructInDb;
pub use crate::update::always_update; pub use crate::update::always_update;
pub use crate::update::helper::Dispatch as UpdateDispatch; pub use crate::update::helper::Dispatch as UpdateDispatch;

View file

@ -1,10 +1,11 @@
use std::any::{Any, TypeId}; use std::any::TypeId;
use orx_concurrent_vec::ConcurrentVec; use orx_concurrent_vec::ConcurrentVec;
use parking_lot::Mutex; use parking_lot::Mutex;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::cycle::CycleRecoveryStrategy; use crate::cycle::CycleRecoveryStrategy;
use crate::database::{DatabaseImpl, UserData};
use crate::ingredient::{Ingredient, Jar}; use crate::ingredient::{Ingredient, Jar};
use crate::nonce::{Nonce, NonceGenerator}; use crate::nonce::{Nonce, NonceGenerator};
use crate::runtime::Runtime; use crate::runtime::Runtime;
@ -15,22 +16,6 @@ pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
db.zalsa().views() db.zalsa().views()
} }
/// Salsa database methods whose implementation is generated by
/// the `#[salsa::database]` procedural macro.
///
/// # Safety
///
/// This trait is meant to be implemented by our procedural macro.
/// We need to document any non-obvious conditions that it satisfies.
pub unsafe trait ZalsaDatabase: Any {
/// Plumbing methods.
#[doc(hidden)]
fn zalsa(&self) -> &dyn Zalsa;
#[doc(hidden)]
fn zalsa_mut(&mut self) -> &mut dyn Zalsa;
}
/// The "plumbing interface" to the Salsa database. /// The "plumbing interface" to the Salsa database.
/// ///
/// **NOT SEMVER STABLE.** /// **NOT SEMVER STABLE.**
@ -87,9 +72,9 @@ pub trait Zalsa {
fn report_tracked_write(&mut self, durability: Durability); fn report_tracked_write(&mut self, durability: Durability);
} }
impl<Db: Database> Zalsa for Storage<Db> { impl<U: UserData> Zalsa for Storage<U> {
fn views(&self) -> &Views { fn views(&self) -> &Views {
&self.upcasts &self.views_of
} }
fn nonce(&self) -> Nonce<StorageNonce> { fn nonce(&self) -> Nonce<StorageNonce> {
@ -227,8 +212,10 @@ impl IngredientIndex {
/// The "storage" struct stores all the data for the jars. /// The "storage" struct stores all the data for the jars.
/// It is shared between the main database and any active snapshots. /// It is shared between the main database and any active snapshots.
pub struct Storage<Db: Database> { pub struct Storage<U: UserData> {
upcasts: ViewsOf<Db>, user_data: U,
views_of: ViewsOf<DatabaseImpl<U>>,
nonce: Nonce<StorageNonce>, nonce: Nonce<StorageNonce>,
@ -254,19 +241,30 @@ pub struct Storage<Db: Database> {
} }
// ANCHOR: default // ANCHOR: default
impl<Db: Database> Default for Storage<Db> { impl<U: UserData + Default> Default for Storage<U> {
fn default() -> Self { fn default() -> Self {
Self::with(Default::default())
}
}
// ANCHOR_END: default
impl<U: UserData> Storage<U> {
pub(crate) fn with(user_data: U) -> Self {
Self { Self {
upcasts: Default::default(), views_of: Default::default(),
nonce: NONCE.nonce(), nonce: NONCE.nonce(),
jar_map: Default::default(), jar_map: Default::default(),
ingredients_vec: Default::default(), ingredients_vec: Default::default(),
ingredients_requiring_reset: Default::default(), ingredients_requiring_reset: Default::default(),
runtime: Runtime::default(), runtime: Runtime::default(),
user_data,
} }
} }
pub(crate) fn user_data(&self) -> &U {
&self.user_data
}
} }
// ANCHOR_END: default
/// Caches a pointer to an ingredient in a database. /// Caches a pointer to an ingredient in a database.
/// Optimized for the case of a single database. /// Optimized for the case of a single database.

View file

@ -4,7 +4,7 @@
mod common; mod common;
use expect_test::expect; use expect_test::expect;
use salsa::{Accumulator, Database}; use salsa::{Accumulator, Database, DatabaseImpl};
use test_log::test; use test_log::test;
#[salsa::accumulator] #[salsa::accumulator]
@ -40,7 +40,7 @@ fn push_d_logs(db: &dyn Database) {
#[test] #[test]
fn accumulate_chain() { fn accumulate_chain() {
salsa::default_database().attach(|db| { DatabaseImpl::new().attach(|db| {
let logs = push_logs::accumulated::<Log>(db); let logs = push_logs::accumulated::<Log>(db);
// Check that we get all the logs. // Check that we get all the logs.
expect![[r#" expect![[r#"

View file

@ -27,7 +27,7 @@ fn push_logs(db: &dyn salsa::Database, input: MyInput) {
#[test] #[test]
fn accumulate_custom_clone() { fn accumulate_custom_clone() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 2); let input = MyInput::new(db, 2);
let logs = push_logs::accumulated::<Log>(db, input); let logs = push_logs::accumulated::<Log>(db, input);
expect![[r##" expect![[r##"

View file

@ -27,7 +27,7 @@ fn push_logs(db: &dyn salsa::Database, input: MyInput) {
#[test] #[test]
fn accumulate_custom_debug() { fn accumulate_custom_debug() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 2); let input = MyInput::new(db, 2);
let logs = push_logs::accumulated::<Log>(db, input); let logs = push_logs::accumulated::<Log>(db, input);
expect![[r##" expect![[r##"

View file

@ -39,7 +39,7 @@ fn push_b_logs(db: &dyn Database, input: MyInput) {
#[test] #[test]
fn accumulate_a_called_twice() { fn accumulate_a_called_twice() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 2, 3); let input = MyInput::new(db, 2, 3);
let logs = push_logs::accumulated::<Log>(db, input); let logs = push_logs::accumulated::<Log>(db, input);
// Check that we don't see logs from `a` appearing twice in the input. // Check that we don't see logs from `a` appearing twice in the input.

View file

@ -41,7 +41,7 @@ fn push_d_logs(db: &dyn Database) {
#[test] #[test]
fn accumulate_execution_order() { fn accumulate_execution_order() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let logs = push_logs::accumulated::<Log>(db); let logs = push_logs::accumulated::<Log>(db);
// Check that we get logs in execution order // Check that we get logs in execution order
expect![[r#" expect![[r#"

View file

@ -2,16 +2,10 @@
//! Then mutate the values so that the tracked function re-executes. //! Then mutate the values so that the tracked function re-executes.
//! Check that we accumulate the appropriate, new values. //! Check that we accumulate the appropriate, new values.
mod common;
use common::{HasLogger, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::{Accumulator, Setter}; use salsa::{Accumulator, Setter};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct List { struct List {
value: u32, value: u32,
@ -23,7 +17,7 @@ struct List {
struct Integers(u32); struct Integers(u32);
#[salsa::tracked] #[salsa::tracked]
fn compute(db: &dyn Db, input: List) { fn compute(db: &dyn salsa::Database, input: List) {
eprintln!( eprintln!(
"{:?}(value={:?}, next={:?})", "{:?}(value={:?}, next={:?})",
input, input,
@ -43,30 +37,9 @@ fn compute(db: &dyn Db, input: List) {
eprintln!("pushed result {:?}", result); eprintln!("pushed result {:?}", result);
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn test1() { fn test1() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let l0 = List::new(&db, 1, None); let l0 = List::new(&db, 1, None);
let l1 = List::new(&db, 10, Some(l0)); let l1 = List::new(&db, 10, Some(l0));

View file

@ -73,7 +73,7 @@ fn push_e_logs(db: &dyn Database) {
#[test] #[test]
fn accumulate_no_duplicates() { fn accumulate_no_duplicates() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let logs = push_logs::accumulated::<Log>(db); let logs = push_logs::accumulated::<Log>(db);
// Test that there aren't duplicate B logs. // Test that there aren't duplicate B logs.
// Note that log A appears twice, because they both come // Note that log A appears twice, because they both come

View file

@ -3,15 +3,12 @@
//! reuse. //! reuse.
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::{Accumulator, Setter}; use salsa::{Accumulator, DatabaseImpl, Setter};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct List { struct List {
value: u32, value: u32,
@ -23,7 +20,7 @@ struct List {
struct Integers(u32); struct Integers(u32);
#[salsa::tracked] #[salsa::tracked]
fn compute(db: &dyn Db, input: List) -> u32 { fn compute(db: &dyn LogDatabase, input: List) -> u32 {
db.push_log(format!("compute({:?})", input,)); db.push_log(format!("compute({:?})", input,));
// always pushes 0 // always pushes 0
@ -42,38 +39,17 @@ fn compute(db: &dyn Db, input: List) -> u32 {
} }
#[salsa::tracked(return_ref)] #[salsa::tracked(return_ref)]
fn accumulated(db: &dyn Db, input: List) -> Vec<u32> { fn accumulated(db: &dyn LogDatabase, input: List) -> Vec<u32> {
db.push_log(format!("accumulated({:?})", input,)); db.push_log(format!("accumulated({:?})", input));
compute::accumulated::<Integers>(db, input) compute::accumulated::<Integers>(db, input)
.into_iter() .into_iter()
.map(|a| a.0) .map(|a| a.0)
.collect() .collect()
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn test1() { fn test1() {
let mut db = Database::default(); let mut db: DatabaseImpl<Logger> = DatabaseImpl::default();
let l1 = List::new(&db, 1, None); let l1 = List::new(&db, 1, None);
let l2 = List::new(&db, 2, Some(l1)); let l2 = List::new(&db, 2, Some(l1));

View file

@ -4,15 +4,12 @@
//! are the accumulated values from another query. //! are the accumulated values from another query.
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::prelude::*; use salsa::{prelude::*, DatabaseImpl};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct List { struct List {
value: u32, value: u32,
@ -23,7 +20,7 @@ struct List {
struct Integers(u32); struct Integers(u32);
#[salsa::tracked] #[salsa::tracked]
fn compute(db: &dyn Db, input: List) -> u32 { fn compute(db: &dyn LogDatabase, input: List) -> u32 {
db.push_log(format!("compute({:?})", input,)); db.push_log(format!("compute({:?})", input,));
// always pushes 0 // always pushes 0
@ -41,30 +38,9 @@ fn compute(db: &dyn Db, input: List) -> u32 {
result result
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn test1() { fn test1() {
let mut db = Database::default(); let mut db = DatabaseImpl::with(Logger::default());
let l1 = List::new(&db, 1, None); let l1 = List::new(&db, 1, None);
let l2 = List::new(&db, 2, Some(l1)); let l2 = List::new(&db, 2, Some(l1));

View file

@ -1,13 +1,10 @@
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::{Accumulator, Setter}; use salsa::{Accumulator, Setter};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field_a: u32, field_a: u32,
@ -18,7 +15,7 @@ struct MyInput {
struct Log(#[allow(dead_code)] String); struct Log(#[allow(dead_code)] String);
#[salsa::tracked] #[salsa::tracked]
fn push_logs(db: &dyn Db, input: MyInput) { fn push_logs(db: &dyn LogDatabase, input: MyInput) {
db.push_log(format!( db.push_log(format!(
"push_logs(a = {}, b = {})", "push_logs(a = {}, b = {})",
input.field_a(db), input.field_a(db),
@ -37,7 +34,7 @@ fn push_logs(db: &dyn Db, input: MyInput) {
} }
#[salsa::tracked] #[salsa::tracked]
fn push_a_logs(db: &dyn Db, input: MyInput) { fn push_a_logs(db: &dyn LogDatabase, input: MyInput) {
let field_a = input.field_a(db); let field_a = input.field_a(db);
db.push_log(format!("push_a_logs({})", field_a)); db.push_log(format!("push_a_logs({})", field_a));
@ -47,7 +44,7 @@ fn push_a_logs(db: &dyn Db, input: MyInput) {
} }
#[salsa::tracked] #[salsa::tracked]
fn push_b_logs(db: &dyn Db, input: MyInput) { fn push_b_logs(db: &dyn LogDatabase, input: MyInput) {
let field_a = input.field_b(db); let field_a = input.field_b(db);
db.push_log(format!("push_b_logs({})", field_a)); db.push_log(format!("push_b_logs({})", field_a));
@ -56,30 +53,9 @@ fn push_b_logs(db: &dyn Db, input: MyInput) {
} }
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn accumulate_once() { fn accumulate_once() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::with(Logger::default());
// Just call accumulate on a base input to see what happens. // Just call accumulate on a base input to see what happens.
let input = MyInput::new(&db, 2, 3); let input = MyInput::new(&db, 2, 3);
@ -115,7 +91,7 @@ fn accumulate_once() {
#[test] #[test]
fn change_a_from_2_to_0() { fn change_a_from_2_to_0() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::with(Logger::default());
// Accumulate logs for `a = 2` and `b = 3` // Accumulate logs for `a = 2` and `b = 3`
let input = MyInput::new(&db, 2, 3); let input = MyInput::new(&db, 2, 3);
@ -170,7 +146,7 @@ fn change_a_from_2_to_0() {
#[test] #[test]
fn change_a_from_2_to_1() { fn change_a_from_2_to_1() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::with(Logger::default());
// Accumulate logs for `a = 2` and `b = 3` // Accumulate logs for `a = 2` and `b = 3`
let input = MyInput::new(&db, 2, 3); let input = MyInput::new(&db, 2, 3);
@ -229,7 +205,7 @@ fn change_a_from_2_to_1() {
#[test] #[test]
fn get_a_logs_after_changing_b() { fn get_a_logs_after_changing_b() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::with(Logger::default());
// Invoke `push_a_logs` with `a = 2` and `b = 3` (but `b` doesn't matter) // Invoke `push_a_logs` with `a = 2` and `b = 3` (but `b` doesn't matter)
let input = MyInput::new(&db, 2, 3); let input = MyInput::new(&db, 2, 3);

View file

@ -2,16 +2,21 @@
#![allow(dead_code)] #![allow(dead_code)]
use salsa::{DatabaseImpl, UserData};
/// Logging userdata: provides [`LogDatabase`][] trait.
///
/// If you wish to use it along with other userdata,
/// you can also embed it in another struct and implement [`HasLogger`][] for that struct.
#[derive(Default)] #[derive(Default)]
pub struct Logger { pub struct Logger {
logs: std::sync::Mutex<Vec<String>>, logs: std::sync::Mutex<Vec<String>>,
} }
/// Trait implemented by databases that lets them log events. impl UserData for Logger {}
pub trait HasLogger {
/// Return a reference to the logger from the database.
fn logger(&self) -> &Logger;
#[salsa::db]
pub trait LogDatabase: HasLogger + salsa::Database {
/// Log an event from inside a tracked function. /// Log an event from inside a tracked function.
fn push_log(&self, string: String) { fn push_log(&self, string: String) {
self.logger().logs.lock().unwrap().push(string); self.logger().logs.lock().unwrap().push(string);
@ -33,3 +38,86 @@ pub trait HasLogger {
assert_eq!(logs.len(), expected); assert_eq!(logs.len(), expected);
} }
} }
#[salsa::db]
impl<U: HasLogger + UserData> LogDatabase for DatabaseImpl<U> {}
/// Trait implemented by databases that lets them log events.
pub trait HasLogger {
/// Return a reference to the logger from the database.
fn logger(&self) -> &Logger;
}
impl<U: HasLogger + UserData> HasLogger for DatabaseImpl<U> {
fn logger(&self) -> &Logger {
U::logger(self)
}
}
impl HasLogger for Logger {
fn logger(&self) -> &Logger {
self
}
}
/// Userdata that provides logging and logs salsa events.
#[derive(Default)]
pub struct EventLogger {
logger: Logger,
}
impl UserData for EventLogger {
fn salsa_event(db: &DatabaseImpl<Self>, event: &dyn Fn() -> salsa::Event) {
db.push_log(format!("{:?}", event()));
}
}
impl HasLogger for EventLogger {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[derive(Default)]
pub struct DiscardLogger(Logger);
impl UserData for DiscardLogger {
fn salsa_event(db: &DatabaseImpl<DiscardLogger>, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillDiscardStaleOutput { .. }
| salsa::EventKind::DidDiscard { .. } => {
db.push_log(format!("salsa_event({:?})", event.kind));
}
_ => {}
}
}
}
impl HasLogger for DiscardLogger {
fn logger(&self) -> &Logger {
&self.0
}
}
#[derive(Default)]
pub struct ExecuteValidateLogger(Logger);
impl UserData for ExecuteValidateLogger {
fn salsa_event(db: &DatabaseImpl<Self>, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillExecute { .. }
| salsa::EventKind::DidValidateMemoizedValue { .. } => {
db.push_log(format!("salsa_event({:?})", event.kind));
}
_ => {}
}
}
}
impl HasLogger for ExecuteValidateLogger {
fn logger(&self) -> &Logger {
&self.0
}
}

View file

@ -8,7 +8,7 @@ mod a {
} }
fn main() { fn main() {
let mut db = salsa::default_database(); let mut db = salsa::DatabaseImpl::new();
let input = a::MyInput::new(&mut db, 22); let input = a::MyInput::new(&mut db, 22);
input.field(&db); input.field(&db);

View file

@ -16,7 +16,7 @@ fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'d
} }
fn main() { fn main() {
let mut db = salsa::default_database(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input); let tracked = tracked_fn(&db, input);
input.set_field(&mut db).to(24); input.set_field(&mut db).to(24);

View file

@ -4,7 +4,7 @@ pub struct MyInput {
} }
fn main() { fn main() {
let mut db = salsa::default_database(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&mut db, 22); let input = MyInput::new(&mut db, 22);
input.field(&db); input.field(&db);
input.set_field(22); input.set_field(22);

View file

@ -10,6 +10,6 @@ fn my_fn(db: &dyn salsa::Database) {
} }
fn main() { fn main() {
let mut db = salsa::default_database(); let mut db = salsa::DatabaseImpl::new();
my_fn(&db); my_fn(&db);
} }

View file

@ -24,7 +24,7 @@ help: consider borrowing here
warning: variable does not need to be mutable warning: variable does not need to be mutable
--> tests/compile-fail/span-tracked-getter.rs:13:9 --> tests/compile-fail/span-tracked-getter.rs:13:9
| |
13 | let mut db = salsa::default_database(); 13 | let mut db = salsa::DatabaseImpl::new();
| ----^^ | ----^^
| | | |
| help: remove this `mut` | help: remove this `mut`

View file

@ -3,6 +3,7 @@
use std::panic::{RefUnwindSafe, UnwindSafe}; use std::panic::{RefUnwindSafe, UnwindSafe};
use expect_test::expect; use expect_test::expect;
use salsa::DatabaseImpl;
use salsa::Durability; use salsa::Durability;
// Axes: // Axes:
@ -59,17 +60,6 @@ struct Error {
use salsa::Database as Db; use salsa::Database as Db;
use salsa::Setter; use salsa::Setter;
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
impl RefUnwindSafe for Database {}
#[salsa::input] #[salsa::input]
struct MyInput {} struct MyInput {}
@ -169,7 +159,7 @@ fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle {
#[test] #[test]
fn cycle_memoized() { fn cycle_memoized() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db); let input = MyInput::new(db);
let cycle = extract_cycle(|| memoized_a(db, input)); let cycle = extract_cycle(|| memoized_a(db, input));
let expected = expect![[r#" let expected = expect![[r#"
@ -184,7 +174,7 @@ fn cycle_memoized() {
#[test] #[test]
fn cycle_volatile() { fn cycle_volatile() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db); let input = MyInput::new(db);
let cycle = extract_cycle(|| volatile_a(db, input)); let cycle = extract_cycle(|| volatile_a(db, input));
let expected = expect![[r#" let expected = expect![[r#"
@ -203,7 +193,7 @@ fn expect_cycle() {
// ^ | // ^ |
// +-----+ // +-----+
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let abc = ABC::new(db, CycleQuery::B, CycleQuery::A, CycleQuery::None); let abc = ABC::new(db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
assert!(cycle_a(db, abc).is_err()); assert!(cycle_a(db, abc).is_err());
}) })
@ -214,7 +204,7 @@ fn inner_cycle() {
// A --> B <-- C // A --> B <-- C
// ^ | // ^ |
// +-----+ // +-----+
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let abc = ABC::new(db, CycleQuery::B, CycleQuery::A, CycleQuery::B); let abc = ABC::new(db, CycleQuery::B, CycleQuery::A, CycleQuery::B);
let err = cycle_c(db, abc); let err = cycle_c(db, abc);
assert!(err.is_err()); assert!(err.is_err());
@ -233,7 +223,7 @@ fn cycle_revalidate() {
// A --> B // A --> B
// ^ | // ^ |
// +-----+ // +-----+
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None); let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
assert!(cycle_a(&db, abc).is_err()); assert!(cycle_a(&db, abc).is_err());
abc.set_b(&mut db).to(CycleQuery::A); // same value as default abc.set_b(&mut db).to(CycleQuery::A); // same value as default
@ -245,7 +235,7 @@ fn cycle_recovery_unchanged_twice() {
// A --> B // A --> B
// ^ | // ^ |
// +-----+ // +-----+
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None); let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
assert!(cycle_a(&db, abc).is_err()); assert!(cycle_a(&db, abc).is_err());
@ -255,8 +245,7 @@ fn cycle_recovery_unchanged_twice() {
#[test] #[test]
fn cycle_appears() { fn cycle_appears() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
// A --> B // A --> B
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::None, CycleQuery::None); let abc = ABC::new(&db, CycleQuery::B, CycleQuery::None, CycleQuery::None);
assert!(cycle_a(&db, abc).is_ok()); assert!(cycle_a(&db, abc).is_ok());
@ -270,7 +259,7 @@ fn cycle_appears() {
#[test] #[test]
fn cycle_disappears() { fn cycle_disappears() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
// A --> B // A --> B
// ^ | // ^ |
@ -289,7 +278,7 @@ fn cycle_disappears() {
/// the fact that the cycle will no longer occur. /// the fact that the cycle will no longer occur.
#[test] #[test]
fn cycle_disappears_durability() { fn cycle_disappears_durability() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let abc = ABC::new( let abc = ABC::new(
&mut db, &mut db,
CycleQuery::None, CycleQuery::None,
@ -320,7 +309,7 @@ fn cycle_disappears_durability() {
#[test] #[test]
fn cycle_mixed_1() { fn cycle_mixed_1() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
// A --> B <-- C // A --> B <-- C
// | ^ // | ^
// +-----+ // +-----+
@ -338,7 +327,7 @@ fn cycle_mixed_1() {
#[test] #[test]
fn cycle_mixed_2() { fn cycle_mixed_2() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
// Configuration: // Configuration:
// //
// A --> B --> C // A --> B --> C
@ -360,7 +349,7 @@ fn cycle_mixed_2() {
fn cycle_deterministic_order() { fn cycle_deterministic_order() {
// No matter whether we start from A or B, we get the same set of participants: // No matter whether we start from A or B, we get the same set of participants:
let f = || { let f = || {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
// A --> B // A --> B
// ^ | // ^ |
@ -390,7 +379,7 @@ fn cycle_deterministic_order() {
#[test] #[test]
fn cycle_multiple() { fn cycle_multiple() {
// No matter whether we start from A or B, we get the same set of participants: // No matter whether we start from A or B, we get the same set of participants:
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
// Configuration: // Configuration:
// //
@ -432,7 +421,7 @@ fn cycle_multiple() {
#[test] #[test]
fn cycle_recovery_set_but_not_participating() { fn cycle_recovery_set_but_not_participating() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
// A --> C -+ // A --> C -+
// ^ | // ^ |
// +--+ // +--+

View file

@ -1,7 +1,7 @@
//! Test that `DeriveWithDb` is correctly derived. //! Test that `DeriveWithDb` is correctly derived.
use expect_test::expect; use expect_test::expect;
use salsa::{Database as _, Setter}; use salsa::{Database, Setter};
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
@ -19,18 +19,9 @@ struct ComplexStruct {
not_salsa: NotSalsa, not_salsa: NotSalsa,
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn input() { fn input() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 22); let input = MyInput::new(db, 22);
let not_salsa = NotSalsa { let not_salsa = NotSalsa {
field: "it's salsa time".to_string(), field: "it's salsa time".to_string(),
@ -54,7 +45,7 @@ fn leak_debug_string(_db: &dyn salsa::Database, input: MyInput) -> String {
/// Don't try this at home, kids. /// Don't try this at home, kids.
#[test] #[test]
fn untracked_dependencies() { fn untracked_dependencies() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
@ -95,7 +86,7 @@ fn leak_derived_custom(db: &dyn salsa::Database, input: MyInput, value: u32) ->
#[test] #[test]
fn custom_debug_impl() { fn custom_debug_impl() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);

View file

@ -3,22 +3,19 @@
//! * when we delete memoized data, also delete outputs from that data //! * when we delete memoized data, also delete outputs from that data
mod common; mod common;
use common::{HasLogger, Logger}; use common::{DiscardLogger, LogDatabase};
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::{DatabaseImpl, Setter};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input(singleton)] #[salsa::input(singleton)]
struct MyInput { struct MyInput {
field: u32, field: u32,
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result(db: &dyn Db, input: MyInput) -> u32 { fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input)); db.push_log(format!("final_result({:?})", input));
let mut sum = 0; let mut sum = 0;
for tracked_struct in create_tracked_structs(db, input) { for tracked_struct in create_tracked_structs(db, input) {
@ -33,7 +30,7 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> { fn create_tracked_structs(db: &dyn LogDatabase, input: MyInput) -> Vec<MyTracked<'_>> {
db.push_log(format!("intermediate_result({:?})", input)); db.push_log(format!("intermediate_result({:?})", input));
(0..input.field(db)) (0..input.field(db))
.map(|i| MyTracked::new(db, i)) .map(|i| MyTracked::new(db, i))
@ -41,49 +38,19 @@ fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> {
} }
#[salsa::tracked] #[salsa::tracked]
fn contribution_from_struct<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> u32 { fn contribution_from_struct<'db>(db: &'db dyn LogDatabase, tracked: MyTracked<'db>) -> u32 {
let m = MyTracked::new(db, tracked.field(db)); let m = MyTracked::new(db, tracked.field(db));
copy_field(db, m) * 2 copy_field(db, m) * 2
} }
#[salsa::tracked] #[salsa::tracked]
fn copy_field<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> u32 { fn copy_field<'db>(db: &'db dyn LogDatabase, tracked: MyTracked<'db>) -> u32 {
tracked.field(db) tracked.field(db)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillDiscardStaleOutput { .. }
| salsa::EventKind::DidDiscard { .. } => {
self.push_log(format!("salsa_event({:?})", event.kind));
}
_ => {}
}
}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn basic() { fn basic() {
let mut db = Database::default(); let mut db: DatabaseImpl<DiscardLogger> = Default::default();
// Creates 3 tracked structs // Creates 3 tracked structs
let input = MyInput::new(&db, 3); let input = MyInput::new(&db, 3);

View file

@ -56,7 +56,7 @@ impl MyInput {
#[test] #[test]
fn deletion_drops() { fn deletion_drops() {
let mut db = salsa::default_database(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);

View file

@ -3,22 +3,19 @@
//! * entities not created in a revision are deleted, as is any memoized data keyed on them. //! * entities not created in a revision are deleted, as is any memoized data keyed on them.
mod common; mod common;
use common::{HasLogger, Logger}; use common::LogDatabase;
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::Setter;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: u32, field: u32,
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result(db: &dyn Db, input: MyInput) -> u32 { fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input)); db.push_log(format!("final_result({:?})", input));
let mut sum = 0; let mut sum = 0;
for tracked_struct in create_tracked_structs(db, input) { for tracked_struct in create_tracked_structs(db, input) {
@ -33,7 +30,7 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> { fn create_tracked_structs(db: &dyn LogDatabase, input: MyInput) -> Vec<MyTracked<'_>> {
db.push_log(format!("intermediate_result({:?})", input)); db.push_log(format!("intermediate_result({:?})", input));
(0..input.field(db)) (0..input.field(db))
.map(|i| MyTracked::new(db, i)) .map(|i| MyTracked::new(db, i))
@ -41,43 +38,13 @@ fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> {
} }
#[salsa::tracked] #[salsa::tracked]
fn contribution_from_struct<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> u32 { fn contribution_from_struct<'db>(db: &'db dyn LogDatabase, tracked: MyTracked<'db>) -> u32 {
tracked.field(db) * 2 tracked.field(db) * 2
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillDiscardStaleOutput { .. }
| salsa::EventKind::DidDiscard { .. } => {
self.push_log(format!("salsa_event({:?})", event.kind));
}
_ => {}
}
}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn basic() { fn basic() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<common::DiscardLogger> = Default::default();
// Creates 3 tracked structs // Creates 3 tracked structs
let input = MyInput::new(&db, 3); let input = MyInput::new(&db, 3);

View file

@ -2,22 +2,19 @@
//! compiles and executes successfully. //! compiles and executes successfully.
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::{DatabaseImpl, Setter};
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: u32, field: u32,
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result(db: &dyn Db, input: MyInput) -> u32 { fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input)); db.push_log(format!("final_result({:?})", input));
intermediate_result(db, input).field(db) * 2 intermediate_result(db, input).field(db) * 2
} }
@ -28,33 +25,14 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked<'_> { fn intermediate_result(db: &dyn LogDatabase, input: MyInput) -> MyTracked<'_> {
db.push_log(format!("intermediate_result({:?})", input)); db.push_log(format!("intermediate_result({:?})", input));
MyTracked::new(db, input.field(db) / 2) MyTracked::new(db, input.field(db) / 2)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db: DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22); assert_eq!(final_result(&db, input), 22);

View file

@ -4,13 +4,10 @@
#![allow(dead_code)] #![allow(dead_code)]
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::{DatabaseImpl, Setter};
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
@ -18,13 +15,13 @@ struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result_depends_on_x(db: &dyn Db, input: MyInput) -> u32 { fn final_result_depends_on_x(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result_depends_on_x({:?})", input)); db.push_log(format!("final_result_depends_on_x({:?})", input));
intermediate_result(db, input).x(db) * 2 intermediate_result(db, input).x(db) * 2
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result_depends_on_y(db: &dyn Db, input: MyInput) -> u32 { fn final_result_depends_on_y(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result_depends_on_y({:?})", input)); db.push_log(format!("final_result_depends_on_y({:?})", input));
intermediate_result(db, input).y(db) * 2 intermediate_result(db, input).y(db) * 2
} }
@ -36,36 +33,17 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked<'_> { fn intermediate_result(db: &dyn LogDatabase, input: MyInput) -> MyTracked<'_> {
MyTracked::new(db, (input.field(db) + 1) / 2, input.field(db) / 2) MyTracked::new(db, (input.field(db) + 1) / 2, input.field(db) / 2)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
// x = (input.field + 1) / 2 // x = (input.field + 1) / 2
// y = input.field / 2 // y = input.field / 2
// final_result_depends_on_x = x * 2 = (input.field + 1) / 2 * 2 // final_result_depends_on_x = x * 2 = (input.field + 1) / 2 * 2
// final_result_depends_on_y = y * 2 = input.field / 2 * 2 // final_result_depends_on_y = y * 2 = input.field / 2 * 2
let mut db = Database::default(); let mut db: DatabaseImpl<Logger> = Default::default();
// intermediate results: // intermediate results:
// x = (22 + 1) / 2 = 11 // x = (22 + 1) / 2 = 11

View file

@ -4,14 +4,11 @@
#![allow(dead_code)] #![allow(dead_code)]
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::Setter;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
x: u32, x: u32,
@ -19,41 +16,21 @@ struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
fn result_depends_on_x(db: &dyn Db, input: MyInput) -> u32 { fn result_depends_on_x(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("result_depends_on_x({:?})", input)); db.push_log(format!("result_depends_on_x({:?})", input));
input.x(db) + 1 input.x(db) + 1
} }
#[salsa::tracked] #[salsa::tracked]
fn result_depends_on_y(db: &dyn Db, input: MyInput) -> u32 { fn result_depends_on_y(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("result_depends_on_y({:?})", input)); db.push_log(format!("result_depends_on_y({:?})", input));
input.y(db) - 1 input.y(db) - 1
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
// result_depends_on_x = x + 1 // result_depends_on_x = x + 1
// result_depends_on_y = y - 1 // result_depends_on_y = y - 1
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22, 33); let input = MyInput::new(&db, 22, 33);
assert_eq!(result_depends_on_x(&db, input), 23); assert_eq!(result_depends_on_x(&db, input), 23);

View file

@ -2,22 +2,19 @@
//! compiles and executes successfully. //! compiles and executes successfully.
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::Setter; use salsa::Setter;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: u32, field: u32,
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result(db: &dyn Db, input: MyInput) -> u32 { fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input)); db.push_log(format!("final_result({:?})", input));
intermediate_result(db, input).field(db) * 2 intermediate_result(db, input).field(db) * 2
} }
@ -28,33 +25,14 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked<'_> { fn intermediate_result(db: &dyn LogDatabase, input: MyInput) -> MyTracked<'_> {
db.push_log(format!("intermediate_result({:?})", input)); db.push_log(format!("intermediate_result({:?})", input));
MyTracked::new(db, input.field(db) / 2) MyTracked::new(db, input.field(db) / 2)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22); assert_eq!(final_result(&db, input), 22);
@ -85,7 +63,7 @@ fn execute() {
/// Create and mutate a distinct input. No re-execution required. /// Create and mutate a distinct input. No re-execution required.
#[test] #[test]
fn red_herring() { fn red_herring() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22); assert_eq!(final_result(&db, input), 22);

View file

@ -1,14 +1,9 @@
//! Test that a `tracked` fn on a `salsa::input` //! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully. //! compiles and executes successfully.
mod common;
use common::{HasLogger, Logger};
use expect_test::expect; use expect_test::expect;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::interned] #[salsa::interned]
struct InternedString<'db> { struct InternedString<'db> {
data: String, data: String,
@ -20,38 +15,17 @@ struct InternedPair<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn intern_stuff(db: &dyn Db) -> String { fn intern_stuff(db: &dyn salsa::Database) -> String {
let s1 = InternedString::new(db, "Hello, ".to_string()); let s1 = InternedString::new(db, "Hello, ".to_string());
let s2 = InternedString::new(db, "World, ".to_string()); let s2 = InternedString::new(db, "World, ".to_string());
let s3 = InternedPair::new(db, (s1, s2)); let s3 = InternedPair::new(db, (s1, s2));
format!("{s3:?}") format!("{s3:?}")
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let db = salsa::DatabaseImpl::new();
expect![[r#" expect![[r#"
"InternedPair { data: (InternedString { data: \"Hello, \" }, InternedString { data: \"World, \" }) }" "InternedPair { data: (InternedString { data: \"Hello, \" }, InternedString { data: \"World, \" }) }"
"#]].assert_debug_eq(&intern_stuff(&db)); "#]].assert_debug_eq(&intern_stuff(&db));
db.assert_logs(expect!["[]"]);
} }

View file

@ -1,23 +1,9 @@
//! Test that a setting a field on a `#[salsa::input]` //! Test that a setting a field on a `#[salsa::input]`
//! overwrites and returns the old value. //! overwrites and returns the old value.
use salsa::Database;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database {}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: String, field: String,
@ -34,7 +20,7 @@ struct MyInterned<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn test(db: &dyn crate::Db, input: MyInput) { fn test(db: &dyn Database, input: MyInput) {
let input = is_send_sync(input); let input = is_send_sync(input);
let interned = is_send_sync(MyInterned::new(db, input.field(db).clone())); let interned = is_send_sync(MyInterned::new(db, input.field(db).clone()));
let _tracked_struct = is_send_sync(MyTracked::new(db, interned)); let _tracked_struct = is_send_sync(MyTracked::new(db, interned));
@ -46,7 +32,7 @@ fn is_send_sync<T: Send + Sync>(t: T) -> T {
#[test] #[test]
fn execute() { fn execute() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, "Hello".to_string()); let input = MyInput::new(&db, "Hello".to_string());
test(&db, input); test(&db, input);
} }

View file

@ -6,14 +6,11 @@ use std::sync::{
Arc, Arc,
}; };
use salsa::Database as _;
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use salsa::Database as _;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct HotPotato(u32); struct HotPotato(u32);
@ -40,50 +37,31 @@ struct MyInput {
} }
#[salsa::tracked(lru = 32)] #[salsa::tracked(lru = 32)]
fn get_hot_potato(db: &dyn Db, input: MyInput) -> Arc<HotPotato> { fn get_hot_potato(db: &dyn LogDatabase, input: MyInput) -> Arc<HotPotato> {
db.push_log(format!("get_hot_potato({:?})", input.field(db))); db.push_log(format!("get_hot_potato({:?})", input.field(db)));
Arc::new(HotPotato::new(input.field(db))) Arc::new(HotPotato::new(input.field(db)))
} }
#[salsa::tracked] #[salsa::tracked]
fn get_hot_potato2(db: &dyn Db, input: MyInput) -> u32 { fn get_hot_potato2(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("get_hot_potato2({:?})", input.field(db))); db.push_log(format!("get_hot_potato2({:?})", input.field(db)));
get_hot_potato(db, input).0 get_hot_potato(db, input).0
} }
#[salsa::tracked(lru = 32)] #[salsa::tracked(lru = 32)]
fn get_volatile(db: &dyn Db, _input: MyInput) -> usize { fn get_volatile(db: &dyn LogDatabase, _input: MyInput) -> usize {
static COUNTER: AtomicUsize = AtomicUsize::new(0); static COUNTER: AtomicUsize = AtomicUsize::new(0);
db.report_untracked_read(); db.report_untracked_read();
COUNTER.fetch_add(1, Ordering::SeqCst) COUNTER.fetch_add(1, Ordering::SeqCst)
} }
#[salsa::db]
#[derive(Default)]
struct DatabaseImpl {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for DatabaseImpl {}
#[salsa::db]
impl Db for DatabaseImpl {}
impl HasLogger for DatabaseImpl {
fn logger(&self) -> &Logger {
&self.logger
}
}
fn load_n_potatoes() -> usize { fn load_n_potatoes() -> usize {
N_POTATOES.with(|n| n.load(Ordering::SeqCst)) N_POTATOES.with(|n| n.load(Ordering::SeqCst))
} }
#[test] #[test]
fn lru_works() { fn lru_works() {
let db = DatabaseImpl::default(); let db: salsa::DatabaseImpl<Logger> = Default::default();
assert_eq!(load_n_potatoes(), 0); assert_eq!(load_n_potatoes(), 0);
for i in 0..128u32 { for i in 0..128u32 {
@ -99,7 +77,7 @@ fn lru_works() {
#[test] #[test]
fn lru_doesnt_break_volatile_queries() { fn lru_doesnt_break_volatile_queries() {
let db = DatabaseImpl::default(); let db: salsa::DatabaseImpl<Logger> = Default::default();
// Create all inputs first, so that there are no revision changes among calls to `get_volatile` // Create all inputs first, so that there are no revision changes among calls to `get_volatile`
let inputs: Vec<MyInput> = (0..128usize).map(|i| MyInput::new(&db, i as u32)).collect(); let inputs: Vec<MyInput> = (0..128usize).map(|i| MyInput::new(&db, i as u32)).collect();
@ -117,7 +95,7 @@ fn lru_doesnt_break_volatile_queries() {
#[test] #[test]
fn lru_can_be_changed_at_runtime() { fn lru_can_be_changed_at_runtime() {
let db = DatabaseImpl::default(); let db: salsa::DatabaseImpl<Logger> = Default::default();
assert_eq!(load_n_potatoes(), 0); assert_eq!(load_n_potatoes(), 0);
let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&db, i))).collect(); let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&db, i))).collect();
@ -160,7 +138,7 @@ fn lru_can_be_changed_at_runtime() {
#[test] #[test]
fn lru_keeps_dependency_info() { fn lru_keeps_dependency_info() {
let mut db = DatabaseImpl::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let capacity = 32; let capacity = 32;
// Invoke `get_hot_potato2` 33 times. This will (in turn) invoke // Invoke `get_hot_potato2` 33 times. This will (in turn) invoke

View file

@ -1,9 +1,6 @@
//! Test that a setting a field on a `#[salsa::input]` //! Test that a setting a field on a `#[salsa::input]`
//! overwrites and returns the old value. //! overwrites and returns the old value.
mod common;
use common::{HasLogger, Logger};
use salsa::Setter; use salsa::Setter;
use test_log::test; use test_log::test;
@ -12,25 +9,9 @@ struct MyInput {
field: String, field: String,
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, "Hello".to_string()); let input = MyInput::new(&db, "Hello".to_string());

View file

@ -66,17 +66,5 @@ impl<'db> MyTracked<'db> {
#[test] #[test]
fn execute() { fn execute() {
#[salsa::db] salsa::DatabaseImpl::new();
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
let mut db = Database::default();
} }

View file

@ -6,20 +6,11 @@ struct MyTracked<'db> {
field: u32, field: u32,
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
#[should_panic( #[should_panic(
expected = "cannot create a tracked struct disambiguator outside of a tracked function" expected = "cannot create a tracked struct disambiguator outside of a tracked function"
)] )]
fn execute() { fn execute() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
MyTracked::new(&db, 0); MyTracked::new(&db, 0);
} }

View file

@ -3,17 +3,12 @@
//! both intra and cross thread. //! both intra and cross thread.
use salsa::Cancelled; use salsa::Cancelled;
use salsa::DatabaseImpl;
use salsa::Handle; use salsa::Handle;
use salsa::Setter; use salsa::Setter;
use crate::setup::Database;
use crate::setup::Knobs; use crate::setup::Knobs;
use crate::setup::KnobsDatabase;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
@ -21,14 +16,14 @@ struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
fn a1(db: &dyn Db, input: MyInput) -> MyInput { fn a1(db: &dyn KnobsDatabase, input: MyInput) -> MyInput {
db.signal(1); db.signal(1);
db.wait_for(2); db.wait_for(2);
dummy(db, input) dummy(db, input)
} }
#[salsa::tracked] #[salsa::tracked]
fn dummy(_db: &dyn Db, _input: MyInput) -> MyInput { fn dummy(_db: &dyn KnobsDatabase, _input: MyInput) -> MyInput {
panic!("should never get here!") panic!("should never get here!")
} }
@ -49,7 +44,7 @@ fn dummy(_db: &dyn Db, _input: MyInput) -> MyInput {
#[test] #[test]
fn execute() { fn execute() {
let mut db = Handle::new(Database::default()); let mut db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3); db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, 1); let input = MyInput::new(&*db, 1);

View file

@ -2,16 +2,11 @@
//! See `../cycles.rs` for a complete listing of cycle tests, //! See `../cycles.rs` for a complete listing of cycle tests,
//! both intra and cross thread. //! both intra and cross thread.
use salsa::DatabaseImpl;
use salsa::Handle; use salsa::Handle;
use crate::setup::Database;
use crate::setup::Knobs; use crate::setup::Knobs;
use crate::setup::KnobsDatabase;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
#[salsa::input] #[salsa::input]
pub(crate) struct MyInput { pub(crate) struct MyInput {
@ -19,7 +14,7 @@ pub(crate) struct MyInput {
} }
#[salsa::tracked(recovery_fn = recover_a1)] #[salsa::tracked(recovery_fn = recover_a1)]
pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.signal(1); db.signal(1);
db.wait_for(2); db.wait_for(2);
@ -27,23 +22,23 @@ pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 {
a2(db, input) a2(db, input)
} }
fn recover_a1(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_a1(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_a1"); dbg!("recover_a1");
key.field(db) * 10 + 1 key.field(db) * 10 + 1
} }
#[salsa::tracked(recovery_fn=recover_a2)] #[salsa::tracked(recovery_fn=recover_a2)]
pub(crate) fn a2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
b1(db, input) b1(db, input)
} }
fn recover_a2(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_a2(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_a2"); dbg!("recover_a2");
key.field(db) * 10 + 2 key.field(db) * 10 + 2
} }
#[salsa::tracked(recovery_fn=recover_b1)] #[salsa::tracked(recovery_fn=recover_b1)]
pub(crate) fn b1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.wait_for(1); db.wait_for(1);
db.signal(2); db.signal(2);
@ -53,17 +48,17 @@ pub(crate) fn b1(db: &dyn Db, input: MyInput) -> i32 {
b2(db, input) b2(db, input)
} }
fn recover_b1(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_b1(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_b1"); dbg!("recover_b1");
key.field(db) * 20 + 1 key.field(db) * 20 + 1
} }
#[salsa::tracked(recovery_fn=recover_b2)] #[salsa::tracked(recovery_fn=recover_b2)]
pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
a1(db, input) a1(db, input)
} }
fn recover_b2(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_b2(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_b2"); dbg!("recover_b2");
key.field(db) * 20 + 2 key.field(db) * 20 + 2
} }
@ -92,7 +87,7 @@ fn recover_b2(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
#[test] #[test]
fn execute() { fn execute() {
let db = Handle::new(Database::default()); let db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3); db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, 1); let input = MyInput::new(&*db, 1);

View file

@ -2,16 +2,9 @@
//! See `../cycles.rs` for a complete listing of cycle tests, //! See `../cycles.rs` for a complete listing of cycle tests,
//! both intra and cross thread. //! both intra and cross thread.
use salsa::Handle; use salsa::{DatabaseImpl, Handle};
use crate::setup::Database; use crate::setup::{Knobs, KnobsDatabase};
use crate::setup::Knobs;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
#[salsa::input] #[salsa::input]
pub(crate) struct MyInput { pub(crate) struct MyInput {
@ -19,7 +12,7 @@ pub(crate) struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// tell thread b we have started // tell thread b we have started
db.signal(1); db.signal(1);
@ -30,25 +23,25 @@ pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 {
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn a2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// create the cycle // create the cycle
b1(db, input) b1(db, input)
} }
#[salsa::tracked(recovery_fn=recover_b1)] #[salsa::tracked(recovery_fn=recover_b1)]
pub(crate) fn b1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// wait for thread a to have started // wait for thread a to have started
db.wait_for(1); db.wait_for(1);
b2(db, input) b2(db, input)
} }
fn recover_b1(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_b1(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_b1"); dbg!("recover_b1");
key.field(db) * 20 + 2 key.field(db) * 20 + 2
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// will encounter a cycle but recover // will encounter a cycle but recover
b3(db, input); b3(db, input);
b1(db, input); // hasn't recovered yet b1(db, input); // hasn't recovered yet
@ -56,12 +49,12 @@ pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 {
} }
#[salsa::tracked(recovery_fn=recover_b3)] #[salsa::tracked(recovery_fn=recover_b3)]
pub(crate) fn b3(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b3(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// will block on thread a, signaling stage 2 // will block on thread a, signaling stage 2
a1(db, input) a1(db, input)
} }
fn recover_b3(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover_b3(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover_b3"); dbg!("recover_b3");
key.field(db) * 200 + 2 key.field(db) * 200 + 2
} }
@ -88,7 +81,7 @@ fn recover_b3(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
#[test] #[test]
fn execute() { fn execute() {
let db = Handle::new(Database::default()); let db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3); db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, 1); let input = MyInput::new(&*db, 1);

View file

@ -2,25 +2,20 @@
//! See the `../cycles.rs` for a complete listing of cycle tests, //! See the `../cycles.rs` for a complete listing of cycle tests,
//! both intra and cross thread. //! both intra and cross thread.
use crate::setup::Database;
use crate::setup::Knobs; use crate::setup::Knobs;
use crate::setup::KnobsDatabase;
use expect_test::expect; use expect_test::expect;
use salsa::Database as _; use salsa::Database as _;
use salsa::DatabaseImpl;
use salsa::Handle; use salsa::Handle;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
#[salsa::input] #[salsa::input]
pub(crate) struct MyInput { pub(crate) struct MyInput {
field: i32, field: i32,
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn a(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.signal(1); db.signal(1);
db.wait_for(2); db.wait_for(2);
@ -29,7 +24,7 @@ pub(crate) fn a(db: &dyn Db, input: MyInput) -> i32 {
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn b(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.wait_for(1); db.wait_for(1);
db.signal(2); db.signal(2);
@ -43,7 +38,7 @@ pub(crate) fn b(db: &dyn Db, input: MyInput) -> i32 {
#[test] #[test]
fn execute() { fn execute() {
let db = Handle::new(Database::default()); let db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3); db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, -1); let input = MyInput::new(&*db, -1);

View file

@ -2,16 +2,9 @@
//! See `../cycles.rs` for a complete listing of cycle tests, //! See `../cycles.rs` for a complete listing of cycle tests,
//! both intra and cross thread. //! both intra and cross thread.
use salsa::Handle; use salsa::{DatabaseImpl, Handle};
use crate::setup::Database; use crate::setup::{Knobs, KnobsDatabase};
use crate::setup::Knobs;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
#[salsa::input] #[salsa::input]
pub(crate) struct MyInput { pub(crate) struct MyInput {
@ -19,7 +12,7 @@ pub(crate) struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.signal(1); db.signal(1);
db.wait_for(2); db.wait_for(2);
@ -28,17 +21,17 @@ pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 {
} }
#[salsa::tracked(recovery_fn=recover)] #[salsa::tracked(recovery_fn=recover)]
pub(crate) fn a2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn a2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
b1(db, input) b1(db, input)
} }
fn recover(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 { fn recover(db: &dyn KnobsDatabase, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
dbg!("recover"); dbg!("recover");
key.field(db) * 20 + 2 key.field(db) * 20 + 2
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn b1(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b1(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
// Wait to create the cycle until both threads have entered // Wait to create the cycle until both threads have entered
db.wait_for(1); db.wait_for(1);
db.signal(2); db.signal(2);
@ -49,7 +42,7 @@ pub(crate) fn b1(db: &dyn Db, input: MyInput) -> i32 {
} }
#[salsa::tracked] #[salsa::tracked]
pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 { pub(crate) fn b2(db: &dyn KnobsDatabase, input: MyInput) -> i32 {
a1(db, input) a1(db, input)
} }
@ -77,7 +70,7 @@ pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 {
#[test] #[test]
fn execute() { fn execute() {
let db = Handle::new(Database::default()); let db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3); db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, 1); let input = MyInput::new(&*db, 1);

View file

@ -1,11 +1,13 @@
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use salsa::{Database, DatabaseImpl, UserData};
use crate::signal::Signal; use crate::signal::Signal;
/// Various "knobs" and utilities used by tests to force /// Various "knobs" and utilities used by tests to force
/// a certain behavior. /// a certain behavior.
pub(crate) trait Knobs { #[salsa::db]
fn knobs(&self) -> &KnobsStruct; pub(crate) trait KnobsDatabase: Database {
fn knobs(&self) -> &Knobs;
fn signal(&self, stage: usize); fn signal(&self, stage: usize);
@ -16,7 +18,7 @@ pub(crate) trait Knobs {
/// behave on one specific thread. Note that this state is /// behave on one specific thread. Note that this state is
/// intentionally thread-local (apart from `signal`). /// intentionally thread-local (apart from `signal`).
#[derive(Default)] #[derive(Default)]
pub(crate) struct KnobsStruct { pub(crate) struct Knobs {
/// A kind of flexible barrier used to coordinate execution across /// A kind of flexible barrier used to coordinate execution across
/// threads to ensure we reach various weird states. /// threads to ensure we reach various weird states.
pub(crate) signal: Signal, pub(crate) signal: Signal,
@ -28,39 +30,32 @@ pub(crate) struct KnobsStruct {
pub(crate) signal_on_did_cancel: AtomicCell<usize>, pub(crate) signal_on_did_cancel: AtomicCell<usize>,
} }
#[salsa::db] impl UserData for Knobs {
#[derive(Default)] fn salsa_event(db: &DatabaseImpl<Self>, event: &dyn Fn() -> salsa::Event) {
pub(crate) struct Database {
storage: salsa::Storage<Self>,
knobs: KnobsStruct,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event(); let event = event();
match event.kind { match event.kind {
salsa::EventKind::WillBlockOn { .. } => { salsa::EventKind::WillBlockOn { .. } => {
self.signal(self.knobs().signal_on_will_block.load()); db.signal(db.signal_on_will_block.load());
} }
salsa::EventKind::DidSetCancellationFlag => { salsa::EventKind::DidSetCancellationFlag => {
self.signal(self.knobs().signal_on_did_cancel.load()); db.signal(db.signal_on_did_cancel.load());
} }
_ => {} _ => {}
} }
} }
} }
impl Knobs for Database { #[salsa::db]
fn knobs(&self) -> &KnobsStruct { impl KnobsDatabase for DatabaseImpl<Knobs> {
&self.knobs fn knobs(&self) -> &Knobs {
self
} }
fn signal(&self, stage: usize) { fn signal(&self, stage: usize) {
self.knobs.signal.signal(stage); self.signal.signal(stage);
} }
fn wait_for(&self, stage: usize) { fn wait_for(&self, stage: usize) {
self.knobs.signal.wait_for(stage); self.signal.wait_for(stage);
} }
} }

View file

@ -5,41 +5,14 @@ use std::cell::Cell;
use expect_test::expect; use expect_test::expect;
mod common; mod common;
use common::{HasLogger, Logger}; use common::{EventLogger, LogDatabase};
use salsa::Setter; use salsa::{Database, Setter};
use test_log::test; use test_log::test;
thread_local! { thread_local! {
static COUNTER: Cell<usize> = const { Cell::new(0) }; static COUNTER: Cell<usize> = const { Cell::new(0) };
} }
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event();
self.push_log(format!("{event:?}"));
}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field1: u32, field1: u32,
@ -52,7 +25,7 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn function(db: &dyn Db, input: MyInput) -> usize { fn function(db: &dyn Database, input: MyInput) -> usize {
// Read input 1 // Read input 1
let _field1 = input.field1(db); let _field1 = input.field1(db);
@ -71,7 +44,7 @@ fn function(db: &dyn Db, input: MyInput) -> usize {
#[test] #[test]
fn test_leaked_inputs_ignored() { fn test_leaked_inputs_ignored() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<EventLogger> = Default::default();
let input = MyInput::new(&db, 10, 20); let input = MyInput::new(&db, 10, 20);
let result_in_rev_1 = function(&db, input); let result_in_rev_1 = function(&db, input);

View file

@ -3,8 +3,6 @@
//! Singleton structs are created only once. Subsequent `get`s and `new`s after creation return the same `Id`. //! Singleton structs are created only once. Subsequent `get`s and `new`s after creation return the same `Id`.
use expect_test::expect; use expect_test::expect;
mod common;
use common::{HasLogger, Logger};
use salsa::Database as _; use salsa::Database as _;
use test_log::test; use test_log::test;
@ -15,25 +13,9 @@ struct MyInput {
id_field: u16, id_field: u16,
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn basic() { fn basic() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 3, 4); let input1 = MyInput::new(&db, 3, 4);
let input2 = MyInput::get(&db); let input2 = MyInput::get(&db);
@ -46,7 +28,7 @@ fn basic() {
#[test] #[test]
#[should_panic] #[should_panic]
fn twice() { fn twice() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 3, 4); let input1 = MyInput::new(&db, 3, 4);
let input2 = MyInput::get(&db); let input2 = MyInput::get(&db);
@ -58,7 +40,7 @@ fn twice() {
#[test] #[test]
fn debug() { fn debug() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 3, 4); let input = MyInput::new(db, 3, 4);
let actual = format!("{:?}", input); let actual = format!("{:?}", input);
let expected = expect!["MyInput { [salsa id]: Id(0), field: 3, id_field: 4 }"]; let expected = expect!["MyInput { [salsa id]: Id(0), field: 3, id_field: 4 }"];

View file

@ -2,9 +2,6 @@
//! compilation succeeds but execution panics //! compilation succeeds but execution panics
#![allow(warnings)] #![allow(warnings)]
#[salsa::db]
trait Db: salsa::Database {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: u32, field: u32,
@ -16,12 +13,15 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn tracked_struct_created_in_another_query<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { fn tracked_struct_created_in_another_query<'db>(
db: &'db dyn salsa::Database,
input: MyInput,
) -> MyTracked<'db> {
MyTracked::new(db, input.field(db) * 2) MyTracked::new(db, input.field(db) * 2)
} }
#[salsa::tracked] #[salsa::tracked]
fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> { fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput) -> MyTracked<'db> {
let t = tracked_struct_created_in_another_query(db, input); let t = tracked_struct_created_in_another_query(db, input);
if input.field(db) != 0 { if input.field(db) != 0 {
tracked_fn_extra::specify(db, t, 2222); tracked_fn_extra::specify(db, t, 2222);
@ -30,28 +30,16 @@ fn tracked_fn<'db>(db: &'db dyn Db, input: MyInput) -> MyTracked<'db> {
} }
#[salsa::tracked(specify)] #[salsa::tracked(specify)]
fn tracked_fn_extra<'db>(_db: &'db dyn Db, _input: MyTracked<'db>) -> u32 { fn tracked_fn_extra<'db>(_db: &'db dyn salsa::Database, _input: MyTracked<'db>) -> u32 {
0 0
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
#[test] #[test]
#[should_panic( #[should_panic(
expected = "can only use `specify` on salsa structs created during the current tracked fn" expected = "can only use `specify` on salsa structs created during the current tracked fn"
)] )]
fn execute_when_specified() { fn execute_when_specified() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input); let tracked = tracked_fn(&db, input);
} }

View file

@ -4,12 +4,9 @@
mod common; mod common;
use common::{HasLogger, Logger}; use common::{ExecuteValidateLogger, LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::{Database as _, Durability, Event, EventKind}; use salsa::{Database, DatabaseImpl, Durability, Event, EventKind};
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
@ -17,47 +14,20 @@ struct MyInput {
} }
#[salsa::tracked] #[salsa::tracked]
fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 { fn tracked_fn(db: &dyn Database, input: MyInput) -> u32 {
input.field(db) * 2 input.field(db) * 2
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: Event) {
if let EventKind::WillExecute { .. } | EventKind::DidValidateMemoizedValue { .. } =
event.kind
{
self.push_log(format!("{:?}", event.kind));
}
}
}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[salsa::db]
impl Db for Database {}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db: DatabaseImpl<ExecuteValidateLogger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 44); assert_eq!(tracked_fn(&db, input), 44);
db.assert_logs(expect![[r#" db.assert_logs(expect![[r#"
[ [
"WillExecute { database_key: tracked_fn(0) }", "salsa_event(WillExecute { database_key: tracked_fn(0) })",
]"#]]); ]"#]]);
// Bumps the revision // Bumps the revision
@ -68,6 +38,6 @@ fn execute() {
db.assert_logs(expect![[r#" db.assert_logs(expect![[r#"
[ [
"DidValidateMemoizedValue { database_key: tracked_fn(0) }", "salsa_event(DidValidateMemoizedValue { database_key: tracked_fn(0) })",
]"#]]); ]"#]]);
} }

View file

@ -1,6 +1,6 @@
//! Test an id field whose `PartialEq` impl is always true. //! Test an id field whose `PartialEq` impl is always true.
use salsa::{Database as Db, Setter}; use salsa::{Database, Setter};
use test_log::test; use test_log::test;
#[salsa::input] #[salsa::input]
@ -33,24 +33,14 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn the_fn(db: &dyn Db, input: MyInput) { fn the_fn(db: &dyn Database, input: MyInput) {
let tracked0 = MyTracked::new(db, BadEq::from(input.field(db))); let tracked0 = MyTracked::new(db, BadEq::from(input.field(db)));
assert_eq!(tracked0.field(db).field, input.field(db)); assert_eq!(tracked0.field(db).field, input.field(db));
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true); let input = MyInput::new(&db, true);
the_fn(&db, input); the_fn(&db, input);
input.set_field(&mut db).to(false); input.set_field(&mut db).to(false);

View file

@ -42,18 +42,9 @@ fn the_fn(db: &dyn Db, input: MyInput) {
assert_eq!(tracked0.field(db).field, input.field(db)); assert_eq!(tracked0.field(db).field, input.field(db));
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true); let input = MyInput::new(&db, true);
the_fn(&db, input); the_fn(&db, input);

View file

@ -16,18 +16,9 @@ fn tracked_fn(db: &dyn Db, input: MyInput) -> MyTracked<'_> {
MyTracked::new(db, input.field(db) / 2) MyTracked::new(db, input.field(db) / 2)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 22); let input1 = MyInput::new(&db, 22);
let input2 = MyInput::new(&db, 44); let input2 = MyInput::new(&db, 44);

View file

@ -3,9 +3,9 @@
//! if we were to execute from scratch. //! if we were to execute from scratch.
use expect_test::expect; use expect_test::expect;
use salsa::{Database as Db, Setter}; use salsa::{Database, Setter};
mod common; mod common;
use common::{HasLogger, Logger}; use common::LogDatabase;
use test_log::test; use test_log::test;
#[salsa::input] #[salsa::input]
@ -37,51 +37,24 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn the_fn(db: &dyn Db, input: MyInput) -> bool { fn the_fn(db: &dyn Database, input: MyInput) -> bool {
let tracked = make_tracked_struct(db, input); let tracked = make_tracked_struct(db, input);
read_tracked_struct(db, tracked) read_tracked_struct(db, tracked)
} }
#[salsa::tracked] #[salsa::tracked]
fn make_tracked_struct(db: &dyn Db, input: MyInput) -> MyTracked<'_> { fn make_tracked_struct(db: &dyn Database, input: MyInput) -> MyTracked<'_> {
MyTracked::new(db, BadEq::from(input.field(db))) MyTracked::new(db, BadEq::from(input.field(db)))
} }
#[salsa::tracked] #[salsa::tracked]
fn read_tracked_struct<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> bool { fn read_tracked_struct<'db>(db: &'db dyn Database, tracked: MyTracked<'db>) -> bool {
tracked.field(db).field tracked.field(db).field
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillExecute { .. }
| salsa::EventKind::DidValidateMemoizedValue { .. } => {
self.push_log(format!("salsa_event({:?})", event.kind));
}
_ => {}
}
}
}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<common::ExecuteValidateLogger> = Default::default();
let input = MyInput::new(&db, true); let input = MyInput::new(&db, true);
let result = the_fn(&db, input); let result = the_fn(&db, input);

View file

@ -2,7 +2,7 @@
//! This can our "last changed" data to be wrong //! This can our "last changed" data to be wrong
//! but we *should* always reflect the final values. //! but we *should* always reflect the final values.
use salsa::{Database as Db, Setter}; use salsa::{Database, Setter};
use test_log::test; use test_log::test;
#[salsa::input] #[salsa::input]
@ -28,23 +28,14 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn the_fn(db: &dyn Db, input: MyInput) { fn the_fn(db: &dyn Database, input: MyInput) {
let tracked0 = MyTracked::new(db, NotEq::from(input.field(db))); let tracked0 = MyTracked::new(db, NotEq::from(input.field(db)));
assert_eq!(tracked0.field(db).field, input.field(db)); assert_eq!(tracked0.field(db).field, input.field(db));
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true); let input = MyInput::new(&db, true);
the_fn(&db, input); the_fn(&db, input);

View file

@ -9,15 +9,6 @@ fn tracked_fn(db: &dyn salsa::Database) -> u32 {
#[test] #[test]
fn execute() { fn execute() {
#[salsa::db] let mut db = salsa::DatabaseImpl::new();
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
let mut db = Database::default();
assert_eq!(tracked_fn(&db), 44); assert_eq!(tracked_fn(&db), 44);
} }

View file

@ -1,11 +1,8 @@
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use expect_test::expect; use expect_test::expect;
use salsa::Setter as _; use salsa::{DatabaseImpl, Setter as _};
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct Input { struct Input {
@ -13,7 +10,7 @@ struct Input {
} }
#[salsa::tracked(no_eq)] #[salsa::tracked(no_eq)]
fn abs_float(db: &dyn Db, input: Input) -> f32 { fn abs_float(db: &dyn LogDatabase, input: Input) -> f32 {
let number = input.number(db); let number = input.number(db);
db.push_log(format!("abs_float({number})")); db.push_log(format!("abs_float({number})"));
@ -21,35 +18,15 @@ fn abs_float(db: &dyn Db, input: Input) -> f32 {
} }
#[salsa::tracked] #[salsa::tracked]
fn derived(db: &dyn Db, input: Input) -> u32 { fn derived(db: &dyn LogDatabase, input: Input) -> u32 {
let x = abs_float(db, input); let x = abs_float(db, input);
db.push_log("derived".to_string()); db.push_log("derived".to_string());
x as u32 x as u32
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
#[test] #[test]
fn invoke() { fn invoke() {
let mut db = Database::default(); let mut db: DatabaseImpl<Logger> = Default::default();
let input = Input::new(&db, 5); let input = Input::new(&db, 5);
let x = derived(&db, input); let x = derived(&db, input);

View file

@ -14,16 +14,7 @@ fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> u32 {
#[test] #[test]
fn execute() { fn execute() {
#[salsa::db] let mut db = salsa::DatabaseImpl::new();
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
let mut db = Database::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 44); assert_eq!(tracked_fn(&db, input), 44);
} }

View file

@ -11,18 +11,9 @@ fn tracked_fn<'db>(db: &'db dyn salsa::Database, name: Name<'db>) -> String {
name.name(db).clone() name.name(db).clone()
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let name = Name::new(&db, "Salsa".to_string()); let name = Name::new(&db, "Salsa".to_string());
assert_eq!(tracked_fn(&db, name), "Salsa"); assert_eq!(tracked_fn(&db, name), "Salsa");

View file

@ -16,18 +16,9 @@ fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> MyTracked<'_> {
MyTracked::new(db, input.field(db) * 2) MyTracked::new(db, input.field(db) * 2)
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute() { fn execute() {
let db = Database::default(); let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input).field(&db), 44); assert_eq!(tracked_fn(&db, input).field(&db), 44);
} }

View file

@ -26,18 +26,9 @@ fn tracked_fn_extra<'db>(_db: &'db dyn salsa::Database, _input: MyTracked<'db>)
0 0
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn execute_when_specified() { fn execute_when_specified() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input); let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 44); assert_eq!(tracked.field(&db), 44);
@ -46,7 +37,7 @@ fn execute_when_specified() {
#[test] #[test]
fn execute_when_not_specified() { fn execute_when_not_specified() {
let mut db = Database::default(); let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 0); let input = MyInput::new(&db, 0);
let tracked = tracked_fn(&db, input); let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 0); assert_eq!(tracked.field(&db), 0);

View file

@ -3,20 +3,17 @@
use expect_test::expect; use expect_test::expect;
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use salsa::Setter; use salsa::Setter;
use test_log::test; use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: u32, field: u32,
} }
#[salsa::tracked] #[salsa::tracked]
fn final_result(db: &dyn Db, input: MyInput) -> u32 { fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("final_result({:?})", input)); db.push_log(format!("final_result({:?})", input));
intermediate_result(db, input).field(db) * 2 intermediate_result(db, input).field(db) * 2
} }
@ -27,35 +24,16 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn intermediate_result(db: &dyn Db, input: MyInput) -> MyTracked<'_> { fn intermediate_result(db: &dyn LogDatabase, input: MyInput) -> MyTracked<'_> {
db.push_log(format!("intermediate_result({:?})", input)); db.push_log(format!("intermediate_result({:?})", input));
let tracked = MyTracked::new(db, input.field(db) / 2); let tracked = MyTracked::new(db, input.field(db) / 2);
let _ = tracked.field(db); // read the field of an entity we created let _ = tracked.field(db); // read the field of an entity we created
tracked tracked
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn one_entity() { fn one_entity() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22); assert_eq!(final_result(&db, input), 22);
@ -86,7 +64,7 @@ fn one_entity() {
/// Create and mutate a distinct input. No re-execution required. /// Create and mutate a distinct input. No re-execution required.
#[test] #[test]
fn red_herring() { fn red_herring() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22); assert_eq!(final_result(&db, input), 22);

View file

@ -1,10 +1,7 @@
use expect_test::expect; use expect_test::expect;
use salsa::Database as SalsaDatabase;
mod common; mod common;
use common::{HasLogger, Logger}; use common::{LogDatabase, Logger};
use salsa::Database;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
@ -17,7 +14,7 @@ struct MyTracked<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 { fn tracked_fn(db: &dyn LogDatabase, input: MyInput) -> u32 {
db.push_log(format!("tracked_fn({input:?})")); db.push_log(format!("tracked_fn({input:?})"));
let t = MyTracked::new(db, input.field(db) * 2); let t = MyTracked::new(db, input.field(db) * 2);
tracked_fn_extra::specify(db, t, 2222); tracked_fn_extra::specify(db, t, 2222);
@ -25,33 +22,14 @@ fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 {
} }
#[salsa::tracked(specify)] #[salsa::tracked(specify)]
fn tracked_fn_extra<'db>(db: &dyn Db, input: MyTracked<'db>) -> u32 { fn tracked_fn_extra<'db>(db: &dyn LogDatabase, input: MyTracked<'db>) -> u32 {
db.push_log(format!("tracked_fn_extra({input:?})")); db.push_log(format!("tracked_fn_extra({input:?})"));
0 0
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
#[test] #[test]
fn execute() { fn execute() {
let mut db = Database::default(); let mut db: salsa::DatabaseImpl<Logger> = salsa::DatabaseImpl::default();
let input = MyInput::new(&db, 22); let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 2222); assert_eq!(tracked_fn(&db, input), 2222);
db.assert_logs(expect![[r#" db.assert_logs(expect![[r#"

View file

@ -1,4 +1,4 @@
use salsa::Database as _; use salsa::Database;
#[salsa::input] #[salsa::input]
struct Input { struct Input {
@ -12,18 +12,9 @@ fn test(db: &dyn salsa::Database, input: Input) -> Vec<String> {
.collect() .collect()
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn invoke() { fn invoke() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, 3); let input = Input::new(db, 3);
let x: &Vec<String> = test(db, input); let x: &Vec<String> = test(db, input);
expect_test::expect![[r#" expect_test::expect![[r#"

View file

@ -34,16 +34,7 @@ impl TrackedTrait for MyInput {
#[test] #[test]
fn execute() { fn execute() {
#[salsa::db] let mut db = salsa::DatabaseImpl::new();
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
let mut db = Database::default();
let object = MyInput::new(&mut db, 22); let object = MyInput::new(&mut db, 22);
// assert_eq!(object.tracked_fn(&db), 44); // assert_eq!(object.tracked_fn(&db), 44);
// assert_eq!(*object.tracked_fn_ref(&db), 66); // assert_eq!(*object.tracked_fn_ref(&db), 66);

View file

@ -1,4 +1,4 @@
use salsa::Database as _; use salsa::Database;
#[salsa::input] #[salsa::input]
struct Input { struct Input {
@ -15,18 +15,9 @@ impl Input {
} }
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn invoke() { fn invoke() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, 3); let input = Input::new(db, 3);
let x: &Vec<String> = input.test(db); let x: &Vec<String> = input.test(db);
expect_test::expect![[r#" expect_test::expect![[r#"

View file

@ -43,7 +43,7 @@ impl<'db1> ItemName<'db1> for SourceTree<'db1> {
#[test] #[test]
fn test_inherent() { fn test_inherent() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, "foo".to_string()); let input = Input::new(db, "foo".to_string());
let source_tree = input.source_tree(db); let source_tree = input.source_tree(db);
expect_test::expect![[r#" expect_test::expect![[r#"
@ -55,7 +55,7 @@ fn test_inherent() {
#[test] #[test]
fn test_trait() { fn test_trait() {
salsa::default_database().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, "foo".to_string()); let input = Input::new(db, "foo".to_string());
let source_tree = input.source_tree(db); let source_tree = input.source_tree(db);
expect_test::expect![[r#" expect_test::expect![[r#"

View file

@ -1,4 +1,4 @@
use salsa::Database as _; use salsa::Database;
#[salsa::input] #[salsa::input]
struct Input { struct Input {
@ -19,18 +19,9 @@ impl Trait for Input {
} }
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn invoke() { fn invoke() {
Database::default().attach(|db| { salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, 3); let input = Input::new(db, 3);
let x: &Vec<String> = input.test(db); let x: &Vec<String> = input.test(db);
expect_test::expect![[r#" expect_test::expect![[r#"

View file

@ -20,14 +20,5 @@ struct MyTracked2<'db2> {
field: u32, field: u32,
} }
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test] #[test]
fn create_db() {} fn create_db() {}

View file

@ -3,15 +3,6 @@
use test_log::test; use test_log::test;
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: String, field: String,

View file

@ -1,18 +1,9 @@
//! Test that a setting a field on a `#[salsa::input]` //! Test that a setting a field on a `#[salsa::input]`
//! overwrites and returns the old value. //! overwrites and returns the old value.
use salsa::Database as _; use salsa::{Database, DatabaseImpl};
use test_log::test; use test_log::test;
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::input] #[salsa::input]
struct MyInput { struct MyInput {
field: String, field: String,
@ -31,7 +22,7 @@ enum MyList<'db> {
} }
#[salsa::tracked] #[salsa::tracked]
fn create_tracked_list(db: &dyn salsa::Database, input: MyInput) -> MyTracked<'_> { fn create_tracked_list(db: &dyn Database, input: MyInput) -> MyTracked<'_> {
let t0 = MyTracked::new(db, input, MyList::None); let t0 = MyTracked::new(db, input, MyList::None);
let t1 = MyTracked::new(db, input, MyList::Next(t0)); let t1 = MyTracked::new(db, input, MyList::Next(t0));
t1 t1
@ -39,7 +30,7 @@ fn create_tracked_list(db: &dyn salsa::Database, input: MyInput) -> MyTracked<'_
#[test] #[test]
fn execute() { fn execute() {
Database::default().attach(|db| { DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, "foo".to_string()); let input = MyInput::new(db, "foo".to_string());
let t0: MyTracked = create_tracked_list(db, input); let t0: MyTracked = create_tracked_list(db, input);
let t1 = create_tracked_list(db, input); let t1 = create_tracked_list(db, input);