1#[macro_export]
3macro_rules! setup_interned_struct {
4 (
5 attrs: [$(#[$attr:meta]),*],
7
8 vis: $vis:vis,
10
11 Struct: $Struct:ident,
13
14 StructData: $StructDataIdent:ident,
17
18 StructWithStatic: $StructWithStatic:ty,
21
22 db_lt: $db_lt:lifetime,
24
25 db_lt_arg: $($db_lt_arg:lifetime)?,
27
28 id: $Id:path,
30
31 interior_lt: $interior_lt:lifetime,
35
36 new_fn: $new_fn:ident,
38
39 field_options: [$($field_option:tt),*],
41
42 field_ids: [$($field_id:ident),*],
44
45 field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*],
47
48 field_tys: [$($field_ty:ty),*],
50
51 field_indices: [$($field_index:tt),*],
53
54 field_indexed_tys: [$($indexed_ty:ident),*],
56
57 num_fields: $N:literal,
59
60 generate_debug_impl: $generate_debug_impl:tt,
62
63 unused_names: [
67 $zalsa:ident,
68 $zalsa_struct:ident,
69 $Configuration:ident,
70 $CACHE:ident,
71 $Db:ident,
72 ]
73 ) => {
74 $(#[$attr])*
75 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
76 $vis struct $Struct< $($db_lt_arg)? >(
77 $Id,
78 std::marker::PhantomData < & $interior_lt salsa::plumbing::interned::Value <$StructWithStatic> >
79 );
80
81 #[allow(clippy::all)]
82 #[allow(dead_code)]
83 const _: () = {
84 use salsa::plumbing as $zalsa;
85 use $zalsa::interned as $zalsa_struct;
86
87 type $Configuration = $StructWithStatic;
88
89 type $StructDataIdent<$db_lt> = ($($field_ty,)*);
90
91 #[derive(Hash)]
94 struct StructKey<$db_lt, $($indexed_ty),*>(
95 $($indexed_ty,)*
96 std::marker::PhantomData<&$db_lt ()>,
97 );
98
99 impl<$db_lt, $($indexed_ty,)*> $zalsa::interned::HashEqLike<StructKey<$db_lt, $($indexed_ty),*>>
100 for $StructDataIdent<$db_lt>
101 where
102 $($field_ty: $zalsa::interned::HashEqLike<$indexed_ty>),*
103 {
104
105 fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
106 $($zalsa::interned::HashEqLike::<$indexed_ty>::hash(&self.$field_index, &mut *h);)*
107 }
108
109 fn eq(&self, data: &StructKey<$db_lt, $($indexed_ty),*>) -> bool {
110 ($($zalsa::interned::HashEqLike::<$indexed_ty>::eq(&self.$field_index, &data.$field_index) && )* true)
111 }
112 }
113
114 impl<$db_lt, $($indexed_ty: $zalsa::interned::Lookup<$field_ty>),*> $zalsa::interned::Lookup<$StructDataIdent<$db_lt>>
115 for StructKey<$db_lt, $($indexed_ty),*> {
116
117 #[allow(unused_unit)]
118 fn into_owned(self) -> $StructDataIdent<$db_lt> {
119 ($($zalsa::interned::Lookup::into_owned(self.$field_index),)*)
120 }
121 }
122
123 impl salsa::plumbing::interned::Configuration for $StructWithStatic {
124 const LOCATION: $zalsa::Location = $zalsa::Location {
125 file: file!(),
126 line: line!(),
127 };
128 const DEBUG_NAME: &'static str = stringify!($Struct);
129 type Fields<'a> = $StructDataIdent<'a>;
130 type Struct<'db> = $Struct< $($db_lt_arg)? >;
131 }
132
133 impl $Configuration {
134 #[allow(unexpected_cfgs)]
136 pub fn ingredient<Db>(db: &Db) -> &$zalsa_struct::IngredientImpl<Self>
137 where
138 Db: ?Sized + $zalsa::Database,
139 {
140 $zalsa::__maybe_lazy_static! {
141 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
142 $zalsa::IngredientCache::new();
143 }
144
145 let zalsa = db.zalsa();
146 CACHE.get_or_create(zalsa, || {
147 zalsa.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
148 })
149 }
150 }
151
152 impl< $($db_lt_arg)? > $zalsa::AsId for $Struct< $($db_lt_arg)? > {
153 fn as_id(&self) -> salsa::Id {
154 self.0.as_id()
155 }
156 }
157
158 impl< $($db_lt_arg)? > $zalsa::FromId for $Struct< $($db_lt_arg)? > {
159 fn from_id(id: salsa::Id) -> Self {
160 Self(<$Id>::from_id(id), std::marker::PhantomData)
161 }
162 }
163
164 unsafe impl< $($db_lt_arg)? > Send for $Struct< $($db_lt_arg)? > {}
165
166 unsafe impl< $($db_lt_arg)? > Sync for $Struct< $($db_lt_arg)? > {}
167
168 $zalsa::macro_if! { $generate_debug_impl =>
169 impl< $($db_lt_arg)? > std::fmt::Debug for $Struct< $($db_lt_arg)? > {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 Self::default_debug_fmt(*self, f)
172 }
173 }
174 }
175
176 impl< $($db_lt_arg)? > $zalsa::SalsaStructInDb for $Struct< $($db_lt_arg)? > {
177 type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
178
179 fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
180 aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
181 }
182
183 #[inline]
184 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
185 if type_id == $zalsa::TypeId::of::<$Struct>() {
186 $zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
187 } else {
188 $zalsa::None
189 }
190 }
191 }
192
193 unsafe impl< $($db_lt_arg)? > $zalsa::Update for $Struct< $($db_lt_arg)? > {
194 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
195 if unsafe { *old_pointer } != new_value {
196 unsafe { *old_pointer = new_value };
197 true
198 } else {
199 false
200 }
201 }
202 }
203
204 impl<$db_lt> $Struct< $($db_lt_arg)? > {
205 pub fn $new_fn<$Db, $($indexed_ty: $zalsa::interned::Lookup<$field_ty> + std::hash::Hash,)*>(db: &$db_lt $Db, $($field_id: $indexed_ty),*) -> Self
206 where
207 $Db: ?Sized + salsa::Database,
209 $(
210 $field_ty: $zalsa::interned::HashEqLike<$indexed_ty>,
211 )*
212 {
213 $Configuration::ingredient(db).intern(db.as_dyn_database(),
214 StructKey::<$db_lt>($($field_id,)* std::marker::PhantomData::default()), |_, data| ($($zalsa::interned::Lookup::into_owned(data.$field_index),)*))
215 }
216
217 $(
218 $field_getter_vis fn $field_getter_id<$Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
219 where
220 $Db: ?Sized + $zalsa::Database,
222 {
223 let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), self);
224 $zalsa::return_mode_expression!(
225 $field_option,
226 $field_ty,
227 &fields.$field_index,
228 )
229 }
230 )*
231
232 pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 $zalsa::with_attached_database(|db| {
235 let fields = $Configuration::ingredient(db).fields(db.as_dyn_database(), this);
236 let mut f = f.debug_struct(stringify!($Struct));
237 $(
238 let f = f.field(stringify!($field_id), &fields.$field_index);
239 )*
240 f.finish()
241 }).unwrap_or_else(|| {
242 f.debug_tuple(stringify!($Struct))
243 .field(&$zalsa::AsId::as_id(&this))
244 .finish()
245 })
246 }
247 }
248 };
249 };
250}