Support on-demand inputs

This adds initial support for on-demand inputs by allowing new inputs to
be created with only a shared reference to the database. This allows
creating new inputs during a revision and therefore from inside tracked
functions.
This commit is contained in:
Jack Rickard 2022-09-03 22:48:24 +01:00
parent f8f6dbd349
commit 5b8464c4f9
No known key found for this signature in database
GPG key ID: 88084D7D08A72C8A
29 changed files with 108 additions and 107 deletions

View file

@ -33,8 +33,8 @@ mod parser;
mod type_check;
pub fn main() {
let mut db = db::Database::default();
let source_program = SourceProgram::new(&mut db, String::new());
let db = db::Database::default();
let source_program = SourceProgram::new(&db, String::new());
compile::compile(&db, source_program);
let diagnostics = compile::compile::accumulated::<Diagnostics>(&db, source_program);
eprintln!("{diagnostics:?}");

View file

@ -355,10 +355,10 @@ fn parse_string(source_text: &str) -> String {
use salsa::debug::DebugWithDb;
// Create the database
let mut db = crate::db::Database::default();
let db = crate::db::Database::default();
// Create the source program
let source_program = SourceProgram::new(&mut db, source_text.to_string());
let source_program = SourceProgram::new(&db, source_text.to_string());
// Invoke the parser
let statements = parse_statements(&db, source_program);

View file

@ -97,7 +97,7 @@ fn check_string(
let mut db = Database::default().enable_logging();
// Create the source program
let source_program = SourceProgram::new(&mut db, source_text.to_string());
let source_program = SourceProgram::new(&db, source_text.to_string());
// Invoke the parser
let program = parse_statements(&db, source_program);

View file

@ -130,10 +130,10 @@ impl InputStruct {
let constructor: syn::ImplItemMethod = if singleton {
parse_quote! {
pub fn #constructor_name(__db: &mut #db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
{
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
let __id = __ingredients.#input_index.new_singleton_input(__runtime);
#(
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);
@ -143,10 +143,10 @@ impl InputStruct {
}
} else {
parse_quote! {
pub fn #constructor_name(__db: &mut #db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
{
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
let __id = __ingredients.#input_index.new_input(__runtime);
#(
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);

View file

@ -1,4 +1,7 @@
use std::fmt;
use std::{
fmt,
sync::atomic::{AtomicU32, Ordering},
};
use crate::{
cycle::CycleRecoveryStrategy,
@ -16,7 +19,7 @@ where
Id: InputId,
{
ingredient_index: IngredientIndex,
counter: u32,
counter: AtomicU32,
debug_name: &'static str,
_phantom: std::marker::PhantomData<Id>,
}
@ -41,24 +44,20 @@ where
}
}
pub fn new_input(&mut self, _runtime: &mut Runtime) -> Id {
let next_id = self.counter;
self.counter += 1;
pub fn new_input(&self, _runtime: &Runtime) -> Id {
let next_id = self.counter.fetch_add(1, Ordering::Relaxed);
Id::from_id(crate::Id::from_u32(next_id))
}
pub fn new_singleton_input(&mut self, _runtime: &mut Runtime) -> Id {
if self.counter >= 1 {
// already exists
Id::from_id(crate::Id::from_u32(self.counter - 1))
} else {
self.new_input(_runtime)
}
pub fn new_singleton_input(&self, _runtime: &Runtime) -> Id {
// There's only one singleton so record that we've created it
// and return the only id.
self.counter.store(1, Ordering::Relaxed);
Id::from_id(crate::Id::from_u32(0))
}
pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option<Id> {
(self.counter > 0)
.then(|| Id::from_id(crate::Id::from_id(crate::Id::from_u32(self.counter - 1))))
(self.counter.load(Ordering::Relaxed) > 0).then(|| Id::from_id(crate::Id::from_u32(0)))
}
}

View file

@ -4,7 +4,7 @@ use crate::key::DependencyIndex;
use crate::runtime::local_state::QueryOrigin;
use crate::runtime::StampedValue;
use crate::{AsId, DatabaseKeyIndex, Durability, Id, IngredientIndex, Revision, Runtime};
use rustc_hash::FxHashMap;
use dashmap::DashMap;
use std::fmt;
use std::hash::Hash;
@ -15,7 +15,7 @@ use std::hash::Hash;
/// This makes the implementation considerably simpler.
pub struct InputFieldIngredient<K, F> {
index: IngredientIndex,
map: FxHashMap<K, StampedValue<F>>,
map: DashMap<K, StampedValue<F>>,
debug_name: &'static str,
}
@ -31,13 +31,7 @@ where
}
}
pub fn store(
&mut self,
runtime: &mut Runtime,
key: K,
value: F,
durability: Durability,
) -> Option<F> {
pub fn store(&self, runtime: &Runtime, key: K, value: F, durability: Durability) -> Option<F> {
let revision = runtime.current_revision();
let stamped_value = StampedValue {
value,
@ -52,12 +46,12 @@ where
}
}
pub fn fetch(&self, runtime: &Runtime, key: K) -> &F {
pub fn fetch<'db>(&'db self, runtime: &'db Runtime, key: K) -> &F {
let StampedValue {
value,
durability,
changed_at,
} = self.map.get(&key).unwrap();
} = &*self.map.get(&key).unwrap();
runtime.report_tracked_read(
self.database_key_index(key).into(),
@ -65,7 +59,9 @@ where
*changed_at,
);
value
// SAFETY:
// * Values are only removed or altered when we have `&mut self`
unsafe { transmute_lifetime(self, value) }
}
fn database_key_index(&self, key: K) -> DatabaseKeyIndex {
@ -76,6 +72,14 @@ where
}
}
// Returns `u` but with the lifetime of `t`.
//
// Safe if you know that data at `u` will remain shared
// until the reference `t` expires.
unsafe fn transmute_lifetime<'t, 'u, T, U>(_t: &'t T, u: &'u U) -> &'t U {
std::mem::transmute(u)
}
impl<DB: ?Sized, K, F> Ingredient<DB> for InputFieldIngredient<K, F>
where
K: AsId,

View file

@ -65,8 +65,8 @@ impl HasLogger for Database {
fn test1() {
let mut db = Database::default();
let l0 = List::new(&mut db, 1, None);
let l1 = List::new(&mut db, 10, Some(l0));
let l0 = List::new(&db, 1, None);
let l1 = List::new(&db, 10, Some(l0));
compute(&db, l1);
expect![[r#"

View file

@ -69,8 +69,8 @@ impl HasLogger for Database {
fn test1() {
let mut db = Database::default();
let l1 = List::new(&mut db, 1, None);
let l2 = List::new(&mut db, 2, Some(l1));
let l1 = List::new(&db, 1, None);
let l2 = List::new(&db, 2, Some(l1));
assert_eq!(compute(&db, l2), 2);
db.assert_logs(expect![[r#"

View file

@ -64,8 +64,8 @@ impl HasLogger for Database {
fn test1() {
let mut db = Database::default();
let l1 = List::new(&mut db, 1, None);
let l2 = List::new(&mut db, 2, Some(l1));
let l1 = List::new(&db, 1, None);
let l2 = List::new(&db, 2, Some(l1));
assert_eq!(compute(&db, l2), 2);
db.assert_logs(expect![[r#"

View file

@ -85,7 +85,7 @@ fn accumulate_once() {
let mut db = Database::default();
// Just call accumulate on a base input to see what happens.
let input = MyInput::new(&mut db, 2, 3);
let input = MyInput::new(&db, 2, 3);
let logs = push_logs::accumulated::<Logs>(&db, input);
expect![[r#"
[
@ -109,7 +109,7 @@ fn change_a_and_reaccumulate() {
let mut db = Database::default();
// Accumulate logs for `a = 2` and `b = 3`
let input = MyInput::new(&mut db, 2, 3);
let input = MyInput::new(&db, 2, 3);
let logs = push_logs::accumulated::<Logs>(&db, input);
expect![[r#"
[
@ -148,7 +148,7 @@ fn get_a_logs_after_changing_b() {
let mut db = Database::default();
// Invoke `push_a_logs` with `a = 2` and `b = 3` (but `b` doesn't matter)
let input = MyInput::new(&mut db, 2, 3);
let input = MyInput::new(&db, 2, 3);
let logs = push_a_logs::accumulated::<Logs>(&db, input);
expect![[r#"
[

View file

@ -183,7 +183,7 @@ fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle {
#[test]
fn cycle_memoized() {
let mut db = Database::default();
let input = MyInput::new(&mut db);
let input = MyInput::new(&db);
let cycle = extract_cycle(|| memoized_a(&db, input));
let expected = expect![[r#"
[
@ -197,7 +197,7 @@ fn cycle_memoized() {
#[test]
fn cycle_volatile() {
let mut db = Database::default();
let input = MyInput::new(&mut db);
let input = MyInput::new(&db);
let cycle = extract_cycle(|| volatile_a(&db, input));
let expected = expect![[r#"
[
@ -215,7 +215,7 @@ fn expect_cycle() {
// +-----+
let mut db = Database::default();
let abc = ABC::new(&mut 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());
}
@ -225,7 +225,7 @@ fn inner_cycle() {
// ^ |
// +-----+
let mut db = Database::default();
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::B);
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::B);
let err = cycle_c(&db, abc);
assert!(err.is_err());
let expected = expect![[r#"
@ -243,7 +243,7 @@ fn cycle_revalidate() {
// ^ |
// +-----+
let mut db = Database::default();
let abc = ABC::new(&mut 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());
abc.set_b(&mut db).to(CycleQuery::A); // same value as default
assert!(cycle_a(&db, abc).is_err());
@ -255,7 +255,7 @@ fn cycle_recovery_unchanged_twice() {
// ^ |
// +-----+
let mut db = Database::default();
let abc = ABC::new(&mut 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());
abc.set_c(&mut db).to(CycleQuery::A); // force new revision
@ -267,7 +267,7 @@ fn cycle_appears() {
let mut db = Database::default();
// A --> B
let abc = ABC::new(&mut 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());
// A --> B
@ -284,7 +284,7 @@ fn cycle_disappears() {
// A --> B
// ^ |
// +-----+
let abc = ABC::new(&mut 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());
// A --> B
@ -334,7 +334,7 @@ fn cycle_mixed_1() {
// A --> B <-- C
// | ^
// +-----+
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::C, CycleQuery::B);
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::C, CycleQuery::B);
let expected = expect![[r#"
[
@ -354,7 +354,7 @@ fn cycle_mixed_2() {
// A --> B --> C
// ^ |
// +-----------+
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::C, CycleQuery::A);
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::C, CycleQuery::A);
let expected = expect![[r#"
[
"cycle_a(0)",
@ -374,7 +374,7 @@ fn cycle_deterministic_order() {
// A --> B
// ^ |
// +-----+
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
(db, abc)
};
let (db, abc) = f();
@ -411,7 +411,7 @@ fn cycle_multiple() {
//
// Here, conceptually, B encounters a cycle with A and then
// recovers.
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::AthenC, CycleQuery::A);
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::AthenC, CycleQuery::A);
let c = cycle_c(&db, abc);
let b = cycle_b(&db, abc);
@ -446,7 +446,7 @@ fn cycle_recovery_set_but_not_participating() {
// A --> C -+
// ^ |
// +--+
let abc = ABC::new(&mut db, CycleQuery::C, CycleQuery::None, CycleQuery::C);
let abc = ABC::new(&db, CycleQuery::C, CycleQuery::None, CycleQuery::C);
// Here we expect C to panic and A not to recover:
let r = extract_cycle(|| drop(cycle_a(&db, abc)));

View file

@ -36,13 +36,13 @@ impl Db for Database {}
#[test]
fn input() {
let mut db = Database::default();
let db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
let not_salsa = NotSalsa {
field: "it's salsa time".to_string(),
};
let complex_struct = ComplexStruct::new(&mut db, input, not_salsa);
let complex_struct = ComplexStruct::new(&db, input, not_salsa);
// default debug only includes identity fields
let actual = format!("{:?}", complex_struct.debug(&db));

View file

@ -91,7 +91,7 @@ fn basic() {
let mut db = Database::default();
// Creates 3 tracked structs
let input = MyInput::new(&mut db, 3);
let input = MyInput::new(&db, 3);
assert_eq!(final_result(&db, input), 2 * 2 + 2);
db.assert_logs(expect![[r#"
[

View file

@ -84,7 +84,7 @@ fn basic() {
let mut db = Database::default();
// Creates 3 tracked structs
let input = MyInput::new(&mut db, 3);
let input = MyInput::new(&db, 3);
assert_eq!(final_result(&db, input), 2 * 2 + 2);
db.assert_logs(expect![[r#"
[

View file

@ -74,7 +74,7 @@ fn execute() {
// intermediate results:
// x = (22 + 1) / 2 = 11
// y = 22 / 2 = 11
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(final_result_depends_on_x(&db, input), 22);
db.assert_logs(expect![[r#"
[

View file

@ -53,7 +53,7 @@ fn execute() {
// result_depends_on_y = y - 1
let mut db = Database::default();
let input = MyInput::new(&mut db, 22, 33);
let input = MyInput::new(&db, 22, 33);
assert_eq!(result_depends_on_x(&db, input), 23);
db.assert_logs(expect![[r#"
[

View file

@ -54,7 +54,7 @@ impl HasLogger for Database {
fn execute() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
@ -85,7 +85,7 @@ fn execute() {
fn red_herring() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
@ -96,7 +96,7 @@ fn red_herring() {
// Create a distinct input and mutate it.
// This will trigger a new revision in the database
// but shouldn't actually invalidate our existing ones.
let input2 = MyInput::new(&mut db, 44);
let input2 = MyInput::new(&db, 44);
input2.set_field(&mut db).to(66);
// Re-run the query on the original input. Nothing re-executes!

View file

@ -82,28 +82,26 @@ fn load_n_potatoes() -> usize {
#[test]
fn lru_works() {
let mut db = DatabaseImpl::default();
let db = DatabaseImpl::default();
assert_eq!(load_n_potatoes(), 0);
for i in 0..128u32 {
let input = MyInput::new(&mut db, i);
let input = MyInput::new(&db, i);
let p = get_hot_potato(&db, input);
assert_eq!(p.0, i)
}
// Create a new input to change the revision, and trigger the GC
MyInput::new(&mut db, 0);
MyInput::new(&db, 0);
assert_eq!(load_n_potatoes(), 32);
}
#[test]
fn lru_doesnt_break_volatile_queries() {
let mut db = DatabaseImpl::default();
let db = DatabaseImpl::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(&mut db, i as u32))
.collect();
let inputs: Vec<MyInput> = (0..128usize).map(|i| MyInput::new(&db, i as u32)).collect();
// Here, we check that we execute each volatile query at most once, despite
// LRU. That does mean that we have more values in DB than the LRU capacity,
@ -118,10 +116,10 @@ fn lru_doesnt_break_volatile_queries() {
#[test]
fn lru_can_be_changed_at_runtime() {
let mut db = DatabaseImpl::default();
let db = DatabaseImpl::default();
assert_eq!(load_n_potatoes(), 0);
let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&mut db, i))).collect();
let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&db, i))).collect();
for &(i, input) in inputs.iter() {
let p = get_hot_potato(&db, input);
@ -129,7 +127,7 @@ fn lru_can_be_changed_at_runtime() {
}
// Create a new input to change the revision, and trigger the GC
MyInput::new(&mut db, 0);
MyInput::new(&db, 0);
assert_eq!(load_n_potatoes(), 32);
get_hot_potato::set_lru_capacity(&db, 64);
@ -140,7 +138,7 @@ fn lru_can_be_changed_at_runtime() {
}
// Create a new input to change the revision, and trigger the GC
MyInput::new(&mut db, 0);
MyInput::new(&db, 0);
assert_eq!(load_n_potatoes(), 64);
// Special case: setting capacity to zero disables LRU
@ -152,7 +150,7 @@ fn lru_can_be_changed_at_runtime() {
}
// Create a new input to change the revision, and trigger the GC
MyInput::new(&mut db, 0);
MyInput::new(&db, 0);
assert_eq!(load_n_potatoes(), 128);
drop(db);
@ -167,7 +165,7 @@ fn lru_keeps_dependency_info() {
// Invoke `get_hot_potato2` 33 times. This will (in turn) invoke
// `get_hot_potato`, which will trigger LRU after 32 executions.
let inputs: Vec<MyInput> = (0..(capacity + 1))
.map(|i| MyInput::new(&mut db, i as u32))
.map(|i| MyInput::new(&db, i as u32))
.collect();
for (i, input) in inputs.iter().enumerate() {

View file

@ -36,7 +36,7 @@ impl HasLogger for Database {
fn execute() {
let mut db = Database::default();
let input = MyInput::new(&mut db, "Hello".to_string());
let input = MyInput::new(&db, "Hello".to_string());
// Overwrite field with an empty String
// and store the old value in my_string

View file

@ -92,10 +92,10 @@ fn recover_b2(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
#[test]
fn execute() {
let mut db = Database::default();
let db = Database::default();
db.knobs().signal_on_will_block.set(3);
let input = MyInput::new(&mut db, 1);
let input = MyInput::new(&db, 1);
let thread_a = std::thread::spawn({
let db = db.snapshot();

View file

@ -87,10 +87,10 @@ fn recover_b3(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
#[test]
fn execute() {
let mut db = Database::default();
let db = Database::default();
db.knobs().signal_on_will_block.set(3);
let input = MyInput::new(&mut db, 1);
let input = MyInput::new(&db, 1);
let thread_a = std::thread::spawn({
let db = db.snapshot();

View file

@ -43,10 +43,10 @@ pub(crate) fn b(db: &dyn Db, input: MyInput) -> i32 {
#[test]
fn execute() {
let mut db = Database::default();
let db = Database::default();
db.knobs().signal_on_will_block.set(3);
let input = MyInput::new(&mut db, -1);
let input = MyInput::new(&db, -1);
let thread_a = std::thread::spawn({
let db = db.snapshot();

View file

@ -76,10 +76,10 @@ pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 {
#[test]
fn execute() {
let mut db = Database::default();
let db = Database::default();
db.knobs().signal_on_will_block.set(3);
let input = MyInput::new(&mut db, 1);
let input = MyInput::new(&db, 1);
let thread_a = std::thread::spawn({
let db = db.snapshot();

View file

@ -56,6 +56,6 @@ impl Db for Database {}
#[should_panic]
fn execute_when_specified() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input);
}

View file

@ -89,7 +89,7 @@ impl HasLogger for Database {
fn test_run_0() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 0);
let input = MyInput::new(&db, 0);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -110,7 +110,7 @@ fn test_run_0() {
fn test_run_5() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 5);
let input = MyInput::new(&db, 5);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -131,7 +131,7 @@ fn test_run_5() {
fn test_run_10() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 10);
let input = MyInput::new(&db, 10);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -155,7 +155,7 @@ fn test_run_10() {
fn test_run_20() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 20);
let input = MyInput::new(&db, 20);
assert_eq!(final_result(&db, input), 200);
db.assert_logs(expect![[r#"
[
@ -183,7 +183,7 @@ fn test_run_0_then_5_then_20() {
//
// * `create_tracked` specifies `10` for `maybe_specified`
// * final resuilt of `100` is derived by executing `read_maybe_specified`
let input = MyInput::new(&mut db, 0);
let input = MyInput::new(&db, 0);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -254,7 +254,7 @@ fn test_run_0_then_5_then_10_then_20() {
//
// * `create_tracked` specifies `10` for `maybe_specified`
// * final resuilt of `100` is derived by executing `read_maybe_specified`
let input = MyInput::new(&mut db, 0);
let input = MyInput::new(&db, 0);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -342,7 +342,7 @@ fn test_run_0_then_5_then_10_then_20() {
fn test_run_5_then_20() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 5);
let input = MyInput::new(&db, 5);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[

View file

@ -30,6 +30,6 @@ fn execute() {
impl Db for Database {}
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input), 44);
}

View file

@ -33,7 +33,7 @@ impl Db for Database {}
#[test]
fn execute() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let db = Database::default();
let input = MyInput::new(&db, 22);
assert_eq!(tracked_fn(&db, input).field(&db), 44);
}

View file

@ -44,7 +44,7 @@ impl Db for Database {}
#[test]
fn execute_when_specified() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 44);
assert_eq!(tracked_fn_extra(&db, tracked), 2222);
@ -53,7 +53,7 @@ fn execute_when_specified() {
#[test]
fn execute_when_not_specified() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 0);
let input = MyInput::new(&db, 0);
let tracked = tracked_fn(&db, input);
assert_eq!(tracked.field(&db), 0);
assert_eq!(tracked_fn_extra(&db, tracked), 0);

View file

@ -55,7 +55,7 @@ impl HasLogger for Database {
fn one_entity() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
@ -86,7 +86,7 @@ fn one_entity() {
fn red_herring() {
let mut db = Database::default();
let input = MyInput::new(&mut db, 22);
let input = MyInput::new(&db, 22);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
@ -97,7 +97,7 @@ fn red_herring() {
// Create a distinct input and mutate it.
// This will trigger a new revision in the database
// but shouldn't actually invalidate our existing ones.
let input2 = MyInput::new(&mut db, 44);
let input2 = MyInput::new(&db, 44);
input2.set_field(&mut db).to(66);
// Re-run the query on the original input. Nothing re-executes!