distinguish fields from other specified values

And make `QueryOrigin` finer grained while we are at it.
This commit is contained in:
Niko Matsakis 2022-08-13 00:42:40 -04:00
parent e2763aba88
commit b8643a5f70
7 changed files with 49 additions and 19 deletions

View file

@ -133,7 +133,7 @@ impl TrackedStruct {
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
let __id = __ingredients.#struct_index.new_struct(__runtime, (#(#id_field_names,)*));
#(
__ingredients.#value_field_indices.specify(__db, __id, #value_field_names);
__ingredients.#value_field_indices.specify_field(__db, __id, #value_field_names);
)*
__id
}

View file

@ -65,7 +65,10 @@ impl Stack {
/// Extend the stack of queries with the dependencies from `origin`.
fn extend(&mut self, origin: Option<QueryOrigin>) {
match origin {
None | Some(QueryOrigin::Assigned(_)) => {}
None
| Some(QueryOrigin::Assigned(_))
| Some(QueryOrigin::BaseInput)
| Some(QueryOrigin::Field) => {}
Some(QueryOrigin::Derived(edges)) | Some(QueryOrigin::DerivedUntracked(edges)) => {
for DependencyIndex {
ingredient_index,

View file

@ -96,7 +96,10 @@ where
fn evict(&self, key: C::Key) {
if let Some(memo) = self.memo_map.get(key) {
match memo.revisions.origin {
QueryOrigin::Assigned(_) | QueryOrigin::DerivedUntracked(_) => {
QueryOrigin::Assigned(_)
| QueryOrigin::DerivedUntracked(_)
| QueryOrigin::BaseInput
| QueryOrigin::Field => {
// Careful: Cannot evict memos whose values were
// assigned as output of another query
// or those with untracked inputs

View file

@ -162,7 +162,7 @@ where
}
match &old_memo.revisions.origin {
QueryOrigin::Assigned(Some(_)) => {
QueryOrigin::Assigned(_) => {
// If the value was assigneed by another query,
// and that query were up-to-date,
// then we would have updated the `verified_at` field already.
@ -170,8 +170,10 @@ where
// during this revision or is otherwise stale.
return false;
}
QueryOrigin::Assigned(None) => {
// This value was `set` by the mutator thread -- ie, it's a base input and it cannot be out of date.
QueryOrigin::BaseInput | QueryOrigin::Field => {
// BaseInput: This value was `set` by the mutator thread -- ie, it's a base input and it cannot be out of date.
// Field: This value is the value of a field of some tracked struct S. It is always updated whenever S is created.
// So if a query has access to S, then they will have an up-to-date value.
return true;
}
QueryOrigin::DerivedUntracked(_) => {

View file

@ -16,8 +16,13 @@ where
/// Specifies the value of the function for the given key.
/// This is a way to imperatively set the value of a function.
/// It only works if the key is a tracked struct created in the current query.
pub fn specify<'db>(&self, db: &'db DynDb<'db, C>, key: C::Key, value: C::Value)
where
pub(crate) fn specify<'db>(
&self,
db: &'db DynDb<'db, C>,
key: C::Key,
value: C::Value,
origin: impl Fn(DatabaseKeyIndex) -> QueryOrigin,
) where
C::Key: TrackedStructInDb<DynDb<'db, C>>,
{
let runtime = db.salsa_runtime();
@ -55,7 +60,7 @@ where
let mut revisions = QueryRevisions {
changed_at: current_deps.changed_at,
durability: current_deps.durability,
origin: QueryOrigin::Assigned(Some(active_query_key)),
origin: origin(active_query_key),
};
if let Some(old_memo) = self.memo_map.get(key) {
@ -73,13 +78,26 @@ where
self.insert_memo(key, memo);
}
/// Specify the value for `key` but do not record it is an output.
/// This is used for the value fields declared on a tracked struct.
/// They are different from other calls to specify beacuse we KNOW they will be given a value by construction,
/// so recording them as an explicit output (and checking them for validity, etc) is pure overhead.
pub fn specify_field<'db>(&self, db: &'db DynDb<'db, C>, key: C::Key, value: C::Value)
where
C::Key: TrackedStructInDb<DynDb<'db, C>>,
{
self.specify(db, key, value, |_| QueryOrigin::Field);
}
/// Specify the value for `key` *and* record that we did so.
/// Used for explicit calls to `specify`, but not needed for pre-declared tracked struct fields.
pub fn specify_and_record<'db>(&self, db: &'db DynDb<'db, C>, key: C::Key, value: C::Value)
where
C::Key: TrackedStructInDb<DynDb<'db, C>>,
{
self.specify(db, key, value);
self.specify(db, key, value, |database_key_index| {
QueryOrigin::Assigned(database_key_index)
});
// Record that the current query *specified* a value for this cell.
let database_key_index = self.database_key_index(key);
@ -97,7 +115,6 @@ where
key: C::Key,
) {
let runtime = db.salsa_runtime();
let current_revision = runtime.current_revision();
let memo = match self.memo_map.get(key) {
Some(m) => m,
@ -107,7 +124,7 @@ where
// If we are marking this as validated, it must be a value that was
// assigneed by `executor`.
match memo.revisions.origin {
QueryOrigin::Assigned(Some(by_query)) => assert_eq!(by_query, executor),
QueryOrigin::Assigned(by_query) => assert_eq!(by_query, executor),
_ => panic!(
"expected a query assigned by `{:?}`, not `{:?}`",
executor.debug(db),

View file

@ -28,7 +28,7 @@ where
revisions: QueryRevisions {
changed_at: revision,
durability,
origin: QueryOrigin::Assigned(None),
origin: QueryOrigin::BaseInput,
},
};

View file

@ -56,11 +56,16 @@ impl QueryRevisions {
/// Tracks the way that a memoized value for a query was created.
#[derive(Debug, Clone)]
pub enum QueryOrigin {
/// The value was assigned as the output of another query (e.g., using `specify`)
/// or is an input set by the mutator thread (e.g., using `set`).
/// The `DatabaseKeyIndex` is the identity of the assigning query
/// or `None` if this is an input set by the mutator thread.
Assigned(Option<DatabaseKeyIndex>),
/// The value was assigned as the output of another query (e.g., using `specify`).
/// The `DatabaseKeyIndex` is the identity of the assigning query.
Assigned(DatabaseKeyIndex),
/// This is the value field of a tracked struct.
/// These are different from `Assigned` because we know they will always be assigned a value and hence are never "out of date".
Field,
/// This value was set as a base input to the computation.
BaseInput,
/// The value was derived by executing a function
/// and we were able to track ALL of that function's inputs.
@ -78,10 +83,10 @@ impl QueryOrigin {
/// Indices for queries *written* by this query (or `&[]` if its value was assigned).
pub(crate) fn outputs(&self) -> impl Iterator<Item = DatabaseKeyIndex> + '_ {
let slice = match self {
QueryOrigin::Assigned(_) => &[],
QueryOrigin::Derived(edges) | QueryOrigin::DerivedUntracked(edges) => {
&edges.input_outputs[edges.separator as usize..]
}
QueryOrigin::Assigned(_) | QueryOrigin::BaseInput | QueryOrigin::Field => &[],
};
// the `QueryEdges` repr. invariant guarantees all tracked outputs are full 'dependency index' values,