Skip to content

Commit

Permalink
Introduce Salsa enums
Browse files Browse the repository at this point in the history
Or "supertypes" as they are called in salsa-rs#578.

They are of the form:
```
enum Enum {
    Input(Input),
    Interned(Interned),
    ...
}
```
  • Loading branch information
ChayimFriedman2 committed Jan 7, 2025
1 parent b3d2b28 commit 927dfe8
Show file tree
Hide file tree
Showing 28 changed files with 736 additions and 182 deletions.
3 changes: 2 additions & 1 deletion components/salsa-macro-rules/src/setup_accumulator_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ macro_rules! setup_accumulator_impl {

fn $ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<$Struct> {
$CACHE.get_or_create(db, || {
db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Struct>>::default())
db.zalsa()
.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Struct>>()
})
}

Expand Down
21 changes: 15 additions & 6 deletions components/salsa-macro-rules/src/setup_input_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ macro_rules! setup_input_struct {
use salsa::plumbing as $zalsa;
use $zalsa::input as $zalsa_struct;

struct $Configuration;
$vis struct $Configuration;

impl $zalsa_struct::Configuration for $Configuration {
const DEBUG_NAME: &'static str = stringify!($Struct);
Expand All @@ -89,13 +89,13 @@ macro_rules! setup_input_struct {
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
CACHE.get_or_create(db, || {
db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
db.zalsa().add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
})
}

pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
let zalsa_mut = db.zalsa_mut();
let index = zalsa_mut.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default());
let index = zalsa_mut.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
let current_revision = zalsa_mut.current_revision();
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
Expand Down Expand Up @@ -124,8 +124,17 @@ macro_rules! setup_input_struct {
}

impl $zalsa::SalsaStructInDb for $Struct {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
}

#[inline]
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
if type_id == $zalsa::TypeId::of::<$Struct>() {
$zalsa::Some($Struct(id))
} else {
$zalsa::None
}
}
}

Expand Down Expand Up @@ -187,7 +196,7 @@ macro_rules! setup_input_struct {
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
$Db: ?Sized + salsa::Database,
{
$Configuration::ingredient(db.as_dyn_database()).get_singleton_input()
$Configuration::ingredient(db.as_dyn_database()).get_singleton_input(db)
}

#[track_caller]
Expand Down
15 changes: 12 additions & 3 deletions components/salsa-macro-rules/src/setup_interned_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ macro_rules! setup_interned_struct {
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
CACHE.get_or_create(db.as_dyn_database(), || {
db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
db.zalsa().add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
})
}
}
Expand Down Expand Up @@ -149,8 +149,17 @@ macro_rules! setup_interned_struct {
}

impl $zalsa::SalsaStructInDb for $Struct<'_> {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
}

#[inline]
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
if type_id == $zalsa::TypeId::of::<$Struct>() {
$zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
} else {
$zalsa::None
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ macro_rules! setup_interned_struct_sans_lifetime {
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
CACHE.get_or_create(db.as_dyn_database(), || {
db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
db.zalsa().add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
})
}
}
Expand Down Expand Up @@ -157,8 +157,17 @@ macro_rules! setup_interned_struct_sans_lifetime {
}

impl $zalsa::SalsaStructInDb for $Struct {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
}

#[inline]
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
if type_id == $zalsa::TypeId::of::<$Struct>() {
$zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
} else {
$zalsa::None
}
}
}

Expand Down
49 changes: 34 additions & 15 deletions components/salsa-macro-rules/src/setup_tracked_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,17 @@ macro_rules! setup_tracked_fn {
$zalsa::IngredientCache::new();

impl $zalsa::SalsaStructInDb for $InternedData<'_> {
fn lookup_ingredient_index(_aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
None
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
$zalsa::IngredientIndices::uninitialized()
}

#[inline]
fn cast(id: $zalsa::Id, type_id: ::core::any::TypeId) -> Option<Self> {
if type_id == ::core::any::TypeId::of::<$InternedData>() {
Some($InternedData(id, ::core::marker::PhantomData))
} else {
None
}
}
}

Expand Down Expand Up @@ -130,7 +139,7 @@ macro_rules! setup_tracked_fn {
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
$FN_CACHE.get_or_create(db.as_dyn_database(), || {
<dyn $Db as $Db>::zalsa_db(db);
db.zalsa().add_or_lookup_jar_by_type(&$Configuration)
db.zalsa().add_or_lookup_jar_by_type::<$Configuration>()
})
}

Expand All @@ -139,7 +148,7 @@ macro_rules! setup_tracked_fn {
db: &dyn $Db,
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
$INTERN_CACHE.get_or_create(db.as_dyn_database(), || {
db.zalsa().add_or_lookup_jar_by_type(&$Configuration).successor(0)
db.zalsa().add_or_lookup_jar_by_type::<$Configuration>().successor(0)
})
}
}
Expand Down Expand Up @@ -190,33 +199,43 @@ macro_rules! setup_tracked_fn {
if $needs_interner {
$Configuration::intern_ingredient(db).data(db.as_dyn_database(), key).clone()
} else {
$zalsa::FromId::from_id(key)
$zalsa::FromIdWithDb::from_id(key, db)
}
}
}
}

impl $zalsa::Jar for $Configuration {
fn create_dependencies(zalsa: &$zalsa::Zalsa) -> $zalsa::IngredientIndices
where
Self: Sized
{
$zalsa::macro_if! {
if $needs_interner {
$zalsa::IngredientIndices::uninitialized()
} else {
<$InternedData as $zalsa::SalsaStructInDb>::lookup_or_create_ingredient_index(zalsa)
}
}
}

fn create_ingredients(
&self,
aux: &dyn $zalsa::JarAux,
zalsa: &$zalsa::Zalsa,
first_index: $zalsa::IngredientIndex,
struct_index: $zalsa::IngredientIndices,
) -> Vec<Box<dyn $zalsa::Ingredient>> {
let struct_index = $zalsa::macro_if! {
if $needs_interner {
first_index.successor(0)
first_index.successor(0).into()
} else {
<$InternedData as $zalsa::SalsaStructInDb>::lookup_ingredient_index(aux)
.expect(
"Salsa struct is passed as an argument of a tracked function, but its ingredient hasn't been added!"
)
struct_index
}
};

let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new(
struct_index,
first_index,
aux,
zalsa,
);
fn_ingredient.set_capacity($lru);
$zalsa::macro_if! {
Expand All @@ -235,8 +254,8 @@ macro_rules! setup_tracked_fn {
}
}

fn salsa_struct_type_id(&self) -> Option<core::any::TypeId> {
None
fn id_struct_type_id() -> $zalsa::TypeId {
$zalsa::TypeId::of::<$InternedData<'static>>()
}
}

Expand Down
15 changes: 12 additions & 3 deletions components/salsa-macro-rules/src/setup_tracked_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ macro_rules! setup_tracked_struct {
static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
CACHE.get_or_create(db, || {
db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl::<$Configuration>>::default())
db.zalsa().add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
})
}
}
Expand All @@ -152,8 +152,17 @@ macro_rules! setup_tracked_struct {
}

impl $zalsa::SalsaStructInDb for $Struct<'_> {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
}

#[inline]
fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
if type_id == $zalsa::TypeId::of::<$Struct>() {
$zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
} else {
$zalsa::None
}
}
}

Expand Down
142 changes: 142 additions & 0 deletions components/salsa-macros/src/enum_.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::token_stream_with_error;
use proc_macro2::TokenStream;

/// For an entity struct `Foo` with fields `f1: T1, ..., fN: TN`, we generate...
///
/// * the "id struct" `struct Foo(salsa::Id)`
/// * the entity ingredient, which maps the id fields to the `Id`
/// * for each value field, a function ingredient
pub(crate) fn enum_(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let enum_item = parse_macro_input!(input as syn::ItemEnum);
match enum_impl(enum_item) {
Ok(v) => v.into(),
Err(e) => token_stream_with_error(input, e),
}
}

fn enum_impl(enum_item: syn::ItemEnum) -> syn::Result<TokenStream> {
let enum_name = enum_item.ident.clone();
let mut variant_names = Vec::new();
let mut variant_types = Vec::new();
if enum_item.variants.is_empty() {
return Err(syn::Error::new(
enum_item.enum_token.span,
"empty enums are not permitted",
));
}
for variant in &enum_item.variants {
let valid = match &variant.fields {
syn::Fields::Unnamed(fields) => {
variant_names.push(variant.ident.clone());
variant_types.push(fields.unnamed[0].ty.clone());
fields.unnamed.len() == 1
}
syn::Fields::Unit | syn::Fields::Named(_) => false,
};
if !valid {
return Err(syn::Error::new(
variant.ident.span(),
"the only form allowed is `Variant(SalsaStruct)`",
));
}
}

let (impl_generics, type_generics, where_clause) = enum_item.generics.split_for_impl();

let as_id = quote! {
impl #impl_generics zalsa::AsId for #enum_name #type_generics
#where_clause {
#[inline]
fn as_id(&self) -> zalsa::Id {
match self {
#( Self::#variant_names(__v) => zalsa::AsId::as_id(__v), )*
}
}
}
};

let from_id = quote! {
impl #impl_generics zalsa::FromIdWithDb for #enum_name #type_generics
#where_clause {
#[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);
<Self as zalsa::SalsaStructInDb>::cast(__id, __type_id).expect("invalid enum variant")
}
}
};

let salsa_struct_in_db = quote! {
impl #impl_generics zalsa::SalsaStructInDb for #enum_name #type_generics
#where_clause {
#[inline]
fn lookup_or_create_ingredient_index(__zalsa: &zalsa::Zalsa) -> zalsa::IngredientIndices {
let mut __result = zalsa::IngredientIndices::uninitialized();
#(
__result.merge(
&<#variant_types as zalsa::SalsaStructInDb>::lookup_or_create_ingredient_index(__zalsa)
);
)*
__result
}

#[inline]
fn cast(id: zalsa::Id, type_id: ::core::any::TypeId) -> Option<Self> {
#(
// Subtle: the ingredient can be missing, but in this case the id cannot come
// from it - because it wasn't initialized yet.
if let Some(result) = <#variant_types as zalsa::SalsaStructInDb>::cast(id, type_id) {
Some(Self::#variant_names(result))
} else
)*
{
None
}
}
}
};

let std_traits = quote! {
impl #impl_generics ::core::marker::Copy for #enum_name #type_generics
#where_clause {}

impl #impl_generics ::core::clone::Clone for #enum_name #type_generics
#where_clause {
#[inline]
fn clone(&self) -> Self { *self }
}

impl #impl_generics ::core::cmp::Eq for #enum_name #type_generics
#where_clause {}

impl #impl_generics ::core::cmp::PartialEq for #enum_name #type_generics
#where_clause {
#[inline]
fn eq(&self, __other: &Self) -> bool {
zalsa::AsId::as_id(self) == zalsa::AsId::as_id(__other)
}
}

impl #impl_generics ::core::hash::Hash for #enum_name #type_generics
#where_clause {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) {
::core::hash::Hash::hash(&zalsa::AsId::as_id(self), __state);
}
}
};

let all_impls = quote! {
const _: () = {
use salsa::plumbing as zalsa;

#as_id
#from_id
#salsa_struct_in_db

#std_traits
};
};
Ok(all_impls)
}
Loading

0 comments on commit 927dfe8

Please sign in to comment.