introduce IdLookup trait

We are going to need it for tracked functions.
This commit is contained in:
Niko Matsakis 2024-05-18 06:12:44 -04:00
parent 8d0f8fccbf
commit cf2fa671f5
4 changed files with 89 additions and 1 deletions

View file

@ -64,6 +64,7 @@ impl TrackedStruct {
let tracked_struct_in_db_impl = self.tracked_struct_in_db_impl(); let tracked_struct_in_db_impl = self.tracked_struct_in_db_impl();
let update_impl = self.update_impl(); let update_impl = self.update_impl();
let as_id_impl = self.as_id_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(); let as_debug_with_db_impl = self.as_debug_with_db_impl();
Ok(quote! { Ok(quote! {
#config_struct #config_struct
@ -75,6 +76,7 @@ impl TrackedStruct {
#tracked_struct_in_db_impl #tracked_struct_in_db_impl
#update_impl #update_impl
#as_id_impl #as_id_impl
#id_lookup_impl
#as_debug_with_db_impl #as_debug_with_db_impl
}) })
} }
@ -327,6 +329,37 @@ impl TrackedStruct {
} }
} }
/// Implementation of `IdLookup`.
pub(crate) fn id_lookup_impl(&self) -> Option<syn::ItemImpl> {
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`. /// Implementation of `SalsaStructInDb`.
fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl { fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl {
let (ident, parameters, _, type_generics, where_clause) = self.the_ident_and_generics(); let (ident, parameters, _, type_generics, where_clause) = self.the_ident_and_generics();

View file

@ -65,7 +65,7 @@ impl From<Id> 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 { pub trait AsId: Sized + Copy + Eq + Hash + Debug {
fn as_id(self) -> Id; fn as_id(self) -> Id;
fn from_id(id: Id) -> Self; fn from_id(id: Id) -> Self;
@ -92,3 +92,38 @@ impl AsId for () {
assert_eq!(0, id.as_u32()); 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<DB> {
/// 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<DB, ID> IdLookup<DB> 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)
}
}

View file

@ -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<C> {
self.struct_map.get(runtime, id)
}
/// Deletes the given entities. This is used after a query `Q` executes and we can compare /// 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 /// 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 /// `E_prev` it produced in the last revision. Any missing entities `E_prev - E_new` can be

View file

@ -148,6 +148,16 @@ where
Update::Outdated(UpdateRef { guard: data }) 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<C> {
Self::get_from_map(&self.map, runtime, id)
}
/// Helper function, provides shared functionality for [`StructMapView`][] /// Helper function, provides shared functionality for [`StructMapView`][]
/// ///
/// # Panics /// # Panics