362: Implement Setter API r=XFFXFF a=crlf0710

Fixes #329 

Co-authored-by: Charles Lew <crlf0710@gmail.com>
This commit is contained in:
bors[bot] 2022-08-22 13:14:49 +00:00 committed by GitHub
commit e1f162742d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 74 additions and 32 deletions

View file

@ -114,7 +114,9 @@ fn check_string(
// Apply edits and check diagnostics/logs after each one
for (new_source_text, expected_diagnostics, expected_logs) in edits {
source_program.set_text(&mut db, new_source_text.to_string());
source_program
.set_text(&mut db)
.to(new_source_text.to_string());
let program = parse_statements(&db, source_program);
expected_diagnostics.assert_debug_eq(&type_check_program::accumulated::<Diagnostics>(
&db, program,

View file

@ -94,11 +94,11 @@ impl InputStruct {
let set_field_names = self.all_set_field_names();
let field_setters: Vec<syn::ImplItemMethod> = field_indices.iter().zip(&set_field_names).zip(&field_tys).map(|((field_index, set_field_name), field_ty)| {
parse_quote! {
pub fn #set_field_name<'db>(self, __db: &'db mut #db_dyn_ty, __value: #field_ty) -> #field_ty
pub fn #set_field_name<'db>(self, __db: &'db mut #db_dyn_ty) -> salsa::setter::Setter<'db, #ident, #field_ty>
{
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
__ingredients.#field_index.store(__runtime, self, __value, salsa::Durability::LOW).unwrap()
salsa::setter::Setter::new(__runtime, self, &mut __ingredients.#field_index)
}
}
})

View file

@ -426,7 +426,7 @@ fn setter_fn(
///
/// # Examples
///
/// ```rust
/// ```rust,ignore
/// #[salsa::tracked(lru=32)]
/// fn my_tracked_fn(db: &dyn crate::Db, ...) { }
///

View file

@ -20,6 +20,7 @@ pub mod revision;
pub mod routes;
pub mod runtime;
pub mod salsa_struct;
pub mod setter;
pub mod storage;
#[doc(hidden)]
pub mod tracked_struct;

View file

@ -0,0 +1,39 @@
use crate::input_field::InputFieldIngredient;
use crate::{AsId, Durability, Runtime};
use std::hash::Hash;
#[must_use]
pub struct Setter<'setter, K, F> {
runtime: &'setter mut Runtime,
key: K,
ingredient: &'setter mut InputFieldIngredient<K, F>,
durability: Durability,
}
impl<'setter, K, F> Setter<'setter, K, F>
where
K: Eq + Hash + AsId,
{
pub fn new(
runtime: &'setter mut Runtime,
key: K,
ingredient: &'setter mut InputFieldIngredient<K, F>,
) -> Self {
Setter {
runtime,
key,
ingredient,
durability: Durability::LOW,
}
}
pub fn with_durability(self, durability: Durability) -> Self {
Setter { durability, ..self }
}
pub fn to(self, value: F) -> F {
self.ingredient
.store(self.runtime, self.key, value, self.durability)
.unwrap()
}
}

View file

@ -81,7 +81,7 @@ fn test1() {
"#]]
.assert_debug_eq(&compute::accumulated::<Integers>(&db, l1));
l0.set_value(&mut db, 2);
l0.set_value(&mut db).to(2);
compute(&db, l1);
expect![[r#"
[

View file

@ -87,7 +87,7 @@ fn test1() {
// When we mutate `l1`, we should re-execute `compute` for `l1`,
// and we re-execute accumulated for `l1`, but we do NOT re-execute
// `compute` for `l2`.
l1.set_value(&mut db, 2);
l1.set_value(&mut db).to(2);
assert_eq!(compute(&db, l2), 2);
db.assert_logs(expect![[r#"
[

View file

@ -82,7 +82,7 @@ fn test1() {
// but we should not have to re-execute `compute` for `l2`.
// The only inpout for `compute(l1)` is the accumulated values from `l1`,
// which have not changed.
l1.set_value(&mut db, 2);
l1.set_value(&mut db).to(2);
assert_eq!(compute(&db, l2), 2);
db.assert_logs(expect![[r#"
[

View file

@ -132,7 +132,7 @@ fn change_a_and_reaccumulate() {
]"#]]);
// Change to `a = 1`, which means `push_logs` does not call `push_a_logs` at all
input.set_field_a(&mut db, 1);
input.set_field_a(&mut db).to(1);
let logs = push_logs::accumulated::<Logs>(&db, input);
expect![[r#"
[
@ -167,7 +167,7 @@ fn get_a_logs_after_changing_b() {
// Changing `b` does not cause `push_a_logs` to re-execute
// and we still get the same result
input.set_field_b(&mut db, 5);
input.set_field_b(&mut db).to(5);
let logs = push_a_logs::accumulated::<Logs>(&db, input);
expect![[r#"
[

View file

@ -248,7 +248,7 @@ fn cycle_revalidate() {
let mut db = Database::default();
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
assert!(cycle_a(&db, abc).is_err());
abc.set_b(&mut db, CycleQuery::A); // same value as default
abc.set_b(&mut db).to(CycleQuery::A); // same value as default
assert!(cycle_a(&db, abc).is_err());
}
@ -261,7 +261,7 @@ fn cycle_recovery_unchanged_twice() {
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
assert!(cycle_a(&db, abc).is_err());
abc.set_c(&mut db, CycleQuery::A); // force new revision
abc.set_c(&mut db).to(CycleQuery::A); // force new revision
assert!(cycle_a(&db, abc).is_err());
}
@ -276,7 +276,7 @@ fn cycle_appears() {
// A --> B
// ^ |
// +-----+
abc.set_b(&mut db, CycleQuery::A);
abc.set_b(&mut db).to(CycleQuery::A);
assert!(cycle_a(&db, abc).is_err());
}
@ -291,7 +291,7 @@ fn cycle_disappears() {
assert!(cycle_a(&db, abc).is_err());
// A --> B
abc.set_b(&mut db, CycleQuery::None);
abc.set_b(&mut db).to(CycleQuery::None);
assert!(cycle_a(&db, abc).is_ok());
}

View file

@ -117,7 +117,7 @@ fn basic() {
// * the struct's field
// * the `copy_field` result
input.set_field(&mut db, 2);
input.set_field(&mut db).to(2);
assert_eq!(final_result(&db, input), 1 * 2 + 0 * 2);
db.assert_logs(expect![[r#"
[

View file

@ -103,7 +103,7 @@ fn basic() {
// * the struct itself
// * the struct's field
// * the `contribution_from_struct` result
input.set_field(&mut db, 2);
input.set_field(&mut db).to(2);
assert_eq!(final_result(&db, input), 1 * 2 + 0 * 2);
db.assert_logs(expect![[r#"
[

View file

@ -91,7 +91,7 @@ fn execute() {
"final_result_depends_on_y(MyInput(Id { value: 1 }))",
]"#]]);
input.set_field(&mut db, 23);
input.set_field(&mut db).to(23);
// x = (23 + 1) / 2 = 12
// Intermediate result x changes, so final result depends on x
// needs to be recomputed;

View file

@ -70,7 +70,7 @@ fn execute() {
"result_depends_on_y(MyInput(Id { value: 1 }))",
]"#]]);
input.set_x(&mut db, 23);
input.set_x(&mut db).to(23);
// input x changes, so result depends on x needs to be recomputed;
assert_eq!(result_depends_on_x(&db, input), 24);
db.assert_logs(expect![[r#"

View file

@ -68,14 +68,14 @@ fn execute() {
// Intermediate result is the same, so final result does
// not need to be recomputed:
input.set_field(&mut db, 23);
input.set_field(&mut db).to(23);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
input.set_field(&mut db, 24);
input.set_field(&mut db).to(24);
assert_eq!(final_result(&db, input), 24);
db.assert_logs(expect![[r#"
[
@ -101,7 +101,7 @@ fn red_herring() {
// This will trigger a new revision in the database
// but shouldn't actually invalidate our existing ones.
let input2 = MyInput::new(&mut db, 44);
input2.set_field(&mut db, 66);
input2.set_field(&mut db).to(66);
// Re-run the query on the original input. Nothing re-executes!
assert_eq!(final_result(&db, input), 22);

View file

@ -44,12 +44,12 @@ fn execute() {
// Overwrite field with an empty String
// and store the old value in my_string
let mut my_string = input.set_field(&mut db, String::new());
let mut my_string = input.set_field(&mut db).to(String::new());
my_string.push_str(" World!");
// Set the field back to out initial String,
// expecting to get the empty one back
assert_eq!(input.set_field(&mut db, my_string), "");
assert_eq!(input.set_field(&mut db).to(my_string), "");
// Check if the stored String is the one we expected
assert_eq!(input.field(&db), "Hello World!");

View file

@ -27,7 +27,7 @@ impl MyInput {
}
pub fn set_field(self, db: &mut dyn Db, id: String) {
self.set_text(db, id);
self.set_text(db).to(id);
}
}

View file

@ -207,7 +207,7 @@ fn test_run_0_then_5_then_20() {
//
// * `create_tracked` does re-execute, but specifies same value for `maybe_specified` as before
// * `read_maybe_specified` does not re-execute (its input has not changed)
input.set_field(&mut db, 5);
input.set_field(&mut db).to(5);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -226,7 +226,7 @@ fn test_run_0_then_5_then_20() {
// * `create_tracked` re-executes but does not specify any value
// * `read_maybe_specified` is invoked and it calls `maybe_specified`, which now executes
// (its value has not been specified)
input.set_field(&mut db, 20);
input.set_field(&mut db).to(20);
assert_eq!(final_result(&db, input), 200);
db.assert_logs(expect![[r#"
[
@ -278,7 +278,7 @@ fn test_run_0_then_5_then_10_then_20() {
//
// * `create_tracked` does re-execute, but specifies same value for `maybe_specified` as before
// * `read_maybe_specified` does not re-execute (its input has not changed)
input.set_field(&mut db, 5);
input.set_field(&mut db).to(5);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -297,7 +297,7 @@ fn test_run_0_then_5_then_10_then_20() {
// * `create_tracked` does re-execute and specifies no value for `maybe_specified`
// * `maybe_specified_value` returns 10; this is the same value as was specified.
// * `read_maybe_specified` therefore does NOT need to execute.
input.set_field(&mut db, 10);
input.set_field(&mut db).to(10);
assert_eq!(final_result(&db, input), 100);
db.assert_logs(expect![[r#"
[
@ -318,7 +318,7 @@ fn test_run_0_then_5_then_10_then_20() {
// Set input to 20:
//
// * Everything re-executes to get new result (200).
input.set_field(&mut db, 20);
input.set_field(&mut db).to(20);
assert_eq!(final_result(&db, input), 200);
db.assert_logs(expect![[r#"
[
@ -362,7 +362,7 @@ fn test_run_5_then_20() {
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
]"#]]);
input.set_field(&mut db, 20);
input.set_field(&mut db).to(20);
assert_eq!(final_result(&db, input), 200);
db.assert_logs(expect![[r#"
[

View file

@ -69,14 +69,14 @@ fn one_entity() {
// Intermediate result is the same, so final result does
// not need to be recomputed:
input.set_field(&mut db, 23);
input.set_field(&mut db).to(23);
assert_eq!(final_result(&db, input), 22);
db.assert_logs(expect![[r#"
[
"intermediate_result(MyInput(Id { value: 1 }))",
]"#]]);
input.set_field(&mut db, 24);
input.set_field(&mut db).to(24);
assert_eq!(final_result(&db, input), 24);
db.assert_logs(expect![[r#"
[
@ -102,7 +102,7 @@ fn red_herring() {
// This will trigger a new revision in the database
// but shouldn't actually invalidate our existing ones.
let input2 = MyInput::new(&mut db, 44);
input2.set_field(&mut db, 66);
input2.set_field(&mut db).to(66);
// Re-run the query on the original input. Nothing re-executes!
assert_eq!(final_result(&db, input), 22);