Repository: salsa-rs/salsa (rust)

Issue:
Add options to tracked funcitons for lru capacity
The [set_lru_capacity](https://docs.rs/salsa/0.16.1/salsa/struct.QueryTableMut.html#method.set_lru_capacity) method, in old salsa, can be used to fix the maximum capacity for a query. In salsa-2022, we already have a method [`set_capacity`](https://github.com/salsa-rs/salsa/blob/367c0bfe1154d8848600bf4029343882652a358c/components/salsa-2022/src/function.rs#L151) to do the same thing, but without exposing the api to users.  

We could support users writing somthing like

```rust
#[salsa::tracked(lru_capacity=32)]
fn my_tracked_fn(db: &dyn crate::Db, ...) { }
```  

cc @nikomatsakis 

Pull request:
Add options to tracked funcitons for lru capacity 
fixes #344 

Now we can write something like the following to set the lru capacity of tracked functions  
```rust
#[salsa::tracked(lru=32)]
fn my_tracked_fn(db: &dyn crate::Db, ...) { }
```

some details:  
* lru should not be combined with specify. We will report an error if people do #[salsa::tracked(lru = 32, specify)]
* set 0 as default capacity to disable LRU (Because I think doing this would make the code simpler when implementing `create_ingredients` of tracked functions).
* old salsa support to change lru capacity at runtime, [as noted here](https://salsa-rs.github.io/salsa/rfcs/RFC0004-LRU.html?highlight=change#reference-guide), but we do not support this now

Patch:
diff --git a/components/salsa-2022-macros/src/accumulator.rs b/components/salsa-2022-macros/src/accumulator.rs
index cfc32c9e..26276398 100644
--- a/components/salsa-2022-macros/src/accumulator.rs
+++ b/components/salsa-2022-macros/src/accumulator.rs
@@ -32,6 +32,8 @@ impl crate::options::AllowedOptions for Accumulator {
     const DB: bool = false;
 
     const RECOVERY_FN: bool = false;
+
+    const LRU: bool = false;
 }
 
 fn accumulator_contents(
diff --git a/components/salsa-2022-macros/src/db.rs b/components/salsa-2022-macros/src/db.rs
index 6995ed26..e2b82a9b 100644
--- a/components/salsa-2022-macros/src/db.rs
+++ b/components/salsa-2022-macros/src/db.rs
@@ -141,12 +141,12 @@ fn has_jars_dyn_impl(input: &syn::ItemStruct, storage: &syn::Ident) -> syn::Item
                 ingredient.origin(index.key_index())
             }
 
-            fn mark_validated_output(&self, executor: salsa::DatabaseKeyIndex, output: salsa::key::DependencyIndex) {
+            fn mark_validated_output(&self, executor: salsa::DatabaseKeyIndex, output: salsa::key::DatabaseKeyIndex) {
                 let ingredient = self.#storage.ingredient(output.ingredient_index());
                 ingredient.mark_validated_output(self, executor, output.key_index());
             }
 
-            fn remove_stale_output(&self, executor: salsa::DatabaseKeyIndex, stale_output: salsa::key::DependencyIndex) {
+            fn remove_stale_output(&self, executor: salsa::DatabaseKeyIndex, stale_output: salsa::key::DatabaseKeyIndex) {
                 let ingredient = self.#storage.ingredient(stale_output.ingredient_index());
                 ingredient.remove_stale_output(self, executor, stale_output.key_index());
             }
diff --git a/components/salsa-2022-macros/src/jar.rs b/components/salsa-2022-macros/src/jar.rs
index 7a6498d4..5c582abd 100644
--- a/components/salsa-2022-macros/src/jar.rs
+++ b/components/salsa-2022-macros/src/jar.rs
@@ -41,6 +41,8 @@ impl crate::options::AllowedOptions for Jar {
     const DB: bool = true;
 
     const RECOVERY_FN: bool = false;
+
+    const LRU: bool = false;
 }
 
 pub(crate) fn jar_struct_and_friends(
diff --git a/components/salsa-2022-macros/src/options.rs b/components/salsa-2022-macros/src/options.rs
index eb8bd937..185bde14 100644
--- a/components/salsa-2022-macros/src/options.rs
+++ b/components/salsa-2022-macros/src/options.rs
@@ -46,6 +46,11 @@ pub(crate) struct Options<A: AllowedOptions> {
     /// If this is `Some`, the value is the `<ident>`.
     pub data: Option<syn::Ident>,
 
+    /// The `lru = <usize>` option is used to set the lru capacity for a tracked function.
+    ///
+    /// If this is `Some`, the value is the `<usize>`.
+    pub lru: Option<usize>,
+
     /// Remember the `A` parameter, which plays no role after parsing.
     phantom: PhantomData<A>,
 }
@@ -61,6 +66,7 @@ impl<A: AllowedOptions> Default for Options<A> {
             recovery_fn: Default::default(),
             data: Default::default(),
             phantom: Default::default(),
+            lru: Default::default(),
         }
     }
 }
@@ -74,6 +80,7 @@ pub(crate) trait AllowedOptions {
     const DATA: bool;
     const DB: bool;
     const RECOVERY_FN: bool;
+    const LRU: bool;
 }
 
 type Equals = syn::Token![=];
@@ -195,6 +202,20 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
                         "`data` option not allowed here",
                     ));
                 }
+            } else if ident == "lru" {
+                if A::LRU {
+                    let _eq = Equals::parse(input)?;
+                    let lit = syn::LitInt::parse(input)?;
+                    let value = lit.base10_parse::<usize>()?;
+                    if let Some(old) = std::mem::replace(&mut options.lru, Some(value)) {
+                        return Err(syn::Error::new(old.span(), "option `lru` provided twice"));
+                    }
+                } else {
+                    return Err(syn::Error::new(
+                        ident.span(),
+                        "`lru` option not allowed here",
+                    ));
+                }
             } else {
                 return Err(syn::Error::new(
                     ident.span(),
diff --git a/components/salsa-2022-macros/src/salsa_struct.rs b/components/salsa-2022-macros/src/salsa_struct.rs
index beb7f0d2..36b01df9 100644
--- a/components/salsa-2022-macros/src/salsa_struct.rs
+++ b/components/salsa-2022-macros/src/salsa_struct.rs
@@ -50,6 +50,8 @@ impl crate::options::AllowedOptions for SalsaStruct {
     const DB: bool = false;
 
     const RECOVERY_FN: bool = false;
+
+    const LRU: bool = false;
 }
 
 const BANNED_FIELD_NAMES: &[&str] = &["from", "new"];
diff --git a/components/salsa-2022-macros/src/tracked_fn.rs b/components/salsa-2022-macros/src/tracked_fn.rs
index 8c0884e1..4ce59b3d 100644
--- a/components/salsa-2022-macros/src/tracked_fn.rs
+++ b/components/salsa-2022-macros/src/tracked_fn.rs
@@ -31,6 +31,13 @@ fn tracked_fn(args: Args, item_fn: syn::ItemFn) -> syn::Result<TokenStream> {
                 "tracked functon takes too many argments to have its value set with `specify`",
             ));
         }
+
+        if args.lru.is_some() {
+            return Err(syn::Error::new(
+                s.span(),
+                "`specify` and `lru` cannot be used together",
+            ));
+        }
     }
 
     let struct_item = configuration_struct(&item_fn);
@@ -72,6 +79,8 @@ impl crate::options::AllowedOptions for TrackedFn {
     const DB: bool = false;
 
     const RECOVERY_FN: bool = true;
+
+    const LRU: bool = true;
 }
 
 /// Returns the key type for this tracked function.
@@ -228,6 +237,9 @@ fn ingredients_for_impl(
         }
     };
 
+    // set 0 as default to disable LRU
+    let lru = args.lru.unwrap_or(0);
+
     parse_quote! {
         impl salsa::storage::IngredientsFor for #config_ty {
             type Ingredients = Self;
@@ -254,8 +266,10 @@ fn ingredients_for_impl(
                                     <_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient_mut(jar);
                                 &mut ingredients.function
                             });
-                        salsa::function::FunctionIngredient::new(index)
-                    },
+                        let ingredient = salsa::function::FunctionIngredient::new(index);
+                        ingredient.set_capacity(#lru);
+                        ingredient
+                    }
                 }
             }
         }
diff --git a/components/salsa-2022/src/accumulator.rs b/components/salsa-2022/src/accumulator.rs
index 1d91a405..c34e663a 100644
--- a/components/salsa-2022/src/accumulator.rs
+++ b/components/salsa-2022/src/accumulator.rs
@@ -1,13 +1,11 @@
-//! Basic test of accumulator functionality.
-
 use crate::{
     cycle::CycleRecoveryStrategy,
     hash::FxDashMap,
     ingredient::{Ingredient, IngredientRequiresReset},
     key::DependencyIndex,
-    runtime::local_state::QueryOrigin,
+    runtime::{local_state::QueryOrigin, StampedValue},
     storage::HasJar,
-    DatabaseKeyIndex, Event, EventKind, IngredientIndex, Revision, Runtime,
+    DatabaseKeyIndex, Durability, IngredientIndex, Revision, Runtime,
 };
 
 pub trait Accumulator {
@@ -18,53 +16,36 @@ pub trait Accumulator {
     where
         Db: ?Sized + HasJar<Self::Jar>;
 }
-pub struct AccumulatorIngredient<Data: Clone> {
-    index: IngredientIndex,
-    map: FxDashMap<DatabaseKeyIndex, AccumulatedValues<Data>>,
-}
 
-struct AccumulatedValues<Data> {
-    produced_at: Revision,
-    values: Vec<Data>,
+pub struct AccumulatorIngredient<Data: Clone> {
+    map: FxDashMap<DatabaseKeyIndex, StampedValue<Vec<Data>>>,
 }
 
 impl<Data: Clone> AccumulatorIngredient<Data> {
-    pub fn new(index: IngredientIndex) -> Self {
+    pub fn new(_index: IngredientIndex) -> Self {
         Self {
             map: FxDashMap::default(),
-            index,
-        }
-    }
-
-    fn dependency_index(&self) -> DependencyIndex {
-        DependencyIndex {
-            ingredient_index: self.index,
-            key_index: None,
         }
     }
 
     pub fn push(&self, runtime: &Runtime, value: Data) {
-        let current_revision = runtime.current_revision();
-        let (active_query, _) = match runtime.active_query() {
+        let (active_query, active_inputs) = match runtime.active_query() {
             Some(pair) => pair,
             None => {
                 panic!("cannot accumulate values outside of an active query")
             }
         };
 
-        let mut accumulated_values = self.map.entry(active_query).or_insert(AccumulatedValues {
-            values: vec![],
-            produced_at: current_revision,
+        let mut stamped_value = self.map.entry(active_query).or_insert(StampedValue {
+            value: vec![],
+            durability: Durability::MAX,
+            changed_at: Revision::start(),
         });
 
-        // This is the first push in a new revision. Reset.
-        if accumulated_values.produced_at != current_revision {
-            accumulated_values.values.truncate(0);
-            accumulated_values.produced_at = current_revision;
-        }
-
-        runtime.add_output(self.dependency_index());
-        accumulated_values.values.push(value);
+        stamped_value.value.push(value);
+        stamped_value
+            .value_mut()
+            .merge_revision_info(&active_inputs);
     }
 
     pub(crate) fn produced_by(
@@ -73,28 +54,20 @@ impl<Data: Clone> AccumulatorIngredient<Data> {
         query: DatabaseKeyIndex,
         output: &mut Vec<Data>,
     ) {
-        let current_revision = runtime.current_revision();
         if let Some(v) = self.map.get(&query) {
-            // FIXME: We don't currently have a good way to identify the value that was read.
-            // You can't report is as a tracked read of `query`, because the return value of query is not being read here --
-            // instead it is the set of values accumuated by `query`.
-            runtime.report_untracked_read();
-
-            let AccumulatedValues {
-                values,
-                produced_at,
+            let StampedValue {
+                value,
+                durability,
+                changed_at,
             } = v.value();
-
-            if *produced_at == current_revision {
-                output.extend(values.iter().cloned());
-            }
+            runtime.report_tracked_read(query.into(), *durability, *changed_at);
+            output.extend(value.iter().cloned());
         }
     }
 }
 
 impl<DB: ?Sized, Data> Ingredient<DB> for AccumulatorIngredient<Data>
 where
-    DB: crate::Database,
     Data: Clone,
 {
     fn maybe_changed_after(&self, _db: &DB, _input: DependencyIndex, _revision: Revision) -> bool {
@@ -109,36 +82,19 @@ where
         None
     }
 
-    fn mark_validated_output(
-        &self,
-        db: &DB,
-        executor: DatabaseKeyIndex,
-        output_key: Option<crate::Id>,
-    ) {
-        assert!(output_key.is_none());
-        let current_revision = db.salsa_runtime().current_revision();
-        if let Some(mut v) = self.map.get_mut(&executor) {
-            // The value is still valid in the new revision.
-            v.produced_at = current_revision;
-        }
+    fn mark_validated_output(&self, _db: &DB, executor: DatabaseKeyIndex, output_key: crate::Id) {
+        // FIXME
+        drop((executor, output_key));
     }
 
     fn remove_stale_output(
         &self,
-        db: &DB,
+        _db: &DB,
         executor: DatabaseKeyIndex,
-        stale_output_key: Option<crate::Id>,
+        stale_output_key: crate::Id,
     ) {
-        assert!(stale_output_key.is_none());
-        if self.map.remove(&executor).is_some() {
-            db.salsa_event(Event {
-                runtime_id: db.salsa_runtime().id(),
-                kind: EventKind::DidDiscardAccumulated {
-                    executor_key: executor,
-                    accumulator: self.dependency_index(),
-                },
-            })
-        }
+        // FIXME
+        drop((executor, stale_output_key));
     }
 
     fn reset_for_new_revision(&mut self) {
diff --git a/components/salsa-2022/src/event.rs b/components/salsa-2022/src/event.rs
index 01376646..a2909b17 100644
--- a/components/salsa-2022/src/event.rs
+++ b/components/salsa-2022/src/event.rs
@@ -1,6 +1,4 @@
-use crate::{
-    debug::DebugWithDb, key::DatabaseKeyIndex, key::DependencyIndex, runtime::RuntimeId, Database,
-};
+use crate::{debug::DebugWithDb, runtime::RuntimeId, Database, DatabaseKeyIndex};
 use std::fmt;
 
 /// The `Event` struct identifies various notable things that can
@@ -83,7 +81,7 @@ pub enum EventKind {
         execute_key: DatabaseKeyIndex,
 
         /// Key for the query that is no longer output
-        output_key: DependencyIndex,
+        output_key: DatabaseKeyIndex,
     },
 
     /// Tracked structs or memoized data were discarded (freed).
@@ -91,15 +89,6 @@ pub enum EventKind {
         /// Value being discarded.
         key: DatabaseKeyIndex,
     },
-
-    /// Discarded accumulated data from a given fn
-    DidDiscardAccumulated {
-        /// The key of the fn that accumulated results
-        executor_key: DatabaseKeyIndex,
-
-        /// Accumulator that was accumulated into
-        accumulator: DependencyIndex,
-    },
 }
 
 impl fmt::Debug for EventKind {
@@ -133,14 +122,6 @@ impl fmt::Debug for EventKind {
             EventKind::DidDiscard { key } => {
                 fmt.debug_struct("DidDiscard").field("key", &key).finish()
             }
-            EventKind::DidDiscardAccumulated {
-                executor_key,
-                accumulator,
-            } => fmt
-                .debug_struct("DidDiscardAccumulated")
-                .field("executor_key", executor_key)
-                .field("accumulator", accumulator)
-                .finish(),
         }
     }
 }
@@ -180,14 +161,6 @@ where
                 .debug_struct("DidDiscard")
                 .field("key", &key.debug(db))
                 .finish(),
-            EventKind::DidDiscardAccumulated {
-                executor_key,
-                accumulator,
-            } => fmt
-                .debug_struct("DidDiscardAccumulated")
-                .field("executor_key", &executor_key.debug(db))
-                .field("accumulator", &accumulator.debug(db))
-                .finish(),
         }
     }
 }
diff --git a/components/salsa-2022/src/function.rs b/components/salsa-2022/src/function.rs
index cfadc8c4..0f70641c 100644
--- a/components/salsa-2022/src/function.rs
+++ b/components/salsa-2022/src/function.rs
@@ -225,13 +225,8 @@ where
         self.origin(key)
     }
 
-    fn mark_validated_output(
-        &self,
-        db: &DB,
-        executor: DatabaseKeyIndex,
-        output_key: Option<crate::Id>,
-    ) {
-        let output_key = C::key_from_id(output_key.unwrap());
+    fn mark_validated_output(&self, db: &DB, executor: DatabaseKeyIndex, output_key: crate::Id) {
+        let output_key = C::key_from_id(output_key);
         self.validate_specified_value(db.as_jar_db(), executor, output_key);
     }
 
@@ -239,7 +234,7 @@ where
         &self,
         _db: &DB,
         _executor: DatabaseKeyIndex,
-        _stale_output_key: Option<crate::Id>,
+        _stale_output_key: crate::Id,
     ) {
         // This function is invoked when a query Q specifies the value for `stale_output_key` in rev 1,
         // but not in rev 2. We don't do anything in this case, we just leave the (now stale) memo.
diff --git a/components/salsa-2022/src/function/diff_outputs.rs b/components/salsa-2022/src/function/diff_outputs.rs
index cb989fe3..617371ba 100644
--- a/components/salsa-2022/src/function/diff_outputs.rs
+++ b/components/salsa-2022/src/function/diff_outputs.rs
@@ -1,6 +1,6 @@
 use crate::{
-    key::DependencyIndex, runtime::local_state::QueryRevisions, storage::HasJarsDyn, Database,
-    DatabaseKeyIndex, Event, EventKind,
+    runtime::local_state::QueryRevisions, storage::HasJarsDyn, Database, DatabaseKeyIndex, Event,
+    EventKind,
 };
 
 use super::{memo::Memo, Configuration, DynDb, FunctionIngredient};
@@ -44,7 +44,7 @@ where
         }
     }
 
-    fn report_stale_output(db: &DynDb<'_, C>, key: DatabaseKeyIndex, output: DependencyIndex) {
+    fn report_stale_output(db: &DynDb<'_, C>, key: DatabaseKeyIndex, output: DatabaseKeyIndex) {
         let runtime_id = db.salsa_runtime().id();
         db.salsa_event(Event {
             runtime_id,
diff --git a/components/salsa-2022/src/function/specify.rs b/components/salsa-2022/src/function/specify.rs
index 1769b00c..e511eadd 100644
--- a/components/salsa-2022/src/function/specify.rs
+++ b/components/salsa-2022/src/function/specify.rs
@@ -101,7 +101,7 @@ where
 
         // Record that the current query *specified* a value for this cell.
         let database_key_index = self.database_key_index(key);
-        db.salsa_runtime().add_output(database_key_index.into());
+        db.salsa_runtime().add_output(database_key_index);
     }
 
     /// Invoked when the query `executor` has been validated as having green inputs
diff --git a/components/salsa-2022/src/ingredient.rs b/components/salsa-2022/src/ingredient.rs
index 06b1a903..76347d71 100644
--- a/components/salsa-2022/src/ingredient.rs
+++ b/components/salsa-2022/src/ingredient.rs
@@ -26,18 +26,13 @@ pub trait Ingredient<DB: ?Sized> {
 
     /// Invoked when the value `output_key` should be marked as valid in the current revision.
     /// This occurs because the value for `executor`, which generated it, was marked as valid in the current revision.
-    fn mark_validated_output(&self, db: &DB, executor: DatabaseKeyIndex, output_key: Option<Id>);
+    fn mark_validated_output(&self, db: &DB, executor: DatabaseKeyIndex, output_key: Id);
 
     /// Invoked when the value `stale_output` was output by `executor` in a previous
     /// revision, but was NOT output in the current revision.
     ///
     /// This hook is used to clear out the stale value so others cannot read it.
-    fn remove_stale_output(
-        &self,
-        db: &DB,
-        executor: DatabaseKeyIndex,
-        stale_output_key: Option<Id>,
-    );
+    fn remove_stale_output(&self, db: &DB, executor: DatabaseKeyIndex, stale_output_key: Id);
 
     /// Informs the ingredient `self` that the salsa struct with id `id` has been deleted.
     /// This gives `self` a chance to remove any memoized data dependent on `id`.
diff --git a/components/salsa-2022/src/input.rs b/components/salsa-2022/src/input.rs
index f6dc35cf..d0ce79f0 100644
--- a/components/salsa-2022/src/input.rs
+++ b/components/salsa-2022/src/input.rs
@@ -62,12 +62,7 @@ where
         None
     }
 
-    fn mark_validated_output(
-        &self,
-        _db: &DB,
-        executor: DatabaseKeyIndex,
-        output_key: Option<crate::Id>,
-    ) {
+    fn mark_validated_output(&self, _db: &DB, executor: DatabaseKeyIndex, output_key: crate::Id) {
         unreachable!(
             "mark_validated_output({:?}, {:?}): input cannot be the output of a tracked function",
             executor, output_key
@@ -78,7 +73,7 @@ where
         &self,
         _db: &DB,
         executor: DatabaseKeyIndex,
-        stale_output_key: Option<crate::Id>,
+        stale_output_key: crate::Id,
     ) {
         unreachable!(
             "remove_stale_output({:?}, {:?}): input cannot be the output of a tracked function",
diff --git a/components/salsa-2022/src/input_field.rs b/components/salsa-2022/src/input_field.rs
index 8204e60b..4db87951 100644
--- a/components/salsa-2022/src/input_field.rs
+++ b/components/salsa-2022/src/input_field.rs
@@ -90,21 +90,9 @@ where
         None
     }
 
-    fn mark_validated_output(
-        &self,
-        _db: &DB,
-        _executor: DatabaseKeyIndex,
-        _output_key: Option<Id>,
-    ) {
-    }
+    fn mark_validated_output(&self, _db: &DB, _executor: DatabaseKeyIndex, _output_key: Id) {}
 
-    fn remove_stale_output(
-        &self,
-        _db: &DB,
-        _executor: DatabaseKeyIndex,
-        _stale_output_key: Option<Id>,
-    ) {
-    }
+    fn remove_stale_output(&self, _db: &DB, _executor: DatabaseKeyIndex, _stale_output_key: Id) {}
 
     fn salsa_struct_deleted(&self, _db: &DB, _id: Id) {
         panic!("unexpected call: input fields are never deleted");
diff --git a/components/salsa-2022/src/interned.rs b/components/salsa-2022/src/interned.rs
index e30c1235..9810c5ea 100644
--- a/components/salsa-2022/src/interned.rs
+++ b/components/salsa-2022/src/interned.rs
@@ -200,12 +200,7 @@ where
         None
     }
 
-    fn mark_validated_output(
-        &self,
-        _db: &DB,
-        executor: DatabaseKeyIndex,
-        output_key: Option<crate::Id>,
-    ) {
+    fn mark_validated_output(&self, _db: &DB, executor: DatabaseKeyIndex, output_key: crate::Id) {
         unreachable!(
             "mark_validated_output({:?}, {:?}): input cannot be the output of a tracked function",
             executor, output_key
@@ -216,7 +211,7 @@ where
         &self,
         _db: &DB,
         executor: DatabaseKeyIndex,
-        stale_output_key: Option<crate::Id>,
+        stale_output_key: crate::Id,
     ) {
         unreachable!(
             "remove_stale_output({:?}, {:?}): interned ids are not outputs",
diff --git a/components/salsa-2022/src/runtime.rs b/components/salsa-2022/src/runtime.rs
index c4a03b58..e83a54b8 100644
--- a/components/salsa-2022/src/runtime.rs
+++ b/components/salsa-2022/src/runtime.rs
@@ -146,7 +146,7 @@ impl Runtime {
 
     /// Adds `key` to the list of output created by the current query
     /// (if not already present).
-    pub(crate) fn add_output(&self, key: DependencyIndex) {
+    pub(crate) fn add_output(&self, key: DatabaseKeyIndex) {
         self.local_state.add_output(key);
     }
 
diff --git a/components/salsa-2022/src/runtime/active_query.rs b/components/salsa-2022/src/runtime/active_query.rs
index 9da56e45..8578f62a 100644
--- a/components/salsa-2022/src/runtime/active_query.rs
+++ b/components/salsa-2022/src/runtime/active_query.rs
@@ -44,7 +44,7 @@ pub(super) struct ActiveQuery {
     ///
     /// We use a btree-set because we want to be able to
     /// extract the keys in sorted order.
-    pub(super) outputs: BTreeSet<DependencyIndex>,
+    pub(super) outputs: BTreeSet<DatabaseKeyIndex>,
 }
 
 impl ActiveQuery {
@@ -85,13 +85,12 @@ impl ActiveQuery {
     }
 
     /// Adds a key to our list of outputs.
-    pub(super) fn add_output(&mut self, key: DependencyIndex) {
+    pub(super) fn add_output(&mut self, key: DatabaseKeyIndex) {
         self.outputs.insert(key);
     }
 
     /// True if the given key was output by this query.
     pub(super) fn is_output(&self, key: DatabaseKeyIndex) -> bool {
-        let key: DependencyIndex = key.into();
         self.outputs.contains(&key)
     }
 
diff --git a/components/salsa-2022/src/runtime/local_state.rs b/components/salsa-2022/src/runtime/local_state.rs
index 339e23fe..06a675b4 100644
--- a/components/salsa-2022/src/runtime/local_state.rs
+++ b/components/salsa-2022/src/runtime/local_state.rs
@@ -81,7 +81,7 @@ pub enum QueryOrigin {
 
 impl QueryOrigin {
     /// Indices for queries *written* by this query (or `&[]` if its value was assigned).
-    pub(crate) fn outputs(&self) -> impl Iterator<Item = DependencyIndex> + '_ {
+    pub(crate) fn outputs(&self) -> impl Iterator<Item = DatabaseKeyIndex> + '_ {
         let slice = match self {
             QueryOrigin::Derived(edges) | QueryOrigin::DerivedUntracked(edges) => {
                 &edges.input_outputs[edges.separator as usize..]
@@ -89,7 +89,11 @@ impl QueryOrigin {
             QueryOrigin::Assigned(_) | QueryOrigin::BaseInput | QueryOrigin::Field => &[],
         };
 
-        slice.iter().copied()
+        // the `QueryEdges` repr. invariant guarantees all tracked outputs are full  'dependency index' values,
+        // so we can safely unwrap result of `try_from` here
+        slice
+            .iter()
+            .map(|&dep_index| DatabaseKeyIndex::try_from(dep_index).unwrap())
     }
 }
 
@@ -115,6 +119,7 @@ pub struct QueryEdges {
     ///
     /// * The inputs must be in **execution order** for the red-green algorithm to work.
     /// * The outputs must be in **sorted order** so that we can easily "diff" them between revisions.
+    /// * All outputs must have a `Some` value for `key_index`.
     input_outputs: Arc<[DependencyIndex]>,
 
     /// The index that separates inputs from outputs in the `tracked` field.
@@ -132,6 +137,9 @@ impl QueryEdges {
     /// Creates a new `QueryEdges`; the values given for each field must meet struct invariants.
     pub(crate) fn new(separator: usize, input_outputs: Arc<[DependencyIndex]>) -> Self {
         debug_assert!(separator <= input_outputs.len());
+        debug_assert!(input_outputs[separator..]
+            .iter()
+            .all(|&dep_index| DatabaseKeyIndex::try_from(dep_index).is_ok()));
         Self {
             separator: u32::try_from(separator).unwrap(),
             input_outputs,
@@ -189,7 +197,7 @@ impl LocalState {
         })
     }
 
-    pub(super) fn add_output(&self, entity: DependencyIndex) {
+    pub(super) fn add_output(&self, entity: DatabaseKeyIndex) {
         self.with_query_stack(|stack| {
             if let Some(top_query) = stack.last_mut() {
                 top_query.add_output(entity)
@@ -200,7 +208,7 @@ impl LocalState {
     pub(super) fn is_output(&self, entity: DatabaseKeyIndex) -> bool {
         self.with_query_stack(|stack| {
             if let Some(top_query) = stack.last_mut() {
-                top_query.is_output(entity.into())
+                top_query.is_output(entity)
             } else {
                 false
             }
diff --git a/components/salsa-2022/src/storage.rs b/components/salsa-2022/src/storage.rs
index a7566d20..1737b11d 100644
--- a/components/salsa-2022/src/storage.rs
+++ b/components/salsa-2022/src/storage.rs
@@ -180,12 +180,12 @@ pub trait HasJarsDyn {
 
     fn origin(&self, input: DatabaseKeyIndex) -> Option<QueryOrigin>;
 
-    fn mark_validated_output(&self, executor: DatabaseKeyIndex, output: DependencyIndex);
+    fn mark_validated_output(&self, executor: DatabaseKeyIndex, output: DatabaseKeyIndex);
 
     /// Invoked when `executor` used to output `stale_output` but no longer does.
     /// This method routes that into a call to the [`remove_stale_output`](`crate::ingredient::Ingredient::remove_stale_output`)
     /// method on the ingredient for `stale_output`.
-    fn remove_stale_output(&self, executor: DatabaseKeyIndex, stale_output: DependencyIndex);
+    fn remove_stale_output(&self, executor: DatabaseKeyIndex, stale_output: DatabaseKeyIndex);
 
     /// Informs `ingredient` that the salsa struct with id `id` has been deleted.
     /// This means that `id` will not be used in this revision and hence
diff --git a/components/salsa-2022/src/tracked_struct.rs b/components/salsa-2022/src/tracked_struct.rs
index 98ebf6d4..f03f96a2 100644
--- a/components/salsa-2022/src/tracked_struct.rs
+++ b/components/salsa-2022/src/tracked_struct.rs
@@ -86,7 +86,7 @@ where
             data,
         };
         let result = self.interned.intern(runtime, entity_key);
-        runtime.add_output(self.database_key_index(result).into());
+        runtime.add_output(self.database_key_index(result));
         result
     }
 
@@ -144,12 +144,7 @@ where
         None
     }
 
-    fn mark_validated_output(
-        &self,
-        _db: &DB,
-        executor: DatabaseKeyIndex,
-        output_key: Option<crate::Id>,
-    ) {
+    fn mark_validated_output(&self, _db: &DB, executor: DatabaseKeyIndex, output_key: crate::Id) {
         // FIXME
         drop((executor, output_key));
     }
@@ -158,13 +153,13 @@ where
         &self,
         db: &DB,
         _executor: DatabaseKeyIndex,
-        stale_output_key: Option<crate::Id>,
+        stale_output_key: crate::Id,
     ) {
         // This method is called when, in prior revisions,
         // `executor` creates a tracked struct `salsa_output_key`,
         // but it did not in the current revision.
         // In that case, we can delete `stale_output_key` and any data associated with it.
-        let stale_output_key: Id = Id::from_id(stale_output_key.unwrap());
+        let stale_output_key: Id = Id::from_id(stale_output_key);
         self.delete_entity(db.as_salsa_database(), stale_output_key);
     }
 


Test patch:
diff --git a/salsa-2022-tests/tests/accumulate-from-tracked-fn.rs b/salsa-2022-tests/tests/accumulate-from-tracked-fn.rs
deleted file mode 100644
index 85559e44..00000000
--- a/salsa-2022-tests/tests/accumulate-from-tracked-fn.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-//! Accumulate values from within a tracked function.
-//! Then mutate the values so that the tracked function re-executes.
-//! Check that we accumulate the appropriate, new values.
-
-use salsa_2022_tests::{HasLogger, Logger};
-
-use expect_test::expect;
-use test_log::test;
-
-#[salsa::jar(db = Db)]
-struct Jar(List, Integers, compute);
-
-trait Db: salsa::DbWithJar<Jar> + HasLogger {}
-
-#[salsa::input]
-struct List {
-    value: u32,
-    next: Option<List>,
-}
-
-#[salsa::accumulator]
-struct Integers(u32);
-
-#[salsa::tracked]
-fn compute(db: &dyn Db, input: List) {
-    eprintln!(
-        "{:?}(value={:?}, next={:?})",
-        input,
-        input.value(db),
-        input.next(db)
-    );
-    let result = if let Some(next) = input.next(db) {
-        let next_integers = compute::accumulated::<Integers>(db, next);
-        eprintln!("{:?}", next_integers);
-        let v = input.value(db) + next_integers.iter().sum::<u32>();
-        eprintln!("input={:?} v={:?}", input.value(db), v);
-        v
-    } else {
-        input.value(db)
-    };
-    Integers::push(db, result);
-    eprintln!("pushed result {:?}", result);
-}
-
-#[salsa::db(Jar)]
-#[derive(Default)]
-struct Database {
-    storage: salsa::Storage<Self>,
-    logger: Logger,
-}
-
-impl salsa::Database for Database {
-    fn salsa_event(&self, _event: salsa::Event) {}
-
-    fn salsa_runtime(&self) -> &salsa::Runtime {
-        self.storage.runtime()
-    }
-}
-
-impl Db for Database {}
-
-impl HasLogger for Database {
-    fn logger(&self) -> &Logger {
-        &self.logger
-    }
-}
-
-#[test]
-fn test1() {
-    let mut db = Database::default();
-
-    let l0 = List::new(&mut db, 1, None);
-    let l1 = List::new(&mut db, 10, Some(l0));
-
-    compute(&db, l1);
-    expect![[r#"
-        [
-            11,
-            1,
-        ]
-    "#]]
-    .assert_debug_eq(&compute::accumulated::<Integers>(&db, l1));
-
-    l0.set_value(&mut db, 2);
-    compute(&db, l1);
-    expect![[r#"
-        [
-            12,
-            2,
-        ]
-    "#]]
-    .assert_debug_eq(&compute::accumulated::<Integers>(&db, l1));
-}
diff --git a/salsa-2022-tests/tests/accumulate-reuse-workaround.rs b/salsa-2022-tests/tests/accumulate-reuse-workaround.rs
deleted file mode 100644
index fa3f5af6..00000000
--- a/salsa-2022-tests/tests/accumulate-reuse-workaround.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! Demonstrates the workaround of wrapping calls to
-//! `accumulated` in a tracked function to get better
-//! reuse.
-
-use salsa_2022_tests::{HasLogger, Logger};
-
-use expect_test::expect;
-use test_log::test;
-
-#[salsa::jar(db = Db)]
-struct Jar(List, Integers, compute, accumulated);
-
-trait Db: salsa::DbWithJar<Jar> + HasLogger {}
-
-#[salsa::input]
-struct List {
-    value: u32,
-    next: Option<List>,
-}
-
-#[salsa::accumulator]
-struct Integers(u32);
-
-#[salsa::tracked]
-fn compute(db: &dyn Db, input: List) -> u32 {
-    db.push_log(format!("compute({:?})", input,));
-
-    // always pushes 0
-    Integers::push(db, 0);
-
-    let result = if let Some(next) = input.next(db) {
-        let next_integers = accumulated(db, next);
-        let v = input.value(db) + next_integers.iter().sum::<u32>();
-        v
-    } else {
-        input.value(db)
-    };
-
-    // return value changes
-    result
-}
-
-#[salsa::tracked(return_ref)]
-fn accumulated(db: &dyn Db, input: List) -> Vec<u32> {
-    db.push_log(format!("accumulated({:?})", input,));
-    compute::accumulated::<Integers>(db, input)
-}
-
-#[salsa::db(Jar)]
-#[derive(Default)]
-struct Database {
-    storage: salsa::Storage<Self>,
-    logger: Logger,
-}
-
-impl salsa::Database for Database {
-    fn salsa_event(&self, _event: salsa::Event) {}
-
-    fn salsa_runtime(&self) -> &salsa::Runtime {
-        self.storage.runtime()
-    }
-}
-
-impl Db for Database {}
-
-impl HasLogger for Database {
-    fn logger(&self) -> &Logger {
-        &self.logger
-    }
-}
-
-#[test]
-fn test1() {
-    let mut db = Database::default();
-
-    let l1 = List::new(&mut db, 1, None);
-    let l2 = List::new(&mut db, 2, Some(l1));
-
-    assert_eq!(compute(&db, l2), 2);
-    db.assert_logs(expect![[r#"
-        [
-            "compute(List(Id { value: 2 }))",
-            "accumulated(List(Id { value: 1 }))",
-            "compute(List(Id { value: 1 }))",
-        ]"#]]);
-
-    // 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);
-    assert_eq!(compute(&db, l2), 2);
-    db.assert_logs(expect![[r#"
-        [
-            "accumulated(List(Id { value: 1 }))",
-            "compute(List(Id { value: 1 }))",
-        ]"#]]);
-}
diff --git a/salsa-2022-tests/tests/accumulate-reuse.rs b/salsa-2022-tests/tests/accumulate-reuse.rs
deleted file mode 100644
index f8258c40..00000000
--- a/salsa-2022-tests/tests/accumulate-reuse.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-//! Accumulator re-use test.
-//!
-//! Tests behavior when a query's only inputs
-//! are the accumulated values from another query.
-
-use salsa_2022_tests::{HasLogger, Logger};
-
-use expect_test::expect;
-use test_log::test;
-
-#[salsa::jar(db = Db)]
-struct Jar(List, Integers, compute);
-
-trait Db: salsa::DbWithJar<Jar> + HasLogger {}
-
-#[salsa::input]
-struct List {
-    value: u32,
-    next: Option<List>,
-}
-
-#[salsa::accumulator]
-struct Integers(u32);
-
-#[salsa::tracked]
-fn compute(db: &dyn Db, input: List) -> u32 {
-    db.push_log(format!("compute({:?})", input,));
-
-    // always pushes 0
-    Integers::push(db, 0);
-
-    let result = if let Some(next) = input.next(db) {
-        let next_integers = compute::accumulated::<Integers>(db, next);
-        let v = input.value(db) + next_integers.iter().sum::<u32>();
-        v
-    } else {
-        input.value(db)
-    };
-
-    // return value changes
-    result
-}
-
-#[salsa::db(Jar)]
-#[derive(Default)]
-struct Database {
-    storage: salsa::Storage<Self>,
-    logger: Logger,
-}
-
-impl salsa::Database for Database {
-    fn salsa_event(&self, _event: salsa::Event) {}
-
-    fn salsa_runtime(&self) -> &salsa::Runtime {
-        self.storage.runtime()
-    }
-}
-
-impl Db for Database {}
-
-impl HasLogger for Database {
-    fn logger(&self) -> &Logger {
-        &self.logger
-    }
-}
-
-#[test]
-fn test1() {
-    let mut db = Database::default();
-
-    let l1 = List::new(&mut db, 1, None);
-    let l2 = List::new(&mut db, 2, Some(l1));
-
-    assert_eq!(compute(&db, l2), 2);
-    db.assert_logs(expect![[r#"
-        [
-            "compute(List(Id { value: 2 }))",
-            "compute(List(Id { value: 1 }))",
-        ]"#]]);
-
-    // When we mutate `l1`, we should re-execute `compute` for `l1`,
-    // 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);
-    assert_eq!(compute(&db, l2), 2);
-    db.assert_logs(expect![[r#"
-        [
-            "compute(List(Id { value: 2 }))",
-            "compute(List(Id { value: 1 }))",
-        ]"#]]);
-}
diff --git a/salsa-2022-tests/tests/accumulate.rs b/salsa-2022-tests/tests/accumulate.rs
deleted file mode 100644
index c12298df..00000000
--- a/salsa-2022-tests/tests/accumulate.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-//! Basic deletion test:
-//!
-//! * entities not created in a revision are deleted, as is any memoized data keyed on them.
-
-use salsa::DebugWithDb;
-use salsa_2022_tests::{HasLogger, Logger};
-
-use expect_test::expect;
-use test_log::test;
-
-#[salsa::jar(db = Db)]
-struct Jar(MyInput, Logs, push_logs, push_a_logs, push_b_logs);
-
-trait Db: salsa::DbWithJar<Jar> + HasLogger {}
-
-#[salsa::input]
-struct MyInput {
-    field_a: u32,
-    field_b: u32,
-}
-
-#[salsa::accumulator]
-struct Logs(String);
-
-#[salsa::tracked]
-#[allow(dead_code)]
-fn push_logs(db: &dyn Db, input: MyInput) {
-    db.push_log(format!(
-        "push_logs(a = {}, b = {})",
-        input.field_a(db),
-        input.field_b(db)
-    ));
-
-    // We don't invoke `push_a_logs` (or `push_b_logs`) with a value of 1 or less.
-    // This allows us to test what happens a change in inputs causes a function not to be called at all.
-    if input.field_a(db) > 1 {
-        push_a_logs(db, input);
-    }
-
-    if input.field_b(db) > 1 {
-        push_b_logs(db, input);
-    }
-}
-
-#[salsa::tracked]
-fn push_a_logs(db: &dyn Db, input: MyInput) {
-    let field_a = input.field_a(db);
-    db.push_log(format!("push_a_logs({})", field_a));
-
-    for i in 0..field_a {
-        Logs::push(db, format!("log_a({} of {})", i, field_a));
-    }
-}
-
-#[salsa::tracked]
-fn push_b_logs(db: &dyn Db, input: MyInput) {
-    let field_a = input.field_b(db);
-    db.push_log(format!("push_b_logs({})", field_a));
-
-    for i in 0..field_a {
-        Logs::push(db, format!("log_b({} of {})", i, field_a));
-    }
-}
-
-#[salsa::db(Jar)]
-#[derive(Default)]
-struct Database {
-    storage: salsa::Storage<Self>,
-    logger: Logger,
-}
-
-impl salsa::Database for Database {
-    fn salsa_event(&self, event: salsa::Event) {}
-
-    fn salsa_runtime(&self) -> &salsa::Runtime {
-        self.storage.runtime()
-    }
-}
-
-impl Db for Database {}
-
-impl HasLogger for Database {
-    fn logger(&self) -> &Logger {
-        &self.logger
-    }
-}
-
-#[test]
-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 logs = push_logs::accumulated::<Logs>(&db, input);
-    expect![[r#"
-        [
-            "log_b(0 of 3)",
-            "log_b(1 of 3)",
-            "log_b(2 of 3)",
-            "log_a(0 of 2)",
-            "log_a(1 of 2)",
-        ]"#]]
-    .assert_eq(&format!("{:#?}", logs));
-    db.assert_logs(expect![[r#"
-        [
-            "push_logs(a = 2, b = 3)",
-            "push_a_logs(2)",
-            "push_b_logs(3)",
-        ]"#]])
-}
-
-#[test]
-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 logs = push_logs::accumulated::<Logs>(&db, input);
-    expect![[r#"
-        [
-            "log_b(0 of 3)",
-            "log_b(1 of 3)",
-            "log_b(2 of 3)",
-            "log_a(0 of 2)",
-            "log_a(1 of 2)",
-        ]"#]]
-    .assert_eq(&format!("{:#?}", logs));
-    db.assert_logs(expect![[r#"
-        [
-            "push_logs(a = 2, b = 3)",
-            "push_a_logs(2)",
-            "push_b_logs(3)",
-        ]"#]]);
-
-    // Change to `a = 1`, which means `push_logs` does not call `push_a_logs` at all
-    input.set_field_a(&mut db, 1);
-    let logs = push_logs::accumulated::<Logs>(&db, input);
-    expect![[r#"
-        [
-            "log_b(0 of 3)",
-            "log_b(1 of 3)",
-            "log_b(2 of 3)",
-        ]"#]]
-    .assert_eq(&format!("{:#?}", logs));
-    db.assert_logs(expect![[r#"
-        [
-            "push_logs(a = 1, b = 3)",
-        ]"#]]);
-}
-
-#[test]
-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 logs = push_a_logs::accumulated::<Logs>(&db, input);
-    expect![[r#"
-        [
-            "log_a(0 of 2)",
-            "log_a(1 of 2)",
-        ]"#]]
-    .assert_eq(&format!("{:#?}", logs));
-    db.assert_logs(expect![[r#"
-        [
-            "push_a_logs(2)",
-        ]"#]]);
-
-    // 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);
-    let logs = push_a_logs::accumulated::<Logs>(&db, input);
-    expect![[r#"
-        [
-            "log_a(0 of 2)",
-            "log_a(1 of 2)",
-        ]"#]]
-    .assert_debug_eq(&logs);
-    db.assert_logs(expect!["[]"]);
-}
diff --git a/salsa-2022-tests/tests/lru.rs b/salsa-2022-tests/tests/lru.rs
new file mode 100644
index 00000000..21d2ecb4
--- /dev/null
+++ b/salsa-2022-tests/tests/lru.rs
@@ -0,0 +1,105 @@
+//! Test that a `tracked` fn with lru options
+//! compiles and executes successfully.
+
+use std::sync::{
+    atomic::{AtomicUsize, Ordering},
+    Arc,
+};
+
+use test_log::test;
+
+#[salsa::jar(db = Db)]
+struct Jar(MyInput, get_hot_potato, get_volatile);
+
+trait Db: salsa::DbWithJar<Jar> {}
+
+#[derive(Debug, PartialEq, Eq)]
+struct HotPotato(u32);
+
+thread_local! {
+    static N_POTATOES: AtomicUsize = AtomicUsize::new(0)
+}
+
+impl HotPotato {
+    fn new(id: u32) -> HotPotato {
+        N_POTATOES.with(|n| n.fetch_add(1, Ordering::SeqCst));
+        HotPotato(id)
+    }
+}
+
+impl Drop for HotPotato {
+    fn drop(&mut self) {
+        N_POTATOES.with(|n| n.fetch_sub(1, Ordering::SeqCst));
+    }
+}
+
+#[salsa::input(jar = Jar)]
+struct MyInput {
+    field: u32,
+}
+
+#[salsa::tracked(jar = Jar, lru = 32)]
+fn get_hot_potato(db: &dyn Db, input: MyInput) -> Arc<HotPotato> {
+    Arc::new(HotPotato::new(input.field(db)))
+}
+
+#[salsa::tracked(jar = Jar, lru = 32)]
+fn get_volatile(db: &dyn Db, _input: MyInput) -> usize {
+    static COUNTER: AtomicUsize = AtomicUsize::new(0);
+    db.salsa_runtime().report_untracked_read();
+    COUNTER.fetch_add(1, Ordering::SeqCst)
+}
+
+#[salsa::db(Jar)]
+#[derive(Default)]
+struct Database {
+    storage: salsa::Storage<Self>,
+}
+
+impl salsa::Database for Database {
+    fn salsa_runtime(&self) -> &salsa::Runtime {
+        self.storage.runtime()
+    }
+}
+
+impl Db for Database {}
+
+fn load_n_potatoes() -> usize {
+    N_POTATOES.with(|n| n.load(Ordering::SeqCst))
+}
+
+#[test]
+fn lru_works() {
+    let mut db = Database::default();
+    assert_eq!(load_n_potatoes(), 0);
+
+    for i in 0..128u32 {
+        let input = MyInput::new(&mut 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);
+    assert_eq!(load_n_potatoes(), 32);
+}
+
+#[test]
+fn lru_doesnt_break_volatile_queries() {
+    let mut db = Database::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();
+
+    // 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,
+    // but it's much better than inconsistent results from volatile queries!
+    for _ in 0..3 {
+        for (i, input) in inputs.iter().enumerate() {
+            let x = get_volatile(&db, *input);
+            assert_eq!(x, i);
+        }
+    }
+}
diff --git a/salsa-2022-tests/tests/mutate_in_place.rs b/salsa-2022-tests/tests/mutate_in_place.rs
index 4d6c1a5c..83166c96 100644
--- a/salsa-2022-tests/tests/mutate_in_place.rs
+++ b/salsa-2022-tests/tests/mutate_in_place.rs
@@ -3,7 +3,6 @@
 
 use salsa_2022_tests::{HasLogger, Logger};
 
-use expect_test::expect;
 use test_log::test;
 
 #[salsa::jar(db = Db)]