1#[macro_export]
3macro_rules! setup_tracked_struct {
4 (
5 attrs: [$(#[$attr:meta]),*],
7
8 vis: $vis:vis,
10
11 Struct: $Struct:ident,
13
14 db_lt: $db_lt:lifetime,
16
17 new_fn: $new_fn:ident,
19
20 field_ids: [$($field_id:ident),*],
22
23 tracked_ids: [$($tracked_id:ident),*],
25
26 tracked_getters: [$($tracked_getter_vis:vis $tracked_getter_id:ident),*],
28
29 untracked_getters: [$($untracked_getter_vis:vis $untracked_getter_id:ident),*],
31
32 field_tys: [$($field_ty:ty),*],
34
35 tracked_tys: [$($tracked_ty:ty),*],
37
38 untracked_tys: [$($untracked_ty:ty),*],
40
41 field_indices: [$($field_index:tt),*],
43
44 absolute_tracked_indices: [$($absolute_tracked_index:tt),*],
46
47 relative_tracked_indices: [$($relative_tracked_index:tt),*],
49
50 absolute_untracked_indices: [$($absolute_untracked_index:tt),*],
52
53 tracked_maybe_updates: [$($tracked_maybe_update:tt),*],
55
56 untracked_maybe_updates: [$($untracked_maybe_update:tt),*],
58
59 tracked_options: [$($tracked_option:tt),*],
69
70 untracked_options: [$($untracked_option:tt),*],
80
81 tracked_field_attrs: [$([$(#[$tracked_field_attr:meta]),*]),*],
83 untracked_field_attrs: [$([$(#[$untracked_field_attr:meta]),*]),*],
84
85 num_tracked_fields: $N:literal,
87
88 generate_debug_impl: $generate_debug_impl:tt,
90
91 heap_size_fn: $($heap_size_fn:path)?,
93
94 persist: $persist:tt,
96
97 serialize_fn: $($serialize_fn:path)?,
99
100 deserialize_fn: $($deserialize_fn:path)?,
102
103 unused_names: [
107 $zalsa:ident,
108 $zalsa_struct:ident,
109 $Configuration:ident,
110 $CACHE:ident,
111 $Db:ident,
112 $Revision:ident,
113 ]
114 ) => {
115 $(#[$attr])*
116 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
117 $vis struct $Struct<$db_lt>(
118 ::salsa::Id,
119 ::std::marker::PhantomData<fn() -> &$db_lt ()>
120 );
121
122 #[allow(dead_code)]
123 #[allow(clippy::all)]
124 const _: () = {
125 use ::salsa::plumbing as $zalsa;
126 use $zalsa::tracked_struct as $zalsa_struct;
127 use $zalsa::Revision as $Revision;
128
129 type $Configuration = $Struct<'static>;
130
131 impl<$db_lt> $zalsa::HasJar for $Struct<$db_lt> {
132 type Jar = $zalsa_struct::JarImpl<$Configuration>;
133 const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
134 }
135
136 $zalsa::register_jar! {
137 $zalsa::ErasedJar::erase::<$Struct<'static>>()
138 }
139
140 impl $zalsa_struct::Configuration for $Configuration {
141 const LOCATION: $zalsa::Location = $zalsa::Location {
142 file: file!(),
143 line: line!(),
144 };
145 const DEBUG_NAME: &'static str = stringify!($Struct);
146
147 const TRACKED_FIELD_NAMES: &'static [&'static str] = &[
148 $(stringify!($tracked_id),)*
149 ];
150
151 const TRACKED_FIELD_INDICES: &'static [usize] = &[
152 $($relative_tracked_index,)*
153 ];
154
155 const PERSIST: bool = $persist;
156
157 type Fields<$db_lt> = ($($field_ty,)*);
158
159 type Revisions = [$Revision; $N];
160
161 type Struct<$db_lt> = $Struct<$db_lt>;
162
163 fn untracked_fields(fields: &Self::Fields<'_>) -> impl ::std::hash::Hash {
164 ( $( &fields.$absolute_untracked_index ),* )
165 }
166
167 fn new_revisions(current_revision: $Revision) -> Self::Revisions {
168 [current_revision; $N]
169 }
170
171 unsafe fn update_fields<$db_lt>(
172 current_revision: $Revision,
173 revisions: &mut Self::Revisions,
174 old_fields: *mut Self::Fields<$db_lt>,
175 new_fields: Self::Fields<$db_lt>,
176 ) -> bool {
177 use $zalsa::UpdateFallback as _;
178 unsafe {
179 $(
180 $crate::maybe_backdate!(
181 $tracked_option,
182 $tracked_maybe_update,
183 (*old_fields).$absolute_tracked_index,
184 new_fields.$absolute_tracked_index,
185 revisions[$relative_tracked_index],
186 current_revision,
187 $zalsa,
188 );
189 )*;
190
191 $(
194 $untracked_maybe_update(
195 &mut (*old_fields).$absolute_untracked_index,
196 new_fields.$absolute_untracked_index,
197 )
198 |
199 )* false
200 }
201 }
202
203 $(
204 fn heap_size(value: &Self::Fields<'_>) -> Option<usize> {
205 Some($heap_size_fn(value))
206 }
207 )?
208
209 fn serialize<S: $zalsa::serde::Serializer>(
210 fields: &Self::Fields<'_>,
211 serializer: S,
212 ) -> ::std::result::Result<S::Ok, S::Error> {
213 $zalsa::macro_if! {
214 if $persist {
215 $($serialize_fn(fields, serializer))?
216 } else {
217 panic!("attempted to serialize value not marked with `persist` attribute")
218 }
219 }
220 }
221
222 fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>(
223 deserializer: D,
224 ) -> ::std::result::Result<Self::Fields<'static>, D::Error> {
225 $zalsa::macro_if! {
226 if $persist {
227 $($deserialize_fn(deserializer))?
228 } else {
229 panic!("attempted to deserialize value not marked with `persist` attribute")
230 }
231 }
232 }
233 }
234
235 impl $Configuration {
236 pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
237 Self::ingredient_(db.zalsa())
238 }
239
240 fn ingredient_(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
241 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
242 $zalsa::IngredientCache::new();
243
244 unsafe {
247 CACHE.get_or_create(zalsa, || {
248 zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
249 })
250 }
251 }
252 }
253
254 impl<$db_lt> $zalsa::FromId for $Struct<$db_lt> {
255 #[inline]
256 fn from_id(id: ::salsa::Id) -> Self {
257 $Struct(id, ::std::marker::PhantomData)
258 }
259 }
260
261 impl $zalsa::AsId for $Struct<'_> {
262 #[inline]
263 fn as_id(&self) -> $zalsa::Id {
264 self.0
265 }
266 }
267
268 impl $zalsa::SalsaStructInDb for $Struct<'_> {
269 type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
270
271 fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
272 aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
273 }
274
275 fn entries(
276 zalsa: &$zalsa::Zalsa
277 ) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
278 let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
279 <$Configuration>::ingredient_(zalsa).entries(zalsa).map(|entry| entry.key())
280 }
281
282 #[inline]
283 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
284 if type_id == $zalsa::TypeId::of::<$Struct<'static>>() {
285 $zalsa::Some(<$Struct<'static> as $zalsa::FromId>::from_id(id))
286 } else {
287 $zalsa::None
288 }
289 }
290
291 #[inline]
292 unsafe fn memo_table(
293 zalsa: &$zalsa::Zalsa,
294 id: $zalsa::Id,
295 current_revision: $zalsa::Revision,
296 ) -> $zalsa::MemoTableWithTypes<'_> {
297 unsafe { zalsa.table().memos::<$zalsa_struct::Value<$Configuration>>(id, current_revision) }
299 }
300 }
301
302 impl $zalsa::TrackedStructInDb for $Struct<'_> {
303 fn database_key_index(zalsa: &$zalsa::Zalsa, id: $zalsa::Id) -> $zalsa::DatabaseKeyIndex {
304 $Configuration::ingredient_(zalsa).database_key_index(id)
305 }
306 }
307
308 $zalsa::macro_if! { $persist =>
309 impl $zalsa::serde::Serialize for $Struct<'_> {
310 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
311 where
312 S: $zalsa::serde::Serializer,
313 {
314 $zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
315 }
316 }
317
318 impl<'de> $zalsa::serde::Deserialize<'de> for $Struct<'_> {
319 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
320 where
321 D: $zalsa::serde::Deserializer<'de>,
322 {
323 let id = $zalsa::Id::deserialize(deserializer)?;
324 Ok($zalsa::FromId::from_id(id))
325 }
326 }
327 }
328
329
330 unsafe impl Send for $Struct<'_> {}
331
332 unsafe impl Sync for $Struct<'_> {}
333
334 $zalsa::macro_if! { $generate_debug_impl =>
335 impl ::std::fmt::Debug for $Struct<'_> {
336 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
337 Self::default_debug_fmt(*self, f)
338 }
339 }
340 }
341
342 unsafe impl $zalsa::Update for $Struct<'_> {
343 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
344 if unsafe { *old_pointer } != new_value {
345 unsafe { *old_pointer = new_value };
346 true
347 } else {
348 false
349 }
350 }
351 }
352
353 impl<$db_lt> $Struct<$db_lt> {
354 pub fn $new_fn<$Db>(db: &$db_lt $Db, $($field_id: $field_ty),*) -> Self
355 where
356 $Db: ?Sized + $zalsa::Database,
358 {
359 let (zalsa, zalsa_local) = db.zalsas();
360 $Configuration::ingredient_(zalsa).new_struct(
361 zalsa,zalsa_local,
362 ($($field_id,)*)
363 )
364 }
365
366 $(
367 $(#[$tracked_field_attr])*
368 $tracked_getter_vis fn $tracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($tracked_option, $db_lt, $tracked_ty)
369 where
370 $Db: ?Sized + $zalsa::Database,
372 {
373 let (zalsa, zalsa_local) = db.zalsas();
374 let fields = $Configuration::ingredient_(zalsa).tracked_field(zalsa, zalsa_local, self, $relative_tracked_index);
375 $crate::return_mode_expression!(
376 $tracked_option,
377 $tracked_ty,
378 &fields.$absolute_tracked_index,
379 )
380 }
381 )*
382
383 $(
384 $(#[$untracked_field_attr])*
385 $untracked_getter_vis fn $untracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($untracked_option, $db_lt, $untracked_ty)
386 where
387 $Db: ?Sized + $zalsa::Database,
389 {
390 let zalsa = db.zalsa();
391 let fields = $Configuration::ingredient_(zalsa).untracked_field(zalsa, self);
392 $crate::return_mode_expression!(
393 $untracked_option,
394 $untracked_ty,
395 &fields.$absolute_untracked_index,
396 )
397 }
398 )*
399 }
400
401 #[allow(unused_lifetimes)]
402 impl<'_db> $Struct<'_db> {
403 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
405 where
406 $(for<$db_lt> $field_ty: ::std::fmt::Debug),*
411 {
412 $zalsa::with_attached_database(|db| {
413 let zalsa = db.zalsa();
414 let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
415 let mut f = f.debug_struct(stringify!($Struct));
416 let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
417 $(
418 let f = f.field(stringify!($field_id), &fields.$field_index);
419 )*
420 f.finish()
421 }).unwrap_or_else(|| {
422 f.debug_struct(stringify!($Struct))
423 .field("[salsa id]", &$zalsa::AsId::as_id(&this))
424 .finish()
425 })
426 }
427 }
428 };
429 };
430}