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 required_fields: [$($required_field_id:ident $required_field_ty:ty),*],
37
38 field_durability_ids: [$($field_durability_id:ident),*],
40
41 num_fields: $N:literal,
43
44 is_singleton: $is_singleton:tt,
46
47 generate_debug_impl: $generate_debug_impl:tt,
49
50 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 #[allow(clippy::all)]
67 #[allow(dead_code)]
68 const _: () = {
69 use salsa::plumbing as $zalsa;
70 use $zalsa::input as $zalsa_struct;
71
72 type $Configuration = $Struct;
73
74 impl $zalsa_struct::Configuration for $Configuration {
75 const LOCATION: $zalsa::Location = $zalsa::Location {
76 file: file!(),
77 line: line!(),
78 };
79 const DEBUG_NAME: &'static str = stringify!($Struct);
80 const FIELD_DEBUG_NAMES: &'static [&'static str] = &[$(stringify!($field_id)),*];
81 type Singleton = $zalsa::macro_if! {if $is_singleton {$zalsa::input::Singleton} else {$zalsa::input::NotSingleton}};
82
83 type Struct = $Struct;
85
86 type Fields = ($($field_ty,)*);
88
89 type Stamps = [$zalsa::Stamp; $N];
91 }
92
93 impl $Configuration {
94 pub fn ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<Self> {
95 static CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Configuration>> =
96 $zalsa::IngredientCache::new();
97 let zalsa = db.zalsa();
98 CACHE.get_or_create(zalsa, || {
99 zalsa.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>()
100 })
101 }
102
103 pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
104 let zalsa_mut = db.zalsa_mut();
105 let current_revision = zalsa_mut.new_revision();
106 let index = zalsa_mut.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>();
107 let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
108 let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
109 (ingredient, runtime)
110 }
111 }
112
113 impl $zalsa::FromId for $Struct {
114 fn from_id(id: salsa::Id) -> Self {
115 Self(id)
116 }
117 }
118
119 impl $zalsa::AsId for $Struct {
120 fn as_id(&self) -> salsa::Id {
121 self.0
122 }
123 }
124
125 unsafe impl $zalsa::Update for $Struct {
126 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
127 if unsafe { *old_pointer } != new_value {
128 unsafe { *old_pointer = new_value };
129 true
130 } else {
131 false
132 }
133 }
134 }
135
136 $zalsa::macro_if! { $generate_debug_impl =>
137 impl std::fmt::Debug for $Struct {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 Self::default_debug_fmt(*self, f)
140 }
141 }
142 }
143
144 impl $zalsa::SalsaStructInDb for $Struct {
145 type MemoIngredientMap = $zalsa::MemoIngredientSingletonIndex;
146
147 fn lookup_or_create_ingredient_index(aux: &$zalsa::Zalsa) -> $zalsa::IngredientIndices {
148 aux.add_or_lookup_jar_by_type::<$zalsa_struct::JarImpl<$Configuration>>().into()
149 }
150
151 #[inline]
152 fn cast(id: $zalsa::Id, type_id: $zalsa::TypeId) -> $zalsa::Option<Self> {
153 if type_id == $zalsa::TypeId::of::<$Struct>() {
154 $zalsa::Some($Struct(id))
155 } else {
156 $zalsa::None
157 }
158 }
159 }
160
161 impl $Struct {
162 #[inline]
163 pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self
164 where
165 $Db: ?Sized + salsa::Database,
167 {
168 Self::builder($($required_field_id,)*).new(db)
169 }
170
171 pub fn builder($($required_field_id: $required_field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
172 {
173 builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*)
174 }
175
176 $(
177 $field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::maybe_cloned_ty!($field_option, 'db, $field_ty)
178 where
179 $Db: ?Sized + $zalsa::Database,
181 {
182 let fields = $Configuration::ingredient(db.as_dyn_database()).field(
183 db.as_dyn_database(),
184 self,
185 $field_index,
186 );
187 $zalsa::maybe_clone!(
188 $field_option,
189 $field_ty,
190 &fields.$field_index,
191 )
192 }
193 )*
194
195 $(
196 #[must_use]
197 $field_setter_vis fn $field_setter_id<'db, $Db>(self, db: &'db mut $Db) -> impl salsa::Setter<FieldTy = $field_ty> + 'db
198 where
199 $Db: ?Sized + $zalsa::Database,
201 {
202 let (ingredient, revision) = $Configuration::ingredient_mut(db.as_dyn_database_mut());
203 $zalsa::input::SetterImpl::new(
204 revision,
205 self,
206 $field_index,
207 ingredient,
208 |fields, f| std::mem::replace(&mut fields.$field_index, f),
209 )
210 }
211 )*
212
213 $zalsa::macro_if! { $is_singleton =>
214 pub fn try_get<$Db>(db: &$Db) -> Option<Self>
215 where
216 $Db: ?Sized + salsa::Database,
218 {
219 $Configuration::ingredient(db.as_dyn_database()).get_singleton_input(db)
220 }
221
222 #[track_caller]
223 pub fn get<$Db>(db: &$Db) -> Self
224 where
225 $Db: ?Sized + salsa::Database,
227 {
228 Self::try_get(db).unwrap()
229 }
230 }
231
232 pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
234 where
235 $(for<'__trivial_bounds> $field_ty: std::fmt::Debug),*
238 {
239 $zalsa::with_attached_database(|db| {
240 let fields = $Configuration::ingredient(db).leak_fields(db, this);
241 let mut f = f.debug_struct(stringify!($Struct));
242 let f = f.field("[salsa id]", &$zalsa::AsId::as_id(&this));
243 $(
244 let f = f.field(stringify!($field_id), &fields.$field_index);
245 )*
246 f.finish()
247 }).unwrap_or_else(|| {
248 f.debug_struct(stringify!($Struct))
249 .field("[salsa id]", &this.0)
250 .finish()
251 })
252 }
253 }
254
255 impl $zalsa_struct::HasBuilder for $Struct {
256 type Builder = builder::$Builder;
257 }
258
259 impl builder::$Builder {
262 #[must_use]
264 pub fn new<$Db>(self, db: &$Db) -> $Struct
265 where
266 $Db: ?Sized + salsa::Database
268 {
269 let current_revision = $zalsa::current_revision(db);
270 let ingredient = $Configuration::ingredient(db.as_dyn_database());
271 let (fields, stamps) = builder::builder_into_inner(self, current_revision);
272 ingredient.new_input(db.as_dyn_database(), fields, stamps)
273 }
274 }
275
276 mod builder {
277 use super::*;
278
279 use salsa::plumbing as $zalsa;
280 use $zalsa::input as $zalsa_struct;
281
282 pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
285 $Builder {
286 fields: ($($field_id,)*),
287 durabilities: [salsa::Durability::default(); $N],
288 }
289 }
290
291 pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), [$zalsa::Stamp; $N]) {
292 let stamps = [$($zalsa::stamp(revision, builder.durabilities[$field_index])),*];
293
294 (builder.fields, stamps)
295 }
296
297 #[must_use]
298 pub struct $Builder {
299 fields: ($($field_ty,)*),
301
302 durabilities: [salsa::Durability; $N],
304 }
305
306 impl $Builder {
307 pub fn durability(mut self, durability: salsa::Durability) -> Self {
311 self.durabilities = [durability; $N];
312 self
313 }
314
315 $($zalsa::maybe_default_tt! { $field_option =>
316 #[must_use]
318 pub fn $field_id(mut self, value: $field_ty) -> Self
319 {
320 self.fields.$field_index = value;
321 self
322 }
323 })*
324
325 $(
326 #[must_use]
328 pub fn $field_durability_id(mut self, durability: salsa::Durability) -> Self
329 {
330 self.durabilities[$field_index] = durability;
331 self
332 }
333 )*
334 }
335 }
336 };
337 };
338}