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 revisions: $($revisions:expr)?,
33
34 interior_lt: $interior_lt:lifetime,
38
39 new_fn: $new_fn:ident,
41
42 field_options: [$($field_option:tt),*],
44
45 field_ids: [$($field_id:ident),*],
47
48 field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*],
50
51 field_tys: [$($field_ty:ty),*],
53
54 field_indices: [$($field_index:tt),*],
56
57 field_indexed_tys: [$($indexed_ty:ident),*],
59
60 field_attrs: [$([$(#[$field_attr:meta]),*]),*],
62
63 num_fields: $N:literal,
65
66 generate_debug_impl: $generate_debug_impl:tt,
68
69 heap_size_fn: $($heap_size_fn:path)?,
71
72 persist: $persist:tt,
74
75 serialize_fn: $($serialize_fn:path)?,
77
78 deserialize_fn: $($deserialize_fn:path)?,
80
81 assert_types_are_update: {$($assert_types_are_update:tt)*},
82
83 unused_names: [
87 $zalsa:ident,
88 $zalsa_struct:ident,
89 $Configuration:ident,
90 $CACHE:ident,
91 $Db:ident,
92 ]
93 ) => {
94 $(#[$attr])*
95 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
96 $vis struct $Struct< $($db_lt_arg)? >(
97 $Id,
98 std::marker::PhantomData<fn() -> &$interior_lt ()>
99 );
100
101 #[allow(clippy::all)]
102 #[allow(dead_code)]
103 const _: () = {
104 use ::salsa::plumbing as $zalsa;
105 use $zalsa::interned as $zalsa_struct;
106
107 type $Configuration = $StructWithStatic;
108
109 impl<$($db_lt_arg)?> $zalsa::HasJar for $Struct<$($db_lt_arg)?> {
110 type Jar = $zalsa_struct::JarImpl<$Configuration>;
111 const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
112 }
113
114 $zalsa::register_jar! {
115 $zalsa::ErasedJar::erase::<$StructWithStatic>()
116 }
117
118 type $StructDataIdent<$db_lt> = ($($field_ty,)*);
119
120 #[derive(Hash)]
123 struct StructKey<$db_lt, $($indexed_ty),*>(
124 $($indexed_ty,)*
125 ::std::marker::PhantomData<&$db_lt ()>,
126 );
127
128 impl<$db_lt, $($indexed_ty,)*> $zalsa::HashEqLike<StructKey<$db_lt, $($indexed_ty),*>>
129 for $StructDataIdent<$db_lt>
130 where
131 $($field_ty: $zalsa::HashEqLike<$indexed_ty>),*
132 {
133
134 fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
135 $($zalsa::HashEqLike::<$indexed_ty>::hash(&self.$field_index, &mut *h);)*
136 }
137
138 fn eq(&self, data: &StructKey<$db_lt, $($indexed_ty),*>) -> bool {
139 ($($zalsa::HashEqLike::<$indexed_ty>::eq(&self.$field_index, &data.$field_index) && )* true)
140 }
141 }
142
143 impl<$db_lt, $($indexed_ty: $zalsa::Lookup<$field_ty>),*> $zalsa::Lookup<$StructDataIdent<$db_lt>>
144 for StructKey<$db_lt, $($indexed_ty),*> {
145
146 #[allow(unused_unit)]
147 fn into_owned(self) -> $StructDataIdent<$db_lt> {
148 ($($zalsa::Lookup::into_owned(self.$field_index),)*)
149 }
150 }
151
152 impl $zalsa::interned::Configuration for $StructWithStatic {
153 const LOCATION: $zalsa::Location = $zalsa::Location {
154 file: file!(),
155 line: line!(),
156 };
157 const DEBUG_NAME: &'static str = stringify!($Struct);
158 const PERSIST: bool = $persist;
159
160 $(
161 const REVISIONS: ::core::num::NonZeroUsize = ::core::num::NonZeroUsize::new($revisions).unwrap();
162 )?
163
164 type Fields<'a> = $StructDataIdent<'a>;
165 type Struct<'db> = $Struct< $($db_lt_arg)? >;
166
167 $(
168 fn heap_size(value: &Self::Fields<'_>) -> Option<usize> {
169 Some($heap_size_fn(value))
170 }
171 )?
172
173 fn serialize<S: $zalsa::serde::Serializer>(
174 fields: &Self::Fields<'_>,
175 serializer: S,
176 ) -> ::std::result::Result<S::Ok, S::Error> {
177 $zalsa::macro_if! {
178 if $persist {
179 $($serialize_fn(fields, serializer))?
180 } else {
181 panic!("attempted to serialize value not marked with `persist` attribute")
182 }
183 }
184 }
185
186 fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>(
187 deserializer: D,
188 ) -> ::std::result::Result<Self::Fields<'static>, D::Error> {
189 $zalsa::macro_if! {
190 if $persist {
191 $($deserialize_fn(deserializer))?
192 } else {
193 panic!("attempted to deserialize value not marked with `persist` attribute")
194 }
195 }
196 }
197 }
198
199 impl $Configuration {
200 pub fn ingredient(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
201 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
202 $zalsa::IngredientCache::new();
203
204 unsafe {
207 CACHE.get_or_create(zalsa, || {
208 zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
209 })
210 }
211 }
212 }
213
214 impl< $($db_lt_arg)? > $zalsa::AsId for $Struct< $($db_lt_arg)? > {
215 fn as_id(&self) -> ::salsa::Id {
216 self.0.as_id()
217 }
218 }
219
220 impl< $($db_lt_arg)? > $zalsa::FromId for $Struct< $($db_lt_arg)? > {
221 fn from_id(id: ::salsa::Id) -> Self {
222 Self(<$Id>::from_id(id), ::std::marker::PhantomData)
223 }
224 }
225
226 unsafe impl< $($db_lt_arg)? > Send for $Struct< $($db_lt_arg)? > {}
227
228 unsafe impl< $($db_lt_arg)? > Sync for $Struct< $($db_lt_arg)? > {}
229
230 $zalsa::macro_if! { $generate_debug_impl =>
231 impl< $($db_lt_arg)? > ::std::fmt::Debug for $Struct< $($db_lt_arg)? > {
232 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
233 Self::default_debug_fmt(*self, f)
234 }
235 }
236 }
237
238 impl< $($db_lt_arg)? > $zalsa::SalsaStructInDb for $Struct< $($db_lt_arg)? > {
239 type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
240 const LEAF_TYPE_IDS: &'static [$zalsa::ConstTypeId] = &[$zalsa::ConstTypeId::of::<$Struct>()];
241
242 fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
243 aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
244 }
245
246 fn entries(
247 zalsa: &$zalsa::Zalsa
248 ) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
249 let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
250 <$Configuration>::ingredient(zalsa).entries(zalsa).map(|entry| entry.key())
251 }
252
253 #[inline]
254 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
255 if type_id == $zalsa::TypeId::of::<$Struct>() {
256 $zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
257 } else {
258 $zalsa::None
259 }
260 }
261
262 #[inline]
263 unsafe fn memo_table(
264 zalsa: &$zalsa::Zalsa,
265 id: $zalsa::Id,
266 current_revision: $zalsa::Revision,
267 ) -> $zalsa::MemoTableWithTypes<'_> {
268 unsafe { zalsa.table().memos::<$zalsa_struct::Value<$Configuration>>(id, current_revision) }
270 }
271 }
272
273 $zalsa::macro_if! { $persist =>
274 impl<$($db_lt_arg)?> $zalsa::serde::Serialize for $Struct<$($db_lt_arg)?> {
275 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
276 where
277 S: $zalsa::serde::Serializer,
278 {
279 $zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
280 }
281 }
282
283 impl<'de, $($db_lt_arg)?> $zalsa::serde::Deserialize<'de> for $Struct<$($db_lt_arg)?> {
284 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
285 where
286 D: $zalsa::serde::Deserializer<'de>,
287 {
288 let id = $zalsa::Id::deserialize(deserializer)?;
289 Ok($zalsa::FromId::from_id(id))
290 }
291 }
292 }
293
294
295 unsafe impl< $($db_lt_arg)? > $zalsa::Update for $Struct< $($db_lt_arg)? > {
296 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
297 $($assert_types_are_update)*
298
299 if unsafe { *old_pointer } != new_value {
300 unsafe { *old_pointer = new_value };
301 true
302 } else {
303 false
304 }
305 }
306 }
307
308 impl<$db_lt> $Struct< $($db_lt_arg)? > {
309 pub fn $new_fn<$Db, $($indexed_ty: $zalsa::Lookup<$field_ty> + ::std::hash::Hash,)*>(db: &$db_lt $Db, $($field_id: $indexed_ty),*) -> Self
310 where
311 $Db: ?Sized + ::salsa::Database,
313 $(
314 $field_ty: $zalsa::HashEqLike<$indexed_ty>,
315 )*
316 {
317 let (zalsa, zalsa_local) = db.zalsas();
318 $Configuration::ingredient(zalsa).intern(zalsa, zalsa_local,
319 StructKey::<$db_lt>($($field_id,)* ::std::marker::PhantomData::default()), |_, data| ($($zalsa::Lookup::into_owned(data.$field_index),)*))
320 }
321
322 $(
323 $(#[$field_attr])*
324 $field_getter_vis fn $field_getter_id<$Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
325 where
326 $Db: ?Sized + $zalsa::Database,
328 {
329 let zalsa = db.zalsa();
330 let fields = $Configuration::ingredient(zalsa).fields(zalsa, self);
331 $zalsa::return_mode_expression!(
332 $field_option,
333 $field_ty,
334 &fields.$field_index,
335 )
336 }
337 )*
338 }
339
340 $zalsa::macro_if! {
342 iftt ($($db_lt_arg)?) {
343 impl $Struct<'_> {
344 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
346 where
347 $(for<$db_lt> $field_ty: ::std::fmt::Debug),*
350 {
351 $zalsa::with_attached_database(|db| {
352 let zalsa = db.zalsa();
353 let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
354 let mut f = f.debug_struct(stringify!($Struct));
355 $(
356 let f = f.field(stringify!($field_id), &fields.$field_index);
357 )*
358 f.finish()
359 }).unwrap_or_else(|| {
360 f.debug_tuple(stringify!($Struct))
361 .field(&$zalsa::AsId::as_id(&this))
362 .finish()
363 })
364 }
365 }
366 } else {
367 impl $Struct {
368 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
370 where
371 $(for<$db_lt> $field_ty: ::std::fmt::Debug),*
374 {
375 $zalsa::with_attached_database(|db| {
376 let zalsa = db.zalsa();
377 let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
378 let mut f = f.debug_struct(stringify!($Struct));
379 $(
380 let f = f.field(stringify!($field_id), &fields.$field_index);
381 )*
382 f.finish()
383 }).unwrap_or_else(|| {
384 f.debug_tuple(stringify!($Struct))
385 .field(&$zalsa::AsId::as_id(&this))
386 .finish()
387 })
388 }
389 }
390 }
391 }
392 };
393 };
394}