Skip to content

Commit 86dd1e1

Browse files
committed
Implement inputs builder
1 parent cd339fc commit 86dd1e1

File tree

7 files changed

+183
-8
lines changed

7 files changed

+183
-8
lines changed

components/salsa-macro-rules/src/setup_input_struct.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ macro_rules! setup_input_struct {
4848
$zalsa:ident,
4949
$zalsa_struct:ident,
5050
$Configuration:ident,
51+
$Builder:ident,
5152
$CACHE:ident,
5253
$Db:ident,
5354
]
@@ -121,14 +122,33 @@ macro_rules! setup_input_struct {
121122
}
122123

123124
impl $Struct {
125+
#[inline]
124126
pub fn $new_fn<$Db>(db: &$Db, $($field_id: $field_ty),*) -> Self
125127
where
126128
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
127129
$Db: ?Sized + salsa::Database,
128130
{
129-
let current_revision = $zalsa::current_revision(db);
130-
let stamps = $zalsa::Array::new([$zalsa::stamp(current_revision, Default::default()); $N]);
131-
$Configuration::ingredient(db.as_salsa_database()).new_input(($($field_id,)*), stamps)
131+
Self::builder($($field_id,)*).new(db)
132+
}
133+
134+
pub fn builder($($field_id: $field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
135+
{
136+
// Implement `new` here instead of inside the builder module
137+
// because $Configuration can't be named in `builder`.
138+
impl builder::$Builder {
139+
pub fn new<$Db>(self, db: &$Db) -> $Struct
140+
where
141+
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
142+
$Db: ?Sized + salsa::Database
143+
{
144+
let current_revision = $zalsa::current_revision(db);
145+
let ingredient = $Configuration::ingredient(db.as_salsa_database());
146+
let (fields, stamps) = builder::builder_into_inner(self, current_revision);
147+
ingredient.new_input(fields, stamps)
148+
}
149+
}
150+
151+
builder::new_builder($($field_id,)*)
132152
}
133153

134154
$(
@@ -204,6 +224,40 @@ macro_rules! setup_input_struct {
204224
})
205225
}
206226
}
227+
228+
mod builder {
229+
use super::*;
230+
use salsa::plumbing as $zalsa;
231+
use $zalsa::input as $zalsa_struct;
232+
233+
impl $zalsa_struct::HasBuilder for $Struct {
234+
type Builder = $Builder;
235+
}
236+
237+
// These are standalone functions instead of methods on `Builder` to prevent
238+
// that the enclosing module can call them.
239+
pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
240+
$Builder { fields: ($($field_id,)*), durability: $zalsa::Durability::default() }
241+
}
242+
243+
pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), $zalsa::Array<$zalsa::Stamp, $N>) {
244+
let stamps = $zalsa::Array::new([$zalsa::stamp(revision, builder.durability); $N]);
245+
(builder.fields, stamps)
246+
}
247+
248+
pub struct $Builder {
249+
fields: ($($field_ty,)*),
250+
durability: $zalsa::Durability,
251+
}
252+
253+
impl $Builder {
254+
/// Sets the durability of all fields
255+
pub fn durability(mut self, durability: $zalsa::Durability) -> Self {
256+
self.durability = durability;
257+
self
258+
}
259+
}
260+
}
207261
};
208262
};
209263
}

components/salsa-macro-rules/src/setup_struct_fn.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

components/salsa-macros/src/input.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl Macro {
9393
let zalsa = self.hygiene.ident("zalsa");
9494
let zalsa_struct = self.hygiene.ident("zalsa_struct");
9595
let Configuration = self.hygiene.ident("Configuration");
96+
let Builder = self.hygiene.ident("Builder");
9697
let CACHE = self.hygiene.ident("CACHE");
9798
let Db = self.hygiene.ident("Db");
9899

@@ -117,6 +118,7 @@ impl Macro {
117118
#zalsa,
118119
#zalsa_struct,
119120
#Configuration,
121+
#Builder,
120122
#CACHE,
121123
#Db,
122124
]

src/input/builder.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use super::{Configuration, IngredientImpl};
2+
use crate::array::Array;
3+
use crate::runtime::{stamp, StampedValue};
4+
use crate::{array, Durability, Revision};
5+
6+
pub trait HasBuilder {
7+
type Builder;
8+
}
9+
10+
#[derive(Debug)]
11+
pub struct BuilderImpl<Fields> {
12+
fields: Fields,
13+
durability: Durability,
14+
}
15+
16+
impl<F> BuilderImpl<F> {
17+
/// Creates a new builder.
18+
pub fn create(fields: F) -> Self {
19+
Self {
20+
fields,
21+
durability: Durability::default(),
22+
}
23+
}
24+
25+
/// Sets the durability of all fields
26+
pub fn durability(&mut self, durability: Durability) {
27+
self.durability = durability;
28+
}
29+
30+
pub fn new<const N: usize, C>(
31+
self,
32+
revision: Revision,
33+
ingredient: &IngredientImpl<C>,
34+
) -> C::Struct
35+
where
36+
C: Configuration<Fields = F, Stamps = array::Array<StampedValue<()>, N>>,
37+
{
38+
let stamps = Array::new([stamp(revision, self.durability); N]);
39+
ingredient.new_input(self.fields, stamps)
40+
}
41+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub mod plumbing {
106106
pub use crate::update::helper::Dispatch as UpdateDispatch;
107107
pub use crate::update::helper::Fallback as UpdateFallback;
108108
pub use crate::update::Update;
109+
pub use crate::Durability;
109110

110111
pub use salsa_macro_rules::macro_if;
111112
pub use salsa_macro_rules::maybe_backdate;
@@ -125,6 +126,7 @@ pub mod plumbing {
125126
}
126127

127128
pub mod input {
129+
pub use crate::input::{HasBuilder};
128130
pub use crate::input::input_field::FieldIngredientImpl;
129131
pub use crate::input::setter::SetterImpl;
130132
pub use crate::input::Configuration;

tests/accumulate-from-tracked-fn.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
//! Then mutate the values so that the tracked function re-executes.
33
//! Check that we accumulate the appropriate, new values.
44
5-
mod common;
6-
use common::{HasLogger, Logger};
7-
85
use expect_test::expect;
9-
use salsa::{Accumulator, Setter};
106
use test_log::test;
117

8+
use common::{HasLogger, Logger};
9+
use salsa::{Accumulator, Setter};
10+
11+
mod common;
1212
#[salsa::db]
1313
trait Db: salsa::Database + HasLogger {}
1414

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! Test that a `tracked` fn on a `salsa::input`
2+
//! compiles and executes successfully.
3+
#![allow(warnings)]
4+
5+
use expect_test::expect;
6+
7+
use common::{HasLogger, Logger};
8+
use salsa::plumbing::HasStorage;
9+
use salsa::{Database, Durability, Event, EventKind, Setter};
10+
11+
mod common;
12+
#[salsa::input]
13+
struct MyInput {
14+
field: u32,
15+
}
16+
17+
#[salsa::tracked]
18+
fn tracked_fn(db: &dyn salsa::Database, input: MyInput) -> u32 {
19+
input.field(db) * 2
20+
}
21+
22+
#[test]
23+
fn execute() {
24+
#[salsa::db]
25+
#[derive(Default)]
26+
struct Database {
27+
storage: salsa::Storage<Self>,
28+
logger: Logger,
29+
}
30+
31+
#[salsa::db]
32+
impl salsa::Database for Database {
33+
fn salsa_event(&self, event: Event) {
34+
match event.kind {
35+
EventKind::WillCheckCancellation => {}
36+
_ => {
37+
self.push_log(format!("salsa_event({:?})", event.kind));
38+
}
39+
}
40+
}
41+
}
42+
43+
impl HasLogger for Database {
44+
fn logger(&self) -> &Logger {
45+
&self.logger
46+
}
47+
}
48+
49+
let mut db = Database::default();
50+
let input_low = MyInput::new(&db, 22);
51+
let input_high = MyInput::builder(2200).durability(Durability::HIGH).new(&db);
52+
53+
assert_eq!(tracked_fn(&db, input_low), 44);
54+
assert_eq!(tracked_fn(&db, input_high), 4400);
55+
56+
db.assert_logs(expect![[r#"
57+
[
58+
"salsa_event(WillExecute { database_key: tracked_fn(0) })",
59+
"salsa_event(WillExecute { database_key: tracked_fn(1) })",
60+
]"#]]);
61+
62+
db.synthetic_write(Durability::LOW);
63+
64+
assert_eq!(tracked_fn(&db, input_low), 44);
65+
assert_eq!(tracked_fn(&db, input_high), 4400);
66+
67+
// There's currently no good way to verify whether an input was validated using shallow or deep comparison.
68+
// All we can do for now is verify that the values were validated.
69+
// Note: It maybe confusing why it validates `input_high` when the write has `Durability::LOW`.
70+
// This is because all values must be validated whenever a write occurs. It doesn't mean that it
71+
// executed the query.
72+
db.assert_logs(expect![[r#"
73+
[
74+
"salsa_event(DidValidateMemoizedValue { database_key: tracked_fn(0) })",
75+
"salsa_event(DidValidateMemoizedValue { database_key: tracked_fn(1) })",
76+
]"#]]);
77+
}

0 commit comments

Comments
 (0)