diff --git a/components/salsa-macro-rules/src/setup_interned_struct.rs b/components/salsa-macro-rules/src/setup_interned_struct.rs index 9f96a62de..da346fa5e 100644 --- a/components/salsa-macro-rules/src/setup_interned_struct.rs +++ b/components/salsa-macro-rules/src/setup_interned_struct.rs @@ -118,17 +118,17 @@ macro_rules! setup_interned_struct { } } - impl salsa::plumbing::interned::Configuration for $StructWithStatic { + impl $zalsa::interned::Configuration for $StructWithStatic { const DEBUG_NAME: &'static str = stringify!($Struct); type Fields<'a> = $StructDataIdent<'a>; type Struct<'db> = $Struct< $($db_lt_arg)? >; fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> { - use salsa::plumbing::FromId; + use $zalsa::FromId; $Struct(<$Id>::from_id(id), std::marker::PhantomData) } fn deref_struct(s: Self::Struct<'_>) -> salsa::Id { - use salsa::plumbing::AsId; + use $zalsa::AsId; s.0.as_id() } } @@ -218,7 +218,7 @@ macro_rules! setup_interned_struct { // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database` $Db: ?Sized + $zalsa::Database, { - let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), self); + let fields = $Configuration::ingredient(db).data_zalsa(db.zalsa(), <$StructWithStatic as $zalsa::interned::Configuration>::deref_struct(self)); $zalsa::maybe_clone!( $field_option, $field_ty, @@ -230,7 +230,7 @@ macro_rules! setup_interned_struct { /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl) pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { $zalsa::with_attached_database(|db| { - let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), this); + let fields = $Configuration::ingredient(db).data_zalsa(db.zalsa(), <$StructWithStatic as $zalsa::interned::Configuration>::deref_struct(this)); let mut f = f.debug_struct(stringify!($Struct)); $( let f = f.field(stringify!($field_id), &fields.$field_index); diff --git a/components/salsa-macro-rules/src/setup_tracked_fn.rs b/components/salsa-macro-rules/src/setup_tracked_fn.rs index 60f46ca89..9898d1461 100644 --- a/components/salsa-macro-rules/src/setup_tracked_fn.rs +++ b/components/salsa-macro-rules/src/setup_tracked_fn.rs @@ -68,7 +68,6 @@ macro_rules! setup_tracked_fn { $Configuration:ident, $InternedData:ident, $FN_CACHE:ident, - $INTERN_CACHE:ident, $inner:ident, ] ) => { @@ -100,9 +99,6 @@ macro_rules! setup_tracked_fn { std::marker::PhantomData<&$db_lt $zalsa::interned::Value<$Configuration>>, ); - static $INTERN_CACHE: $zalsa::IngredientCache<$zalsa::interned::IngredientImpl<$Configuration>> = - $zalsa::IngredientCache::new(); - impl $zalsa::SalsaStructInDb for $InternedData<'_> { type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex; @@ -164,10 +160,11 @@ macro_rules! setup_tracked_fn { db: &dyn $Db, ) -> &$zalsa::interned::IngredientImpl<$Configuration> { let zalsa = db.zalsa(); - $INTERN_CACHE.get_or_create(zalsa, || { + let index = $FN_CACHE.get_or_create_index(zalsa, || { ::zalsa_register_downcaster(db); - zalsa.add_or_lookup_jar_by_type::<$Configuration>().successor(0) - }) + db.zalsa().add_or_lookup_jar_by_type::<$Configuration>() + }).successor(0); + zalsa.lookup_ingredient(index).assert_type() } } } @@ -219,12 +216,13 @@ macro_rules! setup_tracked_fn { $($cycle_recovery_fn)*(db, value, count, $($input_id),*) } - fn id_to_input<$db_lt>(db: &$db_lt Self::DbView, key: salsa::Id) -> Self::Input<$db_lt> { + #[inline] + fn id_to_input<$db_lt>(db: &$db_lt Self::DbView, zalsa: &$db_lt $zalsa::Zalsa, key: salsa::Id) -> Self::Input<$db_lt> { $zalsa::macro_if! { if $needs_interner { - $Configuration::intern_ingredient(db).data(db.as_dyn_database(), key).clone() + $Configuration::intern_ingredient(db).data_zalsa(zalsa, key).clone() } else { - $zalsa::FromIdWithDb::from_id(key, db) + $zalsa::FromIdWithDb::from_id_zalsa(key, zalsa) } } } diff --git a/components/salsa-macros/src/supertype.rs b/components/salsa-macros/src/supertype.rs index 4ffcf6c4d..cbd71ae4e 100644 --- a/components/salsa-macros/src/supertype.rs +++ b/components/salsa-macros/src/supertype.rs @@ -61,8 +61,12 @@ fn enum_impl(enum_item: syn::ItemEnum) -> syn::Result { #[inline] fn from_id(__id: zalsa::Id, __db: &(impl ?Sized + zalsa::Database)) -> Self { let __zalsa = __db.zalsa(); - let __type_id = __zalsa.lookup_page_type_id(__id); - ::cast(__id, __type_id).expect("invalid enum variant") + Self::from_id_zalsa( __id,__zalsa) + } + #[inline] + fn from_id_zalsa(id: zalsa::Id, zalsa: &zalsa::Zalsa) -> Self { + let type_id = zalsa.lookup_page_type_id(id); + ::cast(id, type_id).expect("invalid enum variant") } } }; diff --git a/components/salsa-macros/src/tracked_fn.rs b/components/salsa-macros/src/tracked_fn.rs index 66f1fc82c..0255ee913 100644 --- a/components/salsa-macros/src/tracked_fn.rs +++ b/components/salsa-macros/src/tracked_fn.rs @@ -100,7 +100,6 @@ impl Macro { let Configuration = self.hygiene.ident("Configuration"); let InternedData = self.hygiene.ident("InternedData"); let FN_CACHE = self.hygiene.ident("FN_CACHE"); - let INTERN_CACHE = self.hygiene.ident("INTERN_CACHE"); let inner = &inner_fn.sig.ident; let function_type = function_type(&item); @@ -175,7 +174,6 @@ impl Macro { #Configuration, #InternedData, #FN_CACHE, - #INTERN_CACHE, #inner, ] }], diff --git a/src/function.rs b/src/function.rs index d483323c2..8e213efa2 100644 --- a/src/function.rs +++ b/src/function.rs @@ -60,7 +60,7 @@ pub trait Configuration: Any { /// Convert from the id used internally to the value that execute is expecting. /// This is a no-op if the input to the function is a salsa struct. - fn id_to_input(db: &Self::DbView, key: Id) -> Self::Input<'_>; + fn id_to_input<'db>(db: &'db Self::DbView, zalsa: &'db Zalsa, key: Id) -> Self::Input<'db>; /// Invoked when we need to compute the value for the given key, either because we've never /// computed it before or because the old one relied on inputs that have changed. diff --git a/src/function/diff_outputs.rs b/src/function/diff_outputs.rs index 0ca7f5b9f..011ea8701 100644 --- a/src/function/diff_outputs.rs +++ b/src/function/diff_outputs.rs @@ -24,7 +24,6 @@ where key: DatabaseKeyIndex, old_memo: &Memo>, revisions: &mut QueryRevisions, - provisional: bool, ) { // Iterate over the outputs of the `old_memo` and put them into a hashset let mut old_outputs: FxHashSet<_> = old_memo.revisions.origin.outputs().collect(); @@ -35,14 +34,17 @@ where old_outputs.remove(&new_output); } - if !old_outputs.is_empty() { - // Remove the outputs that are no longer present in the current revision - // to prevent that the next revision is seeded with a id mapping that no longer exists. - revisions.tracked_struct_ids.retain(|&k, &mut value| { - !old_outputs.contains(&DatabaseKeyIndex::new(k.ingredient_index(), value)) - }); + if old_outputs.is_empty() { + return; } + // Remove the outputs that are no longer present in the current revision + // to prevent that the next revision is seeded with a id mapping that no longer exists. + revisions.tracked_struct_ids.retain(|&k, &mut value| { + !old_outputs.contains(&DatabaseKeyIndex::new(k.ingredient_index(), value)) + }); + + let provisional = !revisions.cycle_heads.is_empty(); for old_output in old_outputs { Self::report_stale_output(zalsa, db, key, old_output, provisional); } diff --git a/src/function/execute.rs b/src/function/execute.rs index 557463817..602e4d83b 100644 --- a/src/function/execute.rs +++ b/src/function/execute.rs @@ -25,9 +25,7 @@ where opt_old_memo: Option<&Memo>>, ) -> &'db Memo> { let zalsa = db.zalsa(); - let revision_now = zalsa.current_revision(); let database_key_index = active_query.database_key_index; - let id = database_key_index.key_index(); tracing::info!("{:?}: executing query", database_key_index); @@ -37,17 +35,132 @@ where }) }); + let id = database_key_index.key_index(); let memo_ingredient_index = self.memo_ingredient_index(zalsa, id); - let mut iteration_count: u32 = 0; - let mut fell_back = false; + let (new_value, mut revisions) = if C::CYCLE_STRATEGY == CycleRecoveryStrategy::Fixpoint { + let zalsa_local = active_query.zalsa_local(); + let mut iteration_count: u32 = 0; + let mut fell_back = false; - // Our provisional value from the previous iteration, when doing fixpoint iteration. - // Initially it's set to None, because the initial provisional value is created lazily, - // only when a cycle is actually encountered. - let mut opt_last_provisional: Option<&Memo<::Output<'db>>> = None; + // Our provisional value from the previous iteration, when doing fixpoint iteration. + // Initially it's set to None, because the initial provisional value is created lazily, + // only when a cycle is actually encountered. + let mut opt_last_provisional: Option<&Memo<::Output<'db>>> = None; + + loop { + // If we already executed this query once, then use the tracked-struct ids from the + // previous execution as the starting point for the new one. + if let Some(old_memo) = opt_old_memo { + active_query.seed_tracked_struct_ids(&old_memo.revisions.tracked_struct_ids); + } + + // Query was not previously executed, or value is potentially + // stale, or value is absent. Let's execute! + let mut new_value = C::execute(db, C::id_to_input(db, zalsa, id)); + let mut revisions = active_query.pop(); + + // Did the new result we got depend on our own provisional value, in a cycle? + if revisions.cycle_heads.contains(&database_key_index) { + let last_provisional_value = if let Some(last_provisional) = + opt_last_provisional + { + // We have a last provisional value from our previous time around the loop. + last_provisional.value.as_ref() + } else { + // This is our first time around the loop; a provisional value must have been + // inserted into the memo table when the cycle was hit, so let's pull our + // initial provisional value from there. + let Some(memo) = + self.get_memo_from_table_for(zalsa, id, memo_ingredient_index) + else { + unreachable!("{database_key_index:#?} is a cycle head, but no provisional memo found") + }; + debug_assert!(memo.may_be_provisional()); + memo.value.as_ref() + }; + // SAFETY: The `LRU` does not run mid-execution, so the value remains filled + let last_provisional_value = + unsafe { last_provisional_value.unwrap_unchecked() }; + tracing::debug!( + "{database_key_index:?}: execute: \ + I am a cycle head, comparing last provisional value with new value" + ); + // If the new result is equal to the last provisional result, the cycle has + // converged and we are done. + if !C::values_equal(&new_value, last_provisional_value) { + if fell_back { + // We fell back to a value last iteration, but the fallback didn't result + // in convergence. We only have bad options here: continue iterating + // (ignoring the request to fall back), or forcibly use the fallback and + // leave the cycle in an inconsistent state (we'll be using a value for + // this query that it doesn't evaluate to, given its inputs). Maybe we'll + // have to go with the latter, but for now let's panic and see if real use + // cases need non-converging fallbacks. + panic!("{database_key_index:?}: execute: fallback did not converge"); + } + // We are in a cycle that hasn't converged; ask the user's + // cycle-recovery function what to do: + match C::recover_from_cycle( + db, + &new_value, + iteration_count, + C::id_to_input(db, zalsa, id), + ) { + crate::CycleRecoveryAction::Iterate => { + tracing::debug!( + "{database_key_index:?}: execute: \ + iterate again" + ); + } + crate::CycleRecoveryAction::Fallback(fallback_value) => { + tracing::debug!( + "{database_key_index:?}: execute: \ + user cycle_fn says to fall back" + ); + new_value = fallback_value; + // We have to insert the fallback value for this query and then iterate + // one more time to fill in correct values for everything else in the + // cycle based on it; then we'll re-insert it as final value. + fell_back = true; + } + } + // `iteration_count` can't overflow as we check it against `MAX_ITERATIONS` + // which is less than `u32::MAX`. + iteration_count += 1; + if iteration_count > MAX_ITERATIONS { + panic!("{database_key_index:?}: execute: too many cycle iterations"); + } + db.salsa_event(&|| { + Event::new(EventKind::WillIterateCycle { + database_key: database_key_index, + iteration_count, + fell_back, + }) + }); + revisions + .cycle_heads + .update_iteration_count(database_key_index, iteration_count); + opt_last_provisional = Some(self.insert_memo( + zalsa, + id, + Memo::new(Some(new_value), zalsa.current_revision(), revisions), + memo_ingredient_index, + )); + + active_query = zalsa_local.push_query(database_key_index, iteration_count); + + continue; + } + tracing::debug!( + "{database_key_index:?}: execute: fixpoint iteration has a final value" + ); + revisions.cycle_heads.remove(&database_key_index); + } - loop { + break (new_value, revisions); + } + } else { // If we already executed this query once, then use the tracked-struct ids from the // previous execution as the starting point for the new one. if let Some(old_memo) = opt_old_memo { @@ -56,130 +169,30 @@ where // Query was not previously executed, or value is potentially // stale, or value is absent. Let's execute! - let mut new_value = C::execute(db, C::id_to_input(db, id)); - let mut revisions = active_query.pop(); - - // Did the new result we got depend on our own provisional value, in a cycle? - if C::CYCLE_STRATEGY == CycleRecoveryStrategy::Fixpoint - && revisions.cycle_heads.contains(&database_key_index) - { - let last_provisional_value = if let Some(last_provisional) = opt_last_provisional { - // We have a last provisional value from our previous time around the loop. - last_provisional.value.as_ref() - } else { - // This is our first time around the loop; a provisional value must have been - // inserted into the memo table when the cycle was hit, so let's pull our - // initial provisional value from there. - let memo = - self.get_memo_from_table_for(zalsa, id, memo_ingredient_index) - .unwrap_or_else(|| panic!("{database_key_index:#?} is a cycle head, but no provisional memo found")); - debug_assert!(memo.may_be_provisional()); - memo.value.as_ref() - }; - // SAFETY: The `LRU` does not run mid-execution, so the value remains filled - let last_provisional_value = unsafe { last_provisional_value.unwrap_unchecked() }; - tracing::debug!( - "{database_key_index:?}: execute: \ - I am a cycle head, comparing last provisional value with new value" - ); - // If the new result is equal to the last provisional result, the cycle has - // converged and we are done. - if !C::values_equal(&new_value, last_provisional_value) { - if fell_back { - // We fell back to a value last iteration, but the fallback didn't result - // in convergence. We only have bad options here: continue iterating - // (ignoring the request to fall back), or forcibly use the fallback and - // leave the cycle in an inconsistent state (we'll be using a value for - // this query that it doesn't evaluate to, given its inputs). Maybe we'll - // have to go with the latter, but for now let's panic and see if real use - // cases need non-converging fallbacks. - panic!("{database_key_index:?}: execute: fallback did not converge"); - } - // We are in a cycle that hasn't converged; ask the user's - // cycle-recovery function what to do: - match C::recover_from_cycle( - db, - &new_value, - iteration_count, - C::id_to_input(db, id), - ) { - crate::CycleRecoveryAction::Iterate => { - tracing::debug!("{database_key_index:?}: execute: iterate again"); - } - crate::CycleRecoveryAction::Fallback(fallback_value) => { - tracing::debug!( - "{database_key_index:?}: execute: user cycle_fn says to fall back" - ); - new_value = fallback_value; - // We have to insert the fallback value for this query and then iterate - // one more time to fill in correct values for everything else in the - // cycle based on it; then we'll re-insert it as final value. - fell_back = true; - } - } - // `iteration_count` can't overflow as we check it against `MAX_ITERATIONS` - // which is less than `u32::MAX`. - iteration_count += 1; - if iteration_count > MAX_ITERATIONS { - panic!("{database_key_index:?}: execute: too many cycle iterations"); - } - db.salsa_event(&|| { - Event::new(EventKind::WillIterateCycle { - database_key: database_key_index, - iteration_count, - fell_back, - }) - }); - revisions - .cycle_heads - .update_iteration_count(database_key_index, iteration_count); - opt_last_provisional = Some(self.insert_memo( - zalsa, - id, - Memo::new(Some(new_value), revision_now, revisions), - memo_ingredient_index, - )); - - active_query = db - .zalsa_local() - .push_query(database_key_index, iteration_count); - - continue; - } - tracing::debug!( - "{database_key_index:?}: execute: fixpoint iteration has a final value" - ); - revisions.cycle_heads.remove(&database_key_index); - } + let new_value = C::execute(db, C::id_to_input(db, zalsa, id)); + let revisions = active_query.pop(); - tracing::debug!("{database_key_index:?}: execute: result.revisions = {revisions:#?}"); + (new_value, revisions) + }; - if let Some(old_memo) = opt_old_memo { - // If the new value is equal to the old one, then it didn't - // really change, even if some of its inputs have. So we can - // "backdate" its `changed_at` revision to be the same as the - // old value. - self.backdate_if_appropriate(old_memo, &mut revisions, &new_value); - - // Diff the new outputs with the old, to discard any no-longer-emitted - // outputs and update the tracked struct IDs for seeding the next revision. - let provisional = !revisions.cycle_heads.is_empty(); - self.diff_outputs( - zalsa, - db, - database_key_index, - old_memo, - &mut revisions, - provisional, - ); - } + tracing::debug!("{database_key_index:?}: execute: result.revisions = {revisions:#?}"); + if let Some(old_memo) = opt_old_memo { + // If the new value is equal to the old one, then it didn't + // really change, even if some of its inputs have. So we can + // "backdate" its `changed_at` revision to be the same as the + // old value. + self.backdate_if_appropriate(old_memo, &mut revisions, &new_value); - return self.insert_memo( - zalsa, - id, - Memo::new(Some(new_value), revision_now, revisions), - memo_ingredient_index, - ); + // Diff the new outputs with the old, to discard any no-longer-emitted + // outputs and update the tracked struct IDs for seeding the next revision. + self.diff_outputs(zalsa, db, database_key_index, old_memo, &mut revisions); } + + self.insert_memo( + zalsa, + id, + Memo::new(Some(new_value), zalsa.current_revision(), revisions), + memo_ingredient_index, + ) } } diff --git a/src/function/memo.rs b/src/function/memo.rs index eaa315cb2..6aad70e90 100644 --- a/src/function/memo.rs +++ b/src/function/memo.rs @@ -10,7 +10,7 @@ use crate::function::{Configuration, IngredientImpl}; use crate::key::DatabaseKeyIndex; use crate::revision::AtomicRevision; use crate::table::memo::MemoTable; -use crate::zalsa::{MemoIngredientIndex, Zalsa}; +use crate::zalsa::{MemoIngredientIndex, Zalsa, ZalsaDatabase}; use crate::zalsa_local::{QueryOrigin, QueryRevisions}; use crate::{Event, EventKind, Id, Revision}; @@ -113,7 +113,9 @@ impl IngredientImpl { key: Id, ) -> Option> { match C::CYCLE_STRATEGY { - CycleRecoveryStrategy::Fixpoint => Some(C::cycle_initial(db, C::id_to_input(db, key))), + CycleRecoveryStrategy::Fixpoint => { + Some(C::cycle_initial(db, C::id_to_input(db, db.zalsa(), key))) + } CycleRecoveryStrategy::Panic => None, } } diff --git a/src/function/specify.rs b/src/function/specify.rs index 4d5c7169a..a671ce4ad 100644 --- a/src/function/specify.rs +++ b/src/function/specify.rs @@ -77,14 +77,7 @@ where let memo_ingredient_index = self.memo_ingredient_index(zalsa, key); if let Some(old_memo) = self.get_memo_from_table_for(zalsa, key, memo_ingredient_index) { self.backdate_if_appropriate(old_memo, &mut revisions, &value); - self.diff_outputs( - zalsa, - db, - database_key_index, - old_memo, - &mut revisions, - false, - ); + self.diff_outputs(zalsa, db, database_key_index, old_memo, &mut revisions); } let memo = Memo { diff --git a/src/id.rs b/src/id.rs index 87ca9609a..257d6b60e 100644 --- a/src/id.rs +++ b/src/id.rs @@ -72,12 +72,14 @@ pub trait FromId: AsId + Copy + Eq + Hash { } impl AsId for Id { + #[inline] fn as_id(&self) -> Id { *self } } impl FromId for Id { + #[inline] fn from_id(id: Id) -> Self { id } @@ -87,11 +89,17 @@ impl FromId for Id { /// so they use this trait instead, that has a blanket implementation for `FromId`. pub trait FromIdWithDb: AsId + Copy + Eq + Hash { fn from_id(id: Id, db: &(impl ?Sized + Database)) -> Self; + #[doc(hidden)] + fn from_id_zalsa(id: Id, zalsa: &crate::zalsa::Zalsa) -> Self; } impl FromIdWithDb for T { #[inline] - fn from_id(id: Id, _db: &(impl ?Sized + Database)) -> Self { + fn from_id(id: Id, _: &(impl ?Sized + Database)) -> Self { + FromId::from_id(id) + } + #[inline] + fn from_id_zalsa(id: Id, _: &crate::zalsa::Zalsa) -> Self { FromId::from_id(id) } } diff --git a/src/input.rs b/src/input.rs index 4d4405d74..d43c6c63f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -108,7 +108,7 @@ impl IngredientImpl { }) }); - FromIdWithDb::from_id(id, db) + FromIdWithDb::from_id_zalsa(id, zalsa) } /// Change the value of the field `field_index` to a new value. diff --git a/src/interned.rs b/src/interned.rs index 7be6a3420..855b73537 100644 --- a/src/interned.rs +++ b/src/interned.rs @@ -335,7 +335,11 @@ where /// Rarely used since end-users generally carry a struct with a pointer directly /// to the interned item. pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> { - let zalsa = db.zalsa(); + self.data_zalsa(db.zalsa(), id) + } + + #[doc(hidden)] + pub fn data_zalsa<'db>(&'db self, zalsa: &'db Zalsa, id: Id) -> &'db C::Fields<'db> { let internal_data = zalsa.table().get::>(id); let last_changed_revision = zalsa.last_changed_revision(internal_data.durability()); @@ -350,7 +354,7 @@ where /// Lookup the fields from an interned struct. /// Note that this is not "leaking" since no dependency edge is required. pub fn fields<'db>(&'db self, db: &'db dyn Database, s: C::Struct<'db>) -> &'db C::Fields<'db> { - self.data(db, C::deref_struct(s)) + self.data_zalsa(db.zalsa(), C::deref_struct(s)) } pub fn reset(&mut self, db: &mut dyn Database) { diff --git a/src/zalsa.rs b/src/zalsa.rs index bb86c5233..0a78950ae 100644 --- a/src/zalsa.rs +++ b/src/zalsa.rs @@ -90,6 +90,7 @@ impl IngredientIndex { self.0 as usize } + #[inline] pub fn successor(self, index: usize) -> Self { IngredientIndex(self.0 + 1 + index as u32) } @@ -200,15 +201,6 @@ impl Zalsa { unsafe { self.table().syncs(id, self.current_revision()) } } - #[inline] - pub(crate) fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient { - let index = index.as_usize(); - self.ingredients_vec - .get(index) - .unwrap_or_else(|| panic!("index `{index}` is uninitialized")) - .as_ref() - } - pub(crate) fn ingredient_index_for_memo( &self, struct_ingredient_index: IngredientIndex, @@ -335,6 +327,17 @@ impl Zalsa { (ingredient.as_mut(), &mut self.runtime) } + /// **NOT SEMVER STABLE** + #[doc(hidden)] + #[inline] + pub fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient { + let index = index.as_usize(); + self.ingredients_vec + .get(index) + .unwrap_or_else(|| panic!("index `{index}` is uninitialized")) + .as_ref() + } + /// **NOT SEMVER STABLE** #[doc(hidden)] pub fn current_revision(&self) -> Revision { @@ -420,6 +423,7 @@ where phantom: PhantomData, } } + /// Get a reference to the ingredient in the database. /// If the ingredient is not already in the cache, it will be created. #[inline(always)] diff --git a/src/zalsa_local.rs b/src/zalsa_local.rs index 70d48e506..df168dc26 100644 --- a/src/zalsa_local.rs +++ b/src/zalsa_local.rs @@ -93,7 +93,7 @@ impl ZalsaLocal { let mut query_stack = self.query_stack.borrow_mut(); query_stack.push_new_query(database_key_index, iteration_count); ActiveQueryGuard { - local_state: self, + zalsa_local: self, database_key_index, #[cfg(debug_assertions)] push_len: query_stack.len(), @@ -462,16 +462,16 @@ impl QueryEdges { /// the query from the stack -- in the case of unwinding, the guard's /// destructor will also remove the query. pub(crate) struct ActiveQueryGuard<'me> { - local_state: &'me ZalsaLocal, + zalsa_local: &'me ZalsaLocal, #[cfg(debug_assertions)] push_len: usize, pub(crate) database_key_index: DatabaseKeyIndex, } -impl ActiveQueryGuard<'_> { +impl<'me> ActiveQueryGuard<'me> { /// Initialize the tracked struct ids with the values from the prior execution. pub(crate) fn seed_tracked_struct_ids(&self, tracked_struct_ids: &IdentityMap) { - self.local_state.with_query_stack(|stack| { + self.zalsa_local.with_query_stack(|stack| { #[cfg(debug_assertions)] assert_eq!(stack.len(), self.push_len); let frame = stack.last_mut().unwrap(); @@ -482,7 +482,7 @@ impl ActiveQueryGuard<'_> { /// Invoked when the query has successfully completed execution. fn complete(self) -> QueryRevisions { - let query = self.local_state.with_query_stack(|stack| { + let query = self.zalsa_local.with_query_stack(|stack| { stack.pop_into_revisions( self.database_key_index, #[cfg(debug_assertions)] @@ -500,11 +500,15 @@ impl ActiveQueryGuard<'_> { pub(crate) fn pop(self) -> QueryRevisions { self.complete() } + + pub(crate) fn zalsa_local(&self) -> &'me ZalsaLocal { + self.zalsa_local + } } impl Drop for ActiveQueryGuard<'_> { fn drop(&mut self) { - self.local_state.with_query_stack(|stack| { + self.zalsa_local.with_query_stack(|stack| { stack.pop( self.database_key_index, #[cfg(debug_assertions)] diff --git a/tests/interned-structs_self_ref.rs b/tests/interned-structs_self_ref.rs index 0a39049cd..2ee1ce580 100644 --- a/tests/interned-structs_self_ref.rs +++ b/tests/interned-structs_self_ref.rs @@ -164,20 +164,20 @@ const _: () = { where Db_: ?Sized + zalsa_::Database, { - let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), self); + let fields = Configuration_::ingredient(db).data_zalsa(db.zalsa(), self.0); std::clone::Clone::clone((&fields.0)) } fn other(self, db: &'db Db_) -> InternedString<'db> where Db_: ?Sized + zalsa_::Database, { - let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), self); + let fields = Configuration_::ingredient(db).data_zalsa(db.zalsa(), self.0); std::clone::Clone::clone((&fields.1)) } #[doc = r" Default debug formatting for this struct (may be useful if you define your own `Debug` impl)"] pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { zalsa_::with_attached_database(|db| { - let fields = Configuration_::ingredient(db).fields(db.as_dyn_database(), this); + let fields = Configuration_::ingredient(db).data_zalsa(db.zalsa(), this.0); let mut f = f.debug_struct("InternedString"); let f = f.field("data", &fields.0); let f = f.field("other", &fields.1);