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
241 fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
242 aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
243 }
244
245 fn entries(
246 zalsa: &$zalsa::Zalsa
247 ) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
248 let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
249 <$Configuration>::ingredient(zalsa).entries(zalsa).map(|entry| entry.key())
250 }
251
252 #[inline]
253 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
254 if type_id == $zalsa::TypeId::of::<$Struct>() {
255 $zalsa::Some(<$Struct as $zalsa::FromId>::from_id(id))
256 } else {
257 $zalsa::None
258 }
259 }
260
261 #[inline]
262 unsafe fn memo_table(
263 zalsa: &$zalsa::Zalsa,
264 id: $zalsa::Id,
265 current_revision: $zalsa::Revision,
266 ) -> $zalsa::MemoTableWithTypes<'_> {
267 unsafe { zalsa.table().memos::<$zalsa_struct::Value<$Configuration>>(id, current_revision) }
269 }
270 }
271
272 $zalsa::macro_if! { $persist =>
273 impl<$($db_lt_arg)?> $zalsa::serde::Serialize for $Struct<$($db_lt_arg)?> {
274 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
275 where
276 S: $zalsa::serde::Serializer,
277 {
278 $zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
279 }
280 }
281
282 impl<'de, $($db_lt_arg)?> $zalsa::serde::Deserialize<'de> for $Struct<$($db_lt_arg)?> {
283 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
284 where
285 D: $zalsa::serde::Deserializer<'de>,
286 {
287 let id = $zalsa::Id::deserialize(deserializer)?;
288 Ok($zalsa::FromId::from_id(id))
289 }
290 }
291 }
292
293
294 unsafe impl< $($db_lt_arg)? > $zalsa::Update for $Struct< $($db_lt_arg)? > {
295 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
296 $($assert_types_are_update)*
297
298 if unsafe { *old_pointer } != new_value {
299 unsafe { *old_pointer = new_value };
300 true
301 } else {
302 false
303 }
304 }
305 }
306
307 impl<$db_lt> $Struct< $($db_lt_arg)? > {
308 pub fn $new_fn<$Db, $($indexed_ty: $zalsa::Lookup<$field_ty> + ::std::hash::Hash,)*>(db: &$db_lt $Db, $($field_id: $indexed_ty),*) -> Self
309 where
310 $Db: ?Sized + ::salsa::Database,
312 $(
313 $field_ty: $zalsa::HashEqLike<$indexed_ty>,
314 )*
315 {
316 let (zalsa, zalsa_local) = db.zalsas();
317 $Configuration::ingredient(zalsa).intern(zalsa, zalsa_local,
318 StructKey::<$db_lt>($($field_id,)* ::std::marker::PhantomData::default()), |_, data| ($($zalsa::Lookup::into_owned(data.$field_index),)*))
319 }
320
321 $(
322 $(#[$field_attr])*
323 $field_getter_vis fn $field_getter_id<$Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
324 where
325 $Db: ?Sized + $zalsa::Database,
327 {
328 let zalsa = db.zalsa();
329 let fields = $Configuration::ingredient(zalsa).fields(zalsa, self);
330 $zalsa::return_mode_expression!(
331 $field_option,
332 $field_ty,
333 &fields.$field_index,
334 )
335 }
336 )*
337 }
338
339 $zalsa::macro_if! {
341 iftt ($($db_lt_arg)?) {
342 impl $Struct<'_> {
343 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
345 where
346 $(for<$db_lt> $field_ty: ::std::fmt::Debug),*
349 {
350 $zalsa::with_attached_database(|db| {
351 let zalsa = db.zalsa();
352 let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
353 let mut f = f.debug_struct(stringify!($Struct));
354 $(
355 let f = f.field(stringify!($field_id), &fields.$field_index);
356 )*
357 f.finish()
358 }).unwrap_or_else(|| {
359 f.debug_tuple(stringify!($Struct))
360 .field(&$zalsa::AsId::as_id(&this))
361 .finish()
362 })
363 }
364 }
365 } else {
366 impl $Struct {
367 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
369 where
370 $(for<$db_lt> $field_ty: ::std::fmt::Debug),*
373 {
374 $zalsa::with_attached_database(|db| {
375 let zalsa = db.zalsa();
376 let fields = $Configuration::ingredient(zalsa).fields(zalsa, this);
377 let mut f = f.debug_struct(stringify!($Struct));
378 $(
379 let f = f.field(stringify!($field_id), &fields.$field_index);
380 )*
381 f.finish()
382 }).unwrap_or_else(|| {
383 f.debug_tuple(stringify!($Struct))
384 .field(&$zalsa::AsId::as_id(&this))
385 .finish()
386 })
387 }
388 }
389 }
390 }
391 };
392 };
393}