1#[macro_export]
3macro_rules! setup_input_struct {
4 (
5 attrs: [$(#[$attr:meta]),*],
7
8 vis: $vis:vis,
10
11 Struct: $Struct:ident,
13
14 new_fn: $new_fn:ident,
16
17 field_options: [$($field_option:tt),*],
19
20 field_ids: [$($field_id:ident),*],
22
23 field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*],
25
26 field_setters: [$($field_setter_vis:vis $field_setter_id:ident),*],
28
29 field_tys: [$($field_ty:ty),*],
31
32 field_indices: [$($field_index:tt),*],
34
35 field_attrs: [$([$(#[$field_attr:meta]),*]),*],
37
38 required_fields: [$($required_field_id:ident $required_field_ty:ty),*],
40
41 field_durability_ids: [$($field_durability_id:ident),*],
43
44 num_fields: $N:literal,
46
47 is_singleton: $is_singleton:tt,
49
50 generate_debug_impl: $generate_debug_impl:tt,
52
53 heap_size_fn: $($heap_size_fn:path)?,
55
56 persist: $persist:tt,
58
59 serialize_fn: $($serialize_fn:path)?,
61
62 deserialize_fn: $($deserialize_fn:path)?,
64
65 unused_names: [
69 $zalsa:ident,
70 $zalsa_struct:ident,
71 $Configuration:ident,
72 $Builder:ident,
73 $CACHE:ident,
74 $Db:ident,
75 ]
76 ) => {
77 $(#[$attr])*
78 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
79 $vis struct $Struct(salsa::Id);
80
81 #[allow(clippy::all)]
82 #[allow(dead_code)]
83 const _: () = {
84 use ::salsa::plumbing as $zalsa;
85 use $zalsa::input as $zalsa_struct;
86
87 type $Configuration = $Struct;
88
89 impl $zalsa::HasJar for $Struct {
90 type Jar = $zalsa_struct::JarImpl<$Configuration>;
91 const KIND: $zalsa::JarKind = $zalsa::JarKind::Struct;
92 }
93
94 $zalsa::register_jar! {
95 $zalsa::ErasedJar::erase::<$Struct>()
96 }
97
98 impl $zalsa_struct::Configuration for $Configuration {
99 const LOCATION: $zalsa::Location = $zalsa::Location {
100 file: file!(),
101 line: line!(),
102 };
103 const DEBUG_NAME: &'static str = stringify!($Struct);
104 const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
105
106 const PERSIST: bool = $persist;
107
108 type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}};
109
110 type Struct = $Struct;
111
112 type Fields = ($($field_ty,)*);
113
114 type Revisions = [$zalsa::Revision; $N];
115 type Durabilities = [$zalsa::Durability; $N];
116
117 $(
118 fn heap_size(value: &Self::Fields) -> Option<usize> {
119 Some($heap_size_fn(value))
120 }
121 )?
122
123 fn serialize<S: $zalsa::serde::Serializer>(
124 fields: &Self::Fields,
125 serializer: S,
126 ) -> ::std::result::Result<S::Ok, S::Error> {
127 $zalsa::macro_if! {
128 if $persist {
129 $($serialize_fn(fields, serializer))?
130 } else {
131 panic!("attempted to serialize value not marked with `persist` attribute")
132 }
133 }
134 }
135
136 fn deserialize<'de, D: $zalsa::serde::Deserializer<'de>>(
137 deserializer: D,
138 ) -> ::std::result::Result<Self::Fields, D::Error> {
139 $zalsa::macro_if! {
140 if $persist {
141 $($deserialize_fn(deserializer))?
142 } else {
143 panic!("attempted to deserialize value not marked with `persist` attribute")
144 }
145 }
146 }
147
148 }
149
150 impl $Configuration {
151 pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
152 Self::ingredient_(db.zalsa())
153 }
154
155 fn ingredient_(zalsa: &$zalsa::Zalsa) -> &$zalsa_struct::IngredientImpl<Self> {
156 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
157 $zalsa::IngredientCache::new();
158
159 unsafe {
162 CACHE.get_or_create(zalsa, || {
163 zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
164 })
165 }
166 }
167
168 pub fn ingredient_mut(zalsa_mut: &mut $zalsa::Zalsa) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
169 zalsa_mut.new_revision();
170 let index = zalsa_mut.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
171 let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
172 let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
173 (ingredient, runtime)
174 }
175 }
176
177 impl $zalsa::FromId for $Struct {
178 fn from_id(id: salsa::Id) -> Self {
179 Self(id)
180 }
181 }
182
183 impl $zalsa::AsId for $Struct {
184 fn as_id(&self) -> salsa::Id {
185 self.0
186 }
187 }
188
189 unsafe impl $zalsa::Update for $Struct {
190 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
191 if unsafe { *old_pointer } != new_value {
192 unsafe { *old_pointer = new_value };
193 true
194 } else {
195 false
196 }
197 }
198 }
199
200 $zalsa::macro_if! { $generate_debug_impl =>
201 impl ::std::fmt::Debug for $Struct {
202 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
203 Self::default_debug_fmt(*self, f)
204 }
205 }
206 }
207
208 impl $zalsa::SalsaStructInDb for $Struct {
209 type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
210 const LEAF_TYPE_IDS: &'static [$zalsa::ConstTypeId] = &[$zalsa::ConstTypeId::of::<$Struct>()];
211
212 fn lookup_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
213 aux.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
214 }
215
216 fn entries(
217 zalsa: &$zalsa::Zalsa
218 ) -> impl Iterator<Item = $zalsa::DatabaseKeyIndex> + '_ {
219 let ingredient_index = zalsa.lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
220 <$Configuration>::ingredient_(zalsa).entries(zalsa).map(|entry| entry.key())
221 }
222
223 #[inline]
224 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
225 if type_id == $zalsa::TypeId::of::<$Struct>() {
226 $zalsa::Some($Struct(id))
227 } else {
228 $zalsa::None
229 }
230 }
231
232 #[inline]
233 unsafe fn memo_table(
234 zalsa: &$zalsa::Zalsa,
235 id: $zalsa::Id,
236 current_revision: $zalsa::Revision,
237 ) -> $zalsa::MemoTableWithTypes<'_> {
238 unsafe { zalsa.table().memos::<$zalsa_struct::Value<$Configuration>>(id, current_revision) }
240 }
241 }
242
243 $zalsa::macro_if! { $persist =>
244 impl $zalsa::serde::Serialize for $Struct {
245 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
246 where
247 S: $zalsa::serde::Serializer,
248 {
249 $zalsa::serde::Serialize::serialize(&$zalsa::AsId::as_id(self), serializer)
250 }
251 }
252
253 impl<'de> $zalsa::serde::Deserialize<'de> for $Struct {
254 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
255 where
256 D: $zalsa::serde::Deserializer<'de>,
257 {
258 let id = $zalsa::Id::deserialize(deserializer)?;
259 Ok($zalsa::FromId::from_id(id))
260 }
261 }
262 }
263 impl $Struct {
264 #[inline]
265 pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self
266 where
267 $Db: ?Sized + salsa::Database,
269 {
270 Self::builder($($required_field_id,)*).new(db)
271 }
272
273 pub fn builder($($required_field_id: $required_field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
274 {
275 builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*)
276 }
277
278 $(
279 $(#[$field_attr])*
280 $field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
281 where
282 $Db: ?Sized + $zalsa::Database,
284 {
285 let (zalsa, zalsa_local) = db.zalsas();
286 let fields = $Configuration::ingredient_(zalsa).field(
287 zalsa,
288 zalsa_local,
289 self,
290 $field_index,
291 );
292 $zalsa::return_mode_expression!(
293 $field_option,
294 $field_ty,
295 &fields.$field_index,
296 )
297 }
298 )*
299
300 $(
301 #[must_use]
302 $field_setter_vis fn $field_setter_id<'db, $Db>(self, db: &'db mut $Db) -> impl salsa::Setter<FieldTy = $field_ty> + use<'db, $Db>
303 where
304 $Db: ?Sized + $zalsa::Database,
306 {
307 let zalsa = db.zalsa_mut();
308 let (ingredient, revision) = $Configuration::ingredient_mut(zalsa);
309 $zalsa::input::SetterImpl::new(
310 revision,
311 self,
312 $field_index,
313 ingredient,
314 |fields, f| ::std::mem::replace(&mut fields.$field_index, f),
315 )
316 }
317 )*
318
319 $zalsa::macro_if! { $is_singleton =>
320 pub fn try_get<$Db>(db: &$Db) -> Option<Self>
321 where
322 $Db: ?Sized + salsa::Database,
324 {
325 let zalsa = db.zalsa();
326 $Configuration::ingredient_(zalsa).get_singleton_input(zalsa)
327 }
328
329 #[track_caller]
330 pub fn get<$Db>(db: &$Db) -> Self
331 where
332 $Db: ?Sized + salsa::Database,
334 {
335 Self::try_get(db).unwrap()
336 }
337 }
338
339 pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
341 where
342 $(for<'__trivial_bounds> $field_ty: ::std::fmt::Debug),*
345 {
346 $zalsa::with_attached_database(|db| {
347 let zalsa = db.zalsa();
348 let fields = $Configuration::ingredient_(zalsa).leak_fields(zalsa, this);
349 let mut f = f.debug_struct(stringify!($Struct));
350 let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
351 $(
352 let f = f.field(stringify!($field_id), &fields.$field_index);
353 )*
354 f.finish()
355 }).unwrap_or_else(|| {
356 f.debug_struct(stringify!($Struct))
357 .field("[salsa id]", &this.0)
358 .finish()
359 })
360 }
361 }
362
363 impl $zalsa_struct::HasBuilder for $Struct {
364 type Builder = builder::$Builder;
365 }
366
367 impl builder::$Builder {
370 #[must_use]
372 pub fn new<$Db>(self, db: &$Db) -> $Struct
373 where
374 $Db: ?Sized + ::salsa::Database
376 {
377 let (zalsa, zalsa_local) = db.zalsas();
378 let current_revision = zalsa.current_revision();
379 let ingredient = $Configuration::ingredient_(zalsa);
380 let (fields, revision, durabilities) = builder::builder_into_inner(self, current_revision);
381 ingredient.new_input(zalsa, zalsa_local, fields, revision, durabilities)
382 }
383 }
384
385 mod builder {
386 use super::*;
387
388 use ::salsa::plumbing as $zalsa;
389 use $zalsa::input as $zalsa_struct;
390
391 pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
394 $Builder {
395 fields: ($($field_id,)*),
396 durabilities: [::salsa::Durability::default(); $N],
397 }
398 }
399
400 pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), [$zalsa::Revision; $N], [$zalsa::Durability; $N]) {
401 (builder.fields, [revision; $N], [$(builder.durabilities[$field_index]),*])
402 }
403
404 #[must_use]
405 pub struct $Builder {
406 fields: ($($field_ty,)*),
408
409 durabilities: [::salsa::Durability; $N],
411 }
412
413 impl $Builder {
414 pub fn durability(mut self, durability: ::salsa::Durability) -> Self {
418 self.durabilities = [durability; $N];
419 self
420 }
421
422 $($zalsa::maybe_default_tt! { $field_option =>
423 #[must_use]
425 pub fn $field_id(mut self, value: $field_ty) -> Self
426 {
427 self.fields.$field_index = value;
428 self
429 }
430 })*
431
432 $(
433 #[must_use]
435 pub fn $field_durability_id(mut self, durability: ::salsa::Durability) -> Self
436 {
437 self.durabilities[$field_index] = durability;
438 self
439 }
440 )*
441 }
442 }
443 };
444 };
445}