diff --git a/components/salsa-2022-macros/src/tracked_struct.rs b/components/salsa-2022-macros/src/tracked_struct.rs index 01bc2314..ab03eaee 100644 --- a/components/salsa-2022-macros/src/tracked_struct.rs +++ b/components/salsa-2022-macros/src/tracked_struct.rs @@ -64,6 +64,7 @@ impl TrackedStruct { let tracked_struct_in_db_impl = self.tracked_struct_in_db_impl(); let update_impl = self.update_impl(); let as_id_impl = self.as_id_impl(); + let id_lookup_impl = self.id_lookup_impl(); let as_debug_with_db_impl = self.as_debug_with_db_impl(); Ok(quote! { #config_struct @@ -75,6 +76,7 @@ impl TrackedStruct { #tracked_struct_in_db_impl #update_impl #as_id_impl + #id_lookup_impl #as_debug_with_db_impl }) } @@ -327,6 +329,37 @@ impl TrackedStruct { } } + /// Implementation of `IdLookup`. + pub(crate) fn id_lookup_impl(&self) -> Option { + match self.the_struct_kind() { + TheStructKind::Id => None, + TheStructKind::Pointer(db_lt) => { + let (ident, parameters, _, type_generics, where_clause) = + self.the_ident_and_generics(); + let db = syn::Ident::new("DB", ident.span()); + let jar_ty = self.jar_ty(); + let tracked_struct_ingredient = self.tracked_struct_ingredient_index(); + Some(parse_quote_spanned! { ident.span() => + impl<#db, #parameters> salsa::id::IdLookup<& #db_lt #db> for #ident #type_generics + where + #db: ?Sized + salsa::DbWithJar<#jar_ty>, + #where_clause + { + fn into_id(self) -> salsa::Id { + unsafe { &*self.0 }.id() + } + + fn lookup_id(id: salsa::Id, db: & #db_lt DB) -> Self { + let (jar, runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db); + let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor<#ident #type_generics>>::ingredient(jar); + Self(ingredients.#tracked_struct_ingredient.lookup_struct(runtime, id), std::marker::PhantomData) + } + } + }) + } + } + } + /// Implementation of `SalsaStructInDb`. fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl { let (ident, parameters, _, type_generics, where_clause) = self.the_ident_and_generics(); diff --git a/components/salsa-2022/src/id.rs b/components/salsa-2022/src/id.rs index c1db29b3..155004f3 100644 --- a/components/salsa-2022/src/id.rs +++ b/components/salsa-2022/src/id.rs @@ -65,7 +65,7 @@ impl From for usize { } } -/// Trait for types that can be interconverted to a salsa Id; +/// Internal Salsa trait for types that can be interconverted to a salsa Id; pub trait AsId: Sized + Copy + Eq + Hash + Debug { fn as_id(self) -> Id; fn from_id(id: Id) -> Self; @@ -92,3 +92,38 @@ impl AsId for () { assert_eq!(0, id.as_u32()); } } + +/// Internal Salsa trait for types that have a salsa id but require looking +/// up in the database to find it. This is different from +/// [`AsId`][] where what we have is literally a *newtype* +/// for an `Id`. +pub trait IdLookup { + /// Convert to an `Id` + fn into_id(self) -> Id; + + /// Lookup from an `Id` to get an instance of the type. + /// + /// # Panics + /// + /// This fn may panic if the value with this id has not been + /// produced in this revision already (e.g., for a tracked + /// struct, the function will panic if the tracked struct + /// has not yet been created in this revision). Salsa's + /// dependency tracking typically ensures this does not + /// occur, but it is possible for a user to violate this + /// rule. + fn lookup_id(id: Id, db: DB) -> Self; +} + +impl IdLookup for ID +where + ID: AsId, +{ + fn into_id(self) -> Id { + self.as_id() + } + + fn lookup_id(id: Id, _db: DB) -> Self { + Self::from_id(id) + } +} diff --git a/components/salsa-2022/src/tracked_struct.rs b/components/salsa-2022/src/tracked_struct.rs index 0540ece4..21429792 100644 --- a/components/salsa-2022/src/tracked_struct.rs +++ b/components/salsa-2022/src/tracked_struct.rs @@ -341,6 +341,16 @@ where } } + /// Given the id of a tracked struct created in this revision, + /// returns a pointer to the struct. + /// + /// # Panics + /// + /// If the struct has not been created in this revision. + pub fn lookup_struct<'db>(&'db self, runtime: &'db Runtime, id: Id) -> &'db ValueStruct { + self.struct_map.get(runtime, id) + } + /// Deletes the given entities. This is used after a query `Q` executes and we can compare /// the entities `E_now` that it produced in this revision vs the entities /// `E_prev` it produced in the last revision. Any missing entities `E_prev - E_new` can be diff --git a/components/salsa-2022/src/tracked_struct/struct_map.rs b/components/salsa-2022/src/tracked_struct/struct_map.rs index b9607b05..399eec27 100644 --- a/components/salsa-2022/src/tracked_struct/struct_map.rs +++ b/components/salsa-2022/src/tracked_struct/struct_map.rs @@ -148,6 +148,16 @@ where Update::Outdated(UpdateRef { guard: data }) } + /// Lookup an existing tracked struct from the map. + /// + /// # Panics + /// + /// * If the value is not present in the map. + /// * If the value has not been updated in this revision. + pub fn get<'db>(&'db self, runtime: &'db Runtime, id: Id) -> &'db ValueStruct { + Self::get_from_map(&self.map, runtime, id) + } + /// Helper function, provides shared functionality for [`StructMapView`][] /// /// # Panics