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| {
b.iter_batched_ref(
|| {
let db = salsa::default_database();
let db = salsa::DatabaseImpl::new();
let input = Input::new(&db, 1_000);
let input2 = Input::new(&db, 1);

View file

@ -32,13 +32,6 @@ struct DbMacro {
impl DbMacro {
fn try_db(self, input: syn::Item) -> syn::Result<TokenStream> {
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) => {
self.add_salsa_view_method(&mut input)?;
Ok(quote! {
@ -53,54 +46,11 @@ impl DbMacro {
}
_ => Err(syn::Error::new_spanned(
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<()> {
input.items.push(parse_quote! {
#[doc(hidden)]

View file

@ -1,49 +1,48 @@
use std::sync::{Arc, Mutex};
use salsa::UserData;
pub type CalcDatabaseImpl = salsa::DatabaseImpl<Calc>;
// ANCHOR: db_struct
#[derive(Default)]
#[salsa::db]
pub(crate) struct Database {
storage: salsa::Storage<Self>,
pub struct Calc {
// The logs are only used for testing and demonstrating reuse:
//
logs: Option<Arc<Mutex<Vec<String>>>>,
logs: Arc<Mutex<Option<Vec<String>>>>,
}
// ANCHOR_END: db_struct
impl Database {
impl Calc {
/// Enable logging of each salsa event.
#[cfg(test)]
pub fn enable_logging(self) -> Self {
assert!(self.logs.is_none());
Self {
storage: self.storage,
logs: Some(Default::default()),
pub fn enable_logging(&self) {
let mut logs = self.logs.lock().unwrap();
if logs.is_none() {
*logs = Some(vec![]);
}
}
#[cfg(test)]
pub fn take_logs(&mut self) -> Vec<String> {
if let Some(logs) = &self.logs {
std::mem::take(&mut *logs.lock().unwrap())
pub fn take_logs(&self) -> Vec<String> {
let mut logs = self.logs.lock().unwrap();
if let Some(logs) = &mut *logs {
std::mem::take(logs)
} else {
panic!("logs not enabled");
vec![]
}
}
}
// ANCHOR: db_impl
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
impl UserData for Calc {
fn salsa_event(db: &CalcDatabaseImpl, event: &dyn Fn() -> salsa::Event) {
let event = event();
eprintln!("Event: {event:?}");
// Log interesting events, if logging is enabled
if let Some(logs) = &self.logs {
// don't log boring events
if let Some(logs) = &mut *db.logs.lock().unwrap() {
// only log interesting events
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 salsa::Database as Db;
@ -8,7 +9,7 @@ mod parser;
mod type_check;
pub fn main() {
let db = db::Database::default();
let db: CalcDatabaseImpl = Default::default();
let source_program = SourceProgram::new(&db, String::new());
compile::compile(&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.
#[cfg(test)]
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
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 salsa::Accumulator;
#[cfg(test)]
use salsa::Database as _;
#[cfg(test)]
use test_log::test;
// ANCHOR: parse_statements
@ -100,12 +98,13 @@ fn check_string(
expected_diagnostics: 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
let mut db = Database::default().enable_logging();
let mut db = CalcDatabaseImpl::default();
db.enable_logging();
// Create the source program
let source_program = SourceProgram::new(&db, source_text.to_string());

View file

@ -8,13 +8,13 @@ use notify_debouncer_mini::{
notify::{RecommendedWatcher, RecursiveMode},
DebounceEventResult, Debouncer,
};
use salsa::{Accumulator, Setter};
use salsa::{Accumulator, DatabaseImpl, Setter, UserData};
// ANCHOR: main
fn main() -> Result<()> {
// Create the channel to receive file change events.
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()
.nth(1)
@ -74,19 +74,15 @@ trait Db: salsa::Database {
fn input(&self, path: PathBuf) -> Result<File>;
}
#[salsa::db]
struct Database {
storage: salsa::Storage<Self>,
struct LazyInput {
logs: Mutex<Vec<String>>,
files: DashMap<PathBuf, File>,
file_watcher: Mutex<Debouncer<RecommendedWatcher>>,
}
impl Database {
impl LazyInput {
fn new(tx: Sender<DebounceEventResult>) -> Self {
let storage = Default::default();
Self {
storage,
logs: Default::default(),
files: DashMap::new(),
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]
impl Db for Database {
impl Db for DatabaseImpl<LazyInput> {
fn input(&self, path: PathBuf) -> Result<File> {
let path = path
.canonicalize()
@ -122,17 +128,6 @@ impl Db for Database {
}
// 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]
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]
pub trait Database: ZalsaDatabase + AsDynDatabase {
/// This function is invoked at key points in the salsa
/// runtime. It permits the database to be customized and to
/// inject logging or other custom behavior.
///
/// By default, the event is logged at level debug using
/// the standard `log` facade.
pub unsafe trait Database: AsDynDatabase + Any {
/// This function is invoked by the salsa runtime at various points during execution.
/// You can customize what happens by implementing the [`UserData`][] trait.
/// By default, the event is logged at level debug using tracing facade.
///
/// # Parameters
///
/// * `event`, a fn that, if called, will create the event that occurred
fn salsa_event(&self, event: &dyn Fn() -> Event) {
tracing::debug!("salsa_event: {:?}", event())
}
fn salsa_event(&self, event: &dyn Fn() -> Event);
/// A "synthetic write" causes the system to act *as though* some
/// 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))
}
/// Plumbing methods.
#[doc(hidden)]
fn zalsa(&self) -> &dyn Zalsa;
#[doc(hidden)]
fn zalsa_mut(&mut self) -> &mut dyn Zalsa;
}
/// Upcast to a `dyn Database`.
@ -83,3 +94,79 @@ impl dyn Database {
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::{
accumulator, hash::FxHashSet, local_state, storage::ZalsaDatabase as _, DatabaseKeyIndex, Id,
};
use crate::{accumulator, hash::FxHashSet, local_state, Database, DatabaseKeyIndex, Id};
use super::{Configuration, IngredientImpl};

View file

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

View file

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

View file

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

View file

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

View file

@ -31,6 +31,8 @@ pub use self::cancelled::Cancelled;
pub use self::cycle::Cycle;
pub use self::database::AsDynDatabase;
pub use self::database::Database;
pub use self::database::DatabaseImpl;
pub use self::database::UserData;
pub use self::durability::Durability;
pub use self::event::Event;
pub use self::event::EventKind;
@ -50,23 +52,9 @@ pub use salsa_macros::interned;
pub use salsa_macros::tracked;
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 use crate::Accumulator;
pub use crate::Database;
pub use crate::Setter;
}
@ -82,6 +70,7 @@ pub mod plumbing {
pub use crate::cycle::CycleRecoveryStrategy;
pub use crate::database::current_revision;
pub use crate::database::Database;
pub use crate::database::UserData;
pub use crate::function::should_backdate_value;
pub use crate::id::AsId;
pub use crate::id::FromId;
@ -102,7 +91,6 @@ pub mod plumbing {
pub use crate::storage::IngredientIndex;
pub use crate::storage::Storage;
pub use crate::storage::Zalsa;
pub use crate::storage::ZalsaDatabase;
pub use crate::tracked_struct::TrackedStructInDb;
pub use crate::update::always_update;
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 parking_lot::Mutex;
use rustc_hash::FxHashMap;
use crate::cycle::CycleRecoveryStrategy;
use crate::database::{DatabaseImpl, UserData};
use crate::ingredient::{Ingredient, Jar};
use crate::nonce::{Nonce, NonceGenerator};
use crate::runtime::Runtime;
@ -15,22 +16,6 @@ pub fn views<Db: ?Sized + Database>(db: &Db) -> &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.
///
/// **NOT SEMVER STABLE.**
@ -87,9 +72,9 @@ pub trait Zalsa {
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 {
&self.upcasts
&self.views_of
}
fn nonce(&self) -> Nonce<StorageNonce> {
@ -227,8 +212,10 @@ impl IngredientIndex {
/// The "storage" struct stores all the data for the jars.
/// It is shared between the main database and any active snapshots.
pub struct Storage<Db: Database> {
upcasts: ViewsOf<Db>,
pub struct Storage<U: UserData> {
user_data: U,
views_of: ViewsOf<DatabaseImpl<U>>,
nonce: Nonce<StorageNonce>,
@ -254,19 +241,30 @@ pub struct Storage<Db: Database> {
}
// ANCHOR: default
impl<Db: Database> Default for Storage<Db> {
impl<U: UserData + Default> Default for Storage<U> {
fn default() -> Self {
Self::with(Default::default())
}
}
// ANCHOR_END: default
impl<U: UserData> Storage<U> {
pub(crate) fn with(user_data: U) -> Self {
Self {
upcasts: Default::default(),
views_of: Default::default(),
nonce: NONCE.nonce(),
jar_map: Default::default(),
ingredients_vec: Default::default(),
ingredients_requiring_reset: Default::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.
/// Optimized for the case of a single database.

View file

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

View file

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

View file

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

View file

@ -39,7 +39,7 @@ fn push_b_logs(db: &dyn Database, input: MyInput) {
#[test]
fn accumulate_a_called_twice() {
salsa::default_database().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 2, 3);
let logs = push_logs::accumulated::<Log>(db, 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]
fn accumulate_execution_order() {
salsa::default_database().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let logs = push_logs::accumulated::<Log>(db);
// Check that we get logs in execution order
expect![[r#"

View file

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

View file

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

View file

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

View file

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

View file

@ -1,13 +1,10 @@
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use expect_test::expect;
use salsa::{Accumulator, Setter};
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
field_a: u32,
@ -18,7 +15,7 @@ struct MyInput {
struct Log(#[allow(dead_code)] String);
#[salsa::tracked]
fn push_logs(db: &dyn Db, input: MyInput) {
fn push_logs(db: &dyn LogDatabase, input: MyInput) {
db.push_log(format!(
"push_logs(a = {}, b = {})",
input.field_a(db),
@ -37,7 +34,7 @@ fn push_logs(db: &dyn Db, input: MyInput) {
}
#[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);
db.push_log(format!("push_a_logs({})", field_a));
@ -47,7 +44,7 @@ fn push_a_logs(db: &dyn Db, input: MyInput) {
}
#[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);
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]
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.
let input = MyInput::new(&db, 2, 3);
@ -115,7 +91,7 @@ fn accumulate_once() {
#[test]
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`
let input = MyInput::new(&db, 2, 3);
@ -170,7 +146,7 @@ fn change_a_from_2_to_0() {
#[test]
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`
let input = MyInput::new(&db, 2, 3);
@ -229,7 +205,7 @@ fn change_a_from_2_to_1() {
#[test]
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)
let input = MyInput::new(&db, 2, 3);

View file

@ -2,16 +2,21 @@
#![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)]
pub struct Logger {
logs: std::sync::Mutex<Vec<String>>,
}
/// 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 UserData for Logger {}
#[salsa::db]
pub trait LogDatabase: HasLogger + salsa::Database {
/// Log an event from inside a tracked function.
fn push_log(&self, string: String) {
self.logger().logs.lock().unwrap().push(string);
@ -33,3 +38,86 @@ pub trait HasLogger {
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() {
let mut db = salsa::default_database();
let mut db = salsa::DatabaseImpl::new();
let input = a::MyInput::new(&mut db, 22);
input.field(&db);

View file

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

View file

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

View file

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

View file

@ -24,7 +24,7 @@ help: consider borrowing here
warning: variable does not need to be mutable
--> 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`

View file

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

View file

@ -1,7 +1,7 @@
//! Test that `DeriveWithDb` is correctly derived.
use expect_test::expect;
use salsa::{Database as _, Setter};
use salsa::{Database, Setter};
#[salsa::input]
struct MyInput {
@ -19,18 +19,9 @@ struct ComplexStruct {
not_salsa: NotSalsa,
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn input() {
Database::default().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 22);
let not_salsa = NotSalsa {
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.
#[test]
fn untracked_dependencies() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);
@ -95,7 +86,7 @@ fn leak_derived_custom(db: &dyn salsa::Database, input: MyInput, value: u32) ->
#[test]
fn custom_debug_impl() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);

View file

@ -3,22 +3,19 @@
//! * when we delete memoized data, also delete outputs from that data
mod common;
use common::{HasLogger, Logger};
use common::{DiscardLogger, LogDatabase};
use expect_test::expect;
use salsa::Setter;
use salsa::{DatabaseImpl, Setter};
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input(singleton)]
struct MyInput {
field: u32,
}
#[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));
let mut sum = 0;
for tracked_struct in create_tracked_structs(db, input) {
@ -33,7 +30,7 @@ struct MyTracked<'db> {
}
#[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));
(0..input.field(db))
.map(|i| MyTracked::new(db, i))
@ -41,49 +38,19 @@ fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> {
}
#[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));
copy_field(db, m) * 2
}
#[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)
}
#[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]
fn basic() {
let mut db = Database::default();
let mut db: DatabaseImpl<DiscardLogger> = Default::default();
// Creates 3 tracked structs
let input = MyInput::new(&db, 3);

View file

@ -56,7 +56,7 @@ impl MyInput {
#[test]
fn deletion_drops() {
let mut db = salsa::default_database();
let mut db = salsa::DatabaseImpl::new();
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.
mod common;
use common::{HasLogger, Logger};
use common::LogDatabase;
use expect_test::expect;
use salsa::Setter;
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
field: u32,
}
#[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));
let mut sum = 0;
for tracked_struct in create_tracked_structs(db, input) {
@ -33,7 +30,7 @@ struct MyTracked<'db> {
}
#[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));
(0..input.field(db))
.map(|i| MyTracked::new(db, i))
@ -41,43 +38,13 @@ fn create_tracked_structs(db: &dyn Db, input: MyInput) -> Vec<MyTracked<'_>> {
}
#[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
}
#[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]
fn basic() {
let mut db = Database::default();
let mut db: salsa::DatabaseImpl<common::DiscardLogger> = Default::default();
// Creates 3 tracked structs
let input = MyInput::new(&db, 3);

View file

@ -2,22 +2,19 @@
//! compiles and executes successfully.
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use expect_test::expect;
use salsa::Setter;
use salsa::{DatabaseImpl, Setter};
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
field: u32,
}
#[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));
intermediate_result(db, input).field(db) * 2
}
@ -28,33 +25,14 @@ struct MyTracked<'db> {
}
#[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));
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]
fn execute() {
let mut db = Database::default();
let mut db: DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);

View file

@ -4,13 +4,10 @@
#![allow(dead_code)]
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use expect_test::expect;
use salsa::Setter;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
use salsa::{DatabaseImpl, Setter};
#[salsa::input]
struct MyInput {
@ -18,13 +15,13 @@ struct MyInput {
}
#[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));
intermediate_result(db, input).x(db) * 2
}
#[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));
intermediate_result(db, input).y(db) * 2
}
@ -36,36 +33,17 @@ struct MyTracked<'db> {
}
#[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)
}
#[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]
fn execute() {
// x = (input.field + 1) / 2
// y = input.field / 2
// final_result_depends_on_x = x * 2 = (input.field + 1) / 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:
// x = (22 + 1) / 2 = 11

View file

@ -4,14 +4,11 @@
#![allow(dead_code)]
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use expect_test::expect;
use salsa::Setter;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
x: u32,
@ -19,41 +16,21 @@ struct MyInput {
}
#[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));
input.x(db) + 1
}
#[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));
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]
fn execute() {
// result_depends_on_x = x + 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);
assert_eq!(result_depends_on_x(&db, input), 23);

View file

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

View file

@ -1,14 +1,9 @@
//! Test that a `tracked` fn on a `salsa::input`
//! compiles and executes successfully.
mod common;
use common::{HasLogger, Logger};
use expect_test::expect;
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::interned]
struct InternedString<'db> {
data: String,
@ -20,38 +15,17 @@ struct InternedPair<'db> {
}
#[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 s2 = InternedString::new(db, "World, ".to_string());
let s3 = InternedPair::new(db, (s1, s2));
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]
fn execute() {
let mut db = Database::default();
let db = salsa::DatabaseImpl::new();
expect![[r#"
"InternedPair { data: (InternedString { data: \"Hello, \" }, InternedString { data: \"World, \" }) }"
"#]].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]`
//! overwrites and returns the old value.
use salsa::Database;
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]
struct MyInput {
field: String,
@ -34,7 +20,7 @@ struct MyInterned<'db> {
}
#[salsa::tracked]
fn test(db: &dyn crate::Db, input: MyInput) {
fn test(db: &dyn Database, input: MyInput) {
let input = is_send_sync(input);
let interned = is_send_sync(MyInterned::new(db, input.field(db).clone()));
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]
fn execute() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, "Hello".to_string());
test(&db, input);
}

View file

@ -6,14 +6,11 @@ use std::sync::{
Arc,
};
use salsa::Database as _;
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use salsa::Database as _;
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[derive(Debug, PartialEq, Eq)]
struct HotPotato(u32);
@ -40,50 +37,31 @@ struct MyInput {
}
#[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)));
Arc::new(HotPotato::new(input.field(db)))
}
#[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)));
get_hot_potato(db, input).0
}
#[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);
db.report_untracked_read();
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 {
N_POTATOES.with(|n| n.load(Ordering::SeqCst))
}
#[test]
fn lru_works() {
let db = DatabaseImpl::default();
let db: salsa::DatabaseImpl<Logger> = Default::default();
assert_eq!(load_n_potatoes(), 0);
for i in 0..128u32 {
@ -99,7 +77,7 @@ fn lru_works() {
#[test]
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`
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]
fn lru_can_be_changed_at_runtime() {
let db = DatabaseImpl::default();
let db: salsa::DatabaseImpl<Logger> = Default::default();
assert_eq!(load_n_potatoes(), 0);
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]
fn lru_keeps_dependency_info() {
let mut db = DatabaseImpl::default();
let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let capacity = 32;
// 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]`
//! overwrites and returns the old value.
mod common;
use common::{HasLogger, Logger};
use salsa::Setter;
use test_log::test;
@ -12,25 +9,9 @@ struct MyInput {
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]
fn execute() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, "Hello".to_string());

View file

@ -66,17 +66,5 @@ impl<'db> MyTracked<'db> {
#[test]
fn execute() {
#[salsa::db]
#[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();
salsa::DatabaseImpl::new();
}

View file

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

View file

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

View file

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

View file

@ -2,16 +2,9 @@
//! See `../cycles.rs` for a complete listing of cycle tests,
//! both intra and cross thread.
use salsa::Handle;
use salsa::{DatabaseImpl, Handle};
use crate::setup::Database;
use crate::setup::Knobs;
#[salsa::db]
pub(crate) trait Db: salsa::Database + Knobs {}
#[salsa::db]
impl<T: salsa::Database + Knobs> Db for T {}
use crate::setup::{Knobs, KnobsDatabase};
#[salsa::input]
pub(crate) struct MyInput {
@ -19,7 +12,7 @@ pub(crate) struct MyInput {
}
#[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
db.signal(1);
@ -30,25 +23,25 @@ pub(crate) fn a1(db: &dyn Db, input: MyInput) -> i32 {
}
#[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
b1(db, input)
}
#[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
db.wait_for(1);
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");
key.field(db) * 20 + 2
}
#[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
b3(db, input);
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)]
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
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");
key.field(db) * 200 + 2
}
@ -88,7 +81,7 @@ fn recover_b3(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
#[test]
fn execute() {
let db = Handle::new(Database::default());
let db = Handle::new(<DatabaseImpl<Knobs>>::default());
db.knobs().signal_on_will_block.store(3);
let input = MyInput::new(&*db, 1);

View file

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

View file

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

View file

@ -1,11 +1,13 @@
use crossbeam::atomic::AtomicCell;
use salsa::{Database, DatabaseImpl, UserData};
use crate::signal::Signal;
/// Various "knobs" and utilities used by tests to force
/// a certain behavior.
pub(crate) trait Knobs {
fn knobs(&self) -> &KnobsStruct;
#[salsa::db]
pub(crate) trait KnobsDatabase: Database {
fn knobs(&self) -> &Knobs;
fn signal(&self, stage: usize);
@ -16,7 +18,7 @@ pub(crate) trait Knobs {
/// behave on one specific thread. Note that this state is
/// intentionally thread-local (apart from `signal`).
#[derive(Default)]
pub(crate) struct KnobsStruct {
pub(crate) struct Knobs {
/// A kind of flexible barrier used to coordinate execution across
/// threads to ensure we reach various weird states.
pub(crate) signal: Signal,
@ -28,39 +30,32 @@ pub(crate) struct KnobsStruct {
pub(crate) signal_on_did_cancel: AtomicCell<usize>,
}
#[salsa::db]
#[derive(Default)]
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) {
impl UserData for Knobs {
fn salsa_event(db: &DatabaseImpl<Self>, event: &dyn Fn() -> salsa::Event) {
let event = event();
match event.kind {
salsa::EventKind::WillBlockOn { .. } => {
self.signal(self.knobs().signal_on_will_block.load());
db.signal(db.signal_on_will_block.load());
}
salsa::EventKind::DidSetCancellationFlag => {
self.signal(self.knobs().signal_on_did_cancel.load());
db.signal(db.signal_on_did_cancel.load());
}
_ => {}
}
}
}
impl Knobs for Database {
fn knobs(&self) -> &KnobsStruct {
&self.knobs
#[salsa::db]
impl KnobsDatabase for DatabaseImpl<Knobs> {
fn knobs(&self) -> &Knobs {
self
}
fn signal(&self, stage: usize) {
self.knobs.signal.signal(stage);
self.signal.signal(stage);
}
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;
mod common;
use common::{HasLogger, Logger};
use salsa::Setter;
use common::{EventLogger, LogDatabase};
use salsa::{Database, Setter};
use test_log::test;
thread_local! {
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]
struct MyInput {
field1: u32,
@ -52,7 +25,7 @@ struct MyTracked<'db> {
}
#[salsa::tracked]
fn function(db: &dyn Db, input: MyInput) -> usize {
fn function(db: &dyn Database, input: MyInput) -> usize {
// Read input 1
let _field1 = input.field1(db);
@ -71,7 +44,7 @@ fn function(db: &dyn Db, input: MyInput) -> usize {
#[test]
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 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`.
use expect_test::expect;
mod common;
use common::{HasLogger, Logger};
use salsa::Database as _;
use test_log::test;
@ -15,25 +13,9 @@ struct MyInput {
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]
fn basic() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 3, 4);
let input2 = MyInput::get(&db);
@ -46,7 +28,7 @@ fn basic() {
#[test]
#[should_panic]
fn twice() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 3, 4);
let input2 = MyInput::get(&db);
@ -58,7 +40,7 @@ fn twice() {
#[test]
fn debug() {
Database::default().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let input = MyInput::new(db, 3, 4);
let actual = format!("{:?}", input);
let expected = expect!["MyInput { [salsa id]: Id(0), field: 3, id_field: 4 }"];

View file

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

View file

@ -4,12 +4,9 @@
mod common;
use common::{HasLogger, Logger};
use common::{ExecuteValidateLogger, LogDatabase, Logger};
use expect_test::expect;
use salsa::{Database as _, Durability, Event, EventKind};
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
use salsa::{Database, DatabaseImpl, Durability, Event, EventKind};
#[salsa::input]
struct MyInput {
@ -17,47 +14,20 @@ struct MyInput {
}
#[salsa::tracked]
fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 {
fn tracked_fn(db: &dyn Database, input: MyInput) -> u32 {
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]
fn execute() {
let mut db = Database::default();
let mut db: DatabaseImpl<ExecuteValidateLogger> = Default::default();
let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 44);
db.assert_logs(expect![[r#"
[
"WillExecute { database_key: tracked_fn(0) }",
"salsa_event(WillExecute { database_key: tracked_fn(0) })",
]"#]]);
// Bumps the revision
@ -68,6 +38,6 @@ fn execute() {
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.
use salsa::{Database as Db, Setter};
use salsa::{Database, Setter};
use test_log::test;
#[salsa::input]
@ -33,24 +33,14 @@ struct MyTracked<'db> {
}
#[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)));
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]
fn execute() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true);
the_fn(&db, input);
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));
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn execute() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true);
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)
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn execute() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input1 = MyInput::new(&db, 22);
let input2 = MyInput::new(&db, 44);

View file

@ -3,9 +3,9 @@
//! if we were to execute from scratch.
use expect_test::expect;
use salsa::{Database as Db, Setter};
use salsa::{Database, Setter};
mod common;
use common::{HasLogger, Logger};
use common::LogDatabase;
use test_log::test;
#[salsa::input]
@ -37,51 +37,24 @@ struct MyTracked<'db> {
}
#[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);
read_tracked_struct(db, 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)))
}
#[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
}
#[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]
fn execute() {
let mut db = Database::default();
let mut db: salsa::DatabaseImpl<common::ExecuteValidateLogger> = Default::default();
let input = MyInput::new(&db, true);
let result = the_fn(&db, input);

View file

@ -2,7 +2,7 @@
//! This can our "last changed" data to be wrong
//! but we *should* always reflect the final values.
use salsa::{Database as Db, Setter};
use salsa::{Database, Setter};
use test_log::test;
#[salsa::input]
@ -28,23 +28,14 @@ struct MyTracked<'db> {
}
#[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)));
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]
fn execute() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, true);
the_fn(&db, input);

View file

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

View file

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

View file

@ -14,16 +14,7 @@ fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> u32 {
#[test]
fn execute() {
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);
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()
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn execute() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let name = Name::new(&db, "Salsa".to_string());
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)
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn execute() {
let db = Database::default();
let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);
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
}
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
}
#[salsa::db]
impl salsa::Database for Database {}
#[test]
fn execute_when_specified() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 44);
@ -46,7 +37,7 @@ fn execute_when_specified() {
#[test]
fn execute_when_not_specified() {
let mut db = Database::default();
let mut db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 0);
let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 0);

View file

@ -3,20 +3,17 @@
use expect_test::expect;
mod common;
use common::{HasLogger, Logger};
use common::{LogDatabase, Logger};
use salsa::Setter;
use test_log::test;
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct MyInput {
field: u32,
}
#[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));
intermediate_result(db, input).field(db) * 2
}
@ -27,35 +24,16 @@ struct MyTracked<'db> {
}
#[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));
let tracked = MyTracked::new(db, input.field(db) / 2);
let _ = tracked.field(db); // read the field of an entity we created
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]
fn one_entity() {
let mut db = Database::default();
let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 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.
#[test]
fn red_herring() {
let mut db = Database::default();
let mut db: salsa::DatabaseImpl<Logger> = Default::default();
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);

View file

@ -1,10 +1,7 @@
use expect_test::expect;
use salsa::Database as SalsaDatabase;
mod common;
use common::{HasLogger, Logger};
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
use common::{LogDatabase, Logger};
use salsa::Database;
#[salsa::input]
struct MyInput {
@ -17,7 +14,7 @@ struct MyTracked<'db> {
}
#[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:?})"));
let t = MyTracked::new(db, input.field(db) * 2);
tracked_fn_extra::specify(db, t, 2222);
@ -25,33 +22,14 @@ fn tracked_fn(db: &dyn Db, input: MyInput) -> u32 {
}
#[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:?})"));
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]
fn execute() {
let mut db = Database::default();
let mut db: salsa::DatabaseImpl<Logger> = salsa::DatabaseImpl::default();
let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 2222);
db.assert_logs(expect![[r#"

View file

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

View file

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

View file

@ -1,4 +1,4 @@
use salsa::Database as _;
use salsa::Database;
#[salsa::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]
fn invoke() {
Database::default().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, 3);
let x: &Vec<String> = input.test(db);
expect_test::expect![[r#"

View file

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

View file

@ -1,4 +1,4 @@
use salsa::Database as _;
use salsa::Database;
#[salsa::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]
fn invoke() {
Database::default().attach(|db| {
salsa::DatabaseImpl::new().attach(|db| {
let input = Input::new(db, 3);
let x: &Vec<String> = input.test(db);
expect_test::expect![[r#"

View file

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

View file

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

View file

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