Skip to main content

salsa_macro_rules/
setup_input_struct.rs

1/// Macro for setting up a function that must intern its arguments.
2#[macro_export]
3macro_rules! setup_input_struct {
4    (
5        // Attributes on the struct
6        attrs: [$(#[$attr:meta]),*],
7
8        // Visibility of the struct
9        vis: $vis:vis,
10
11        // Name of the struct
12        Struct: $Struct:ident,
13
14        // Name user gave for `new`
15        new_fn: $new_fn:ident,
16
17        // A series of option tuples; see `setup_tracked_struct` macro
18        field_options: [$($field_option:tt),*],
19
20        // Field names
21        field_ids: [$($field_id:ident),*],
22
23        // Names for field getter methods (typically `foo`)
24        field_getters: [$($field_getter_vis:vis $field_getter_id:ident),*],
25
26        // Names for field setter methods (typically `set_foo`)
27        field_setters: [$($field_setter_vis:vis $field_setter_id:ident),*],
28
29        // Field types
30        field_tys: [$($field_ty:ty),*],
31
32        // Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`).
33        field_indices: [$($field_index:tt),*],
34
35        // Attributes for each field
36        field_attrs: [$([$(#[$field_attr:meta]),*]),*],
37
38        // Fields that are required (have no default value). Each item is the fields name and type.
39        required_fields: [$($required_field_id:ident $required_field_ty:ty),*],
40
41        // Names for the field durability methods on the builder (typically `foo_durability`)
42        field_durability_ids: [$($field_durability_id:ident),*],
43
44        // Number of fields
45        num_fields: $N:literal,
46
47        // If true, this is a singleton input.
48        is_singleton: $is_singleton:tt,
49
50        // If true, generate a debug impl.
51        generate_debug_impl: $generate_debug_impl:tt,
52
53        // The function used to implement `C::heap_size`.
54        heap_size_fn: $($heap_size_fn:path)?,
55
56        // If `true`, `serialize_fn` and `deserialize_fn` have been provided.
57        persist: $persist:tt,
58
59        // The path to the `serialize` function for the value's fields.
60        serialize_fn: $($serialize_fn:path)?,
61
62        // The path to the `serialize` function for the value's fields.
63        deserialize_fn: $($deserialize_fn:path)?,
64
65        // Annoyingly macro-rules hygiene does not extend to items defined in the macro.
66        // We have the procedural macro generate names for those items that are
67        // not used elsewhere in the user's code.
68        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                    // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
160                    // ingredient created by our jar is the struct ingredient.
161                    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                    // SAFETY: Guaranteed by caller.
239                    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                    // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
268                    $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                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
283                        $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                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
305                        $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                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
323                        $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                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
333                        $Db: ?Sized + salsa::Database,
334                    {
335                        Self::try_get(db).unwrap()
336                    }
337                }
338
339                /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
340                pub fn default_debug_fmt(this: Self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
341                where
342                    // rustc rejects trivial bounds, but it cannot see through higher-ranked bounds
343                    // with its check :^)
344                    $(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            // Implement `new` here instead of inside the builder module
368            // because $Configuration can't be named in `builder`.
369            impl builder::$Builder {
370                /// Creates the new input with the set values.
371                #[must_use]
372                pub fn new<$Db>(self, db: &$Db) -> $Struct
373                where
374                    // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
375                    $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                // These are standalone functions instead of methods on `Builder` to prevent
392                // that the enclosing module can call them.
393                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                    /// The field values.
407                    fields: ($($field_ty,)*),
408
409                    /// The durabilities per field.
410                    durabilities: [::salsa::Durability; $N],
411                }
412
413                impl $Builder {
414                    /// Sets the durability of all fields.
415                    ///
416                    /// Overrides any previously set durabilities.
417                    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                        /// Sets the value of the field `$field_id`.
424                        #[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                        /// Sets the durability for the field `$field_id`.
434                        #[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}