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        // Fields that are required (have no default value). Each item is the fields name and type.
36        required_fields: [$($required_field_id:ident $required_field_ty:ty),*],
37
38        // Names for the field durability methods on the builder (typically `foo_durability`)
39        field_durability_ids: [$($field_durability_id:ident),*],
40
41        // Number of fields
42        num_fields: $N:literal,
43
44        // If true, this is a singleton input.
45        is_singleton: $is_singleton:tt,
46
47        // If true, generate a debug impl.
48        generate_debug_impl: $generate_debug_impl:tt,
49
50        // Annoyingly macro-rules hygiene does not extend to items defined in the macro.
51        // We have the procedural macro generate names for those items that are
52        // not used elsewhere in the user's code.
53        unused_names: [
54            $zalsa:ident,
55            $zalsa_struct:ident,
56            $Configuration:ident,
57            $Builder:ident,
58            $CACHE:ident,
59            $Db:ident,
60        ]
61    ) => {
62        $(#[$attr])*
63        #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
64        $vis struct $Struct(salsa::Id);
65
66        const _: () = {
67            use salsa::plumbing as $zalsa;
68            use $zalsa::input as $zalsa_struct;
69
70            type $Configuration = $Struct;
71
72            impl $zalsa_struct::Configuration for $Configuration {
73                const DEBUG_NAME: &'static str = stringify!($Struct);
74                const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
75                type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}};
76
77                /// The input struct (which wraps an `Id`)
78                type Struct = $Struct;
79
80                /// A (possibly empty) tuple of the fields for this struct.
81                type Fields = ($($field_ty,)*);
82
83                /// A array of [`StampedValue<()>`](`StampedValue`) tuples, one per each of the value fields.
84                type Stamps = $zalsa::Array<$zalsa::Stamp, $N>;
85            }
86
87            impl $Configuration {
88                pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
89                    static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
90                        $zalsa::IngredientCache::new();
91                    CACHE.get_or_create(db, || {
92                        db.zalsa().add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
93                    })
94                }
95
96                pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
97                    let zalsa_mut = db.zalsa_mut();
98                    let index = zalsa_mut.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default());
99                    let current_revision = zalsa_mut.current_revision();
100                    let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
101                    let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
102                    (ingredient, runtime)
103                }
104            }
105
106            impl $zalsa::FromId for $Struct {
107                fn from_id(id: salsa::Id) -> Self {
108                    Self(id)
109                }
110            }
111
112            impl $zalsa::AsId for $Struct {
113                fn as_id(&self) -> salsa::Id {
114                    self.0
115                }
116            }
117
118            unsafe impl $zalsa::Update for $Struct {
119                unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
120                    if unsafe { *old_pointer } != new_value {
121                        unsafe { *old_pointer = new_value };
122                        true
123                    } else {
124                        false
125                    }
126                }
127            }
128
129            $zalsa::macro_if! { $generate_debug_impl =>
130                impl std::fmt::Debug for $Struct {
131                    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132                        Self::default_debug_fmt(*self, f)
133                    }
134                }
135            }
136
137            impl $zalsa::SalsaStructInDb for $Struct {
138                fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
139                    aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
140                }
141            }
142
143            impl $Struct {
144                #[inline]
145                pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self
146                where
147                    // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
148                    $Db: ?Sized + salsa::Database,
149                {
150                    Self::builder($($required_field_id,)*).new(db)
151                }
152
153                pub fn builder($($required_field_id: $required_field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
154                {
155                    builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*)
156                }
157
158                $(
159                    $field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::maybe_cloned_ty!($field_option, 'db, $field_ty)
160                    where
161                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
162                        $Db: ?Sized + $zalsa::Database,
163                    {
164                        let fields = $Configuration::ingredient(db.as_dyn_database()).field(
165                            db.as_dyn_database(),
166                            self,
167                            $field_index,
168                        );
169                        $zalsa::maybe_clone!(
170                            $field_option,
171                            $field_ty,
172                            &fields.$field_index,
173                        )
174                    }
175                )*
176
177                $(
178                    #[must_use]
179                    $field_setter_vis fn $field_setter_id<'db, $Db>(self, db: &'db mut $Db) -> impl salsa::Setter<FieldTy = $field_ty> + 'db
180                    where
181                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
182                        $Db: ?Sized + $zalsa::Database,
183                    {
184                        let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut());
185                        $zalsa::input::SetterImpl::new(
186                            revision,
187                            self,
188                            $field_index,
189                            ingredient,
190                            |fields, f| std::mem::replace(&mut fields.$field_index, f),
191                        )
192                    }
193                )*
194
195                $zalsa::macro_if! { $is_singleton =>
196                    pub fn try_get<$Db>(db: &$Db) -> Option<Self>
197                    where
198                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
199                        $Db: ?Sized + salsa::Database,
200                    {
201                        $Configuration::ingredient(db.as_dyn_database()).get_singleton_input()
202                    }
203
204                    #[track_caller]
205                    pub fn get<$Db>(db: &$Db) -> Self
206                    where
207                        // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
208                        $Db: ?Sized + salsa::Database,
209                    {
210                        Self::try_get(db).unwrap()
211                    }
212                }
213
214                /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
215                pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216                    $zalsa::with_attached_database(|db| {
217                        let fields = $Configuration::ingredient(db).leak_fields(db, this);
218                        let mut f = f.debug_struct(stringify!($Struct));
219                        let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
220                        $(
221                            let f = f.field(stringify!($field_id), &fields.$field_index);
222                        )*
223                        f.finish()
224                    }).unwrap_or_else(|| {
225                        f.debug_struct(stringify!($Struct))
226                            .field("[salsa id]", &this.0)
227                            .finish()
228                    })
229                }
230            }
231
232            impl $zalsa_struct::HasBuilder for $Struct {
233                type Builder = builder::$Builder;
234            }
235
236            // Implement `new` here instead of inside the builder module
237            // because $Configuration can't be named in `builder`.
238            impl builder::$Builder {
239                /// Creates the new input with the set values.
240                #[must_use]
241                pub fn new<$Db>(self, db: &$Db) -> $Struct
242                where
243                    // FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
244                    $Db: ?Sized + salsa::Database
245                {
246                    let current_revision = $zalsa::current_revision(db);
247                    let ingredient = $Configuration::ingredient(db.as_dyn_database());
248                    let (fields, stamps) = builder::builder_into_inner(self, current_revision);
249                    ingredient.new_input(db.as_dyn_database(), fields, stamps)
250                }
251            }
252
253            mod builder {
254                use super::*;
255
256                use salsa::plumbing as $zalsa;
257                use $zalsa::input as $zalsa_struct;
258
259                // These are standalone functions instead of methods on `Builder` to prevent
260                // that the enclosing module can call them.
261                pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
262                    $Builder {
263                        fields: ($($field_id,)*),
264                        durabilities: [salsa::Durability::default(); $N],
265                    }
266                }
267
268                pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), $zalsa::Array<$zalsa::Stamp, $N>) {
269                    let stamps = $zalsa::Array::new([
270                        $($zalsa::stamp(revision, builder.durabilities[$field_index])),*
271                    ]);
272
273                    (builder.fields, stamps)
274                }
275
276                #[must_use]
277                pub struct $Builder {
278                    /// The field values.
279                    fields: ($($field_ty,)*),
280
281                    /// The durabilities per field.
282                    durabilities: [salsa::Durability; $N],
283                }
284
285                impl $Builder {
286                    /// Sets the durability of all fields.
287                    ///
288                    /// Overrides any previously set durabilities.
289                    pub fn durability(mut self, durability: salsa::Durability) -> Self {
290                        self.durabilities = [durability; $N];
291                        self
292                    }
293
294                    $($zalsa::maybe_default_tt! { $field_option =>
295                        /// Sets the value of the field `$field_id`.
296                        #[must_use]
297                        pub fn $field_id(mut self, value: $field_ty) -> Self
298                        {
299                            self.fields.$field_index = value;
300                            self
301                        }
302                    })*
303
304                    $(
305                        /// Sets the durability for the field `$field_id`.
306                        #[must_use]
307                        pub fn $field_durability_id(mut self, durability: salsa::Durability) -> Self
308                        {
309                            self.durabilities[$field_index] = durability;
310                            self
311                        }
312                    )*
313                }
314            }
315        };
316    };
317}