]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use std::convert::TryFrom; |
2 | ||
3 | use crate::parenthesized::Parenthesized; | |
4 | use heck::CamelCase; | |
5 | use proc_macro::TokenStream; | |
6 | use proc_macro2::Span; | |
7 | use quote::ToTokens; | |
f035d41b | 8 | use syn::{ |
3dfed10e XL |
9 | parse_macro_input, parse_quote, spanned::Spanned, Attribute, Error, FnArg, Ident, ItemTrait, |
10 | ReturnType, TraitItem, Type, | |
f035d41b XL |
11 | }; |
12 | ||
13 | /// Implementation for `[salsa::query_group]` decorator. | |
14 | pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream { | |
15 | let group_struct = parse_macro_input!(args as Ident); | |
16 | let input: ItemTrait = parse_macro_input!(input as ItemTrait); | |
17 | // println!("args: {:#?}", args); | |
18 | // println!("input: {:#?}", input); | |
19 | ||
3dfed10e | 20 | let input_span = input.span(); |
f035d41b | 21 | let (trait_attrs, salsa_attrs) = filter_attrs(input.attrs); |
3dfed10e XL |
22 | if !salsa_attrs.is_empty() { |
23 | return Error::new( | |
24 | input_span, | |
25 | format!("unsupported attributes: {:?}", salsa_attrs), | |
26 | ) | |
27 | .to_compile_error() | |
28 | .into(); | |
f035d41b XL |
29 | } |
30 | ||
31 | let trait_vis = input.vis; | |
32 | let trait_name = input.ident; | |
33 | let _generics = input.generics.clone(); | |
3dfed10e | 34 | let dyn_db = quote! { dyn #trait_name }; |
f035d41b XL |
35 | |
36 | // Decompose the trait into the corresponding queries. | |
37 | let mut queries = vec![]; | |
38 | for item in input.items { | |
39 | match item { | |
40 | TraitItem::Method(method) => { | |
41 | let mut storage = QueryStorage::Memoized; | |
42 | let mut cycle = None; | |
43 | let mut invoke = None; | |
3dfed10e | 44 | let query_name = method.sig.ident.to_string(); |
f035d41b XL |
45 | let mut query_type = Ident::new( |
46 | &format!("{}Query", method.sig.ident.to_string().to_camel_case()), | |
47 | Span::call_site(), | |
48 | ); | |
49 | let mut num_storages = 0; | |
50 | ||
51 | // Extract attributes. | |
52 | let (attrs, salsa_attrs) = filter_attrs(method.attrs); | |
3dfed10e | 53 | for SalsaAttr { name, tts, span } in salsa_attrs { |
f035d41b XL |
54 | match name.as_str() { |
55 | "memoized" => { | |
56 | storage = QueryStorage::Memoized; | |
57 | num_storages += 1; | |
58 | } | |
59 | "dependencies" => { | |
60 | storage = QueryStorage::Dependencies; | |
61 | num_storages += 1; | |
62 | } | |
63 | "input" => { | |
64 | storage = QueryStorage::Input; | |
65 | num_storages += 1; | |
66 | } | |
67 | "interned" => { | |
68 | storage = QueryStorage::Interned; | |
69 | num_storages += 1; | |
70 | } | |
71 | "cycle" => { | |
72 | cycle = Some(parse_macro_input!(tts as Parenthesized<syn::Path>).0); | |
73 | } | |
74 | "invoke" => { | |
75 | invoke = Some(parse_macro_input!(tts as Parenthesized<syn::Path>).0); | |
76 | } | |
77 | "query_type" => { | |
78 | query_type = parse_macro_input!(tts as Parenthesized<Ident>).0; | |
79 | } | |
80 | "transparent" => { | |
81 | storage = QueryStorage::Transparent; | |
82 | num_storages += 1; | |
83 | } | |
3dfed10e XL |
84 | _ => { |
85 | return Error::new(span, format!("unknown salsa attribute `{}`", name)) | |
86 | .to_compile_error() | |
87 | .into(); | |
88 | } | |
f035d41b XL |
89 | } |
90 | } | |
91 | ||
92 | // Check attribute combinations. | |
93 | if num_storages > 1 { | |
3dfed10e XL |
94 | return Error::new(method.sig.span(), "multiple storage attributes specified") |
95 | .to_compile_error() | |
96 | .into(); | |
f035d41b | 97 | } |
3dfed10e XL |
98 | match &invoke { |
99 | Some(invoke) if storage == QueryStorage::Input => { | |
100 | return Error::new( | |
101 | invoke.span(), | |
102 | "#[salsa::invoke] cannot be set on #[salsa::input] queries", | |
103 | ) | |
104 | .to_compile_error() | |
105 | .into(); | |
106 | } | |
107 | _ => {} | |
f035d41b XL |
108 | } |
109 | ||
110 | // Extract keys. | |
111 | let mut iter = method.sig.inputs.iter(); | |
112 | match iter.next() { | |
113 | Some(FnArg::Receiver(sr)) if sr.mutability.is_none() => (), | |
3dfed10e XL |
114 | _ => { |
115 | return Error::new( | |
116 | method.sig.span(), | |
117 | format!( | |
118 | "first argument of query `{}` must be `&self`", | |
119 | method.sig.ident, | |
120 | ), | |
121 | ) | |
122 | .to_compile_error() | |
123 | .into(); | |
124 | } | |
f035d41b XL |
125 | } |
126 | let mut keys: Vec<Type> = vec![]; | |
127 | for arg in iter { | |
128 | match *arg { | |
129 | FnArg::Typed(ref arg) => { | |
130 | keys.push((*arg.ty).clone()); | |
131 | } | |
3dfed10e XL |
132 | ref arg => { |
133 | return Error::new( | |
134 | arg.span(), | |
135 | format!( | |
136 | "unsupported argument `{:?}` of `{}`", | |
137 | arg, method.sig.ident, | |
138 | ), | |
139 | ) | |
140 | .to_compile_error() | |
141 | .into(); | |
142 | } | |
f035d41b XL |
143 | } |
144 | } | |
145 | ||
146 | // Extract value. | |
147 | let value = match method.sig.output { | |
148 | ReturnType::Type(_, ref ty) => ty.as_ref().clone(), | |
3dfed10e XL |
149 | ref ret => { |
150 | return Error::new( | |
151 | ret.span(), | |
152 | format!( | |
153 | "unsupported return type `{:?}` of `{}`", | |
154 | ret, method.sig.ident | |
155 | ), | |
156 | ) | |
157 | .to_compile_error() | |
158 | .into(); | |
159 | } | |
f035d41b XL |
160 | }; |
161 | ||
162 | // For `#[salsa::interned]` keys, we create a "lookup key" automatically. | |
163 | // | |
164 | // For a query like: | |
165 | // | |
166 | // fn foo(&self, x: Key1, y: Key2) -> u32 | |
167 | // | |
168 | // we would create | |
169 | // | |
170 | // fn lookup_foo(&self, x: u32) -> (Key1, Key2) | |
171 | let lookup_query = if let QueryStorage::Interned = storage { | |
172 | let lookup_query_type = Ident::new( | |
173 | &format!( | |
174 | "{}LookupQuery", | |
175 | method.sig.ident.to_string().to_camel_case() | |
176 | ), | |
177 | Span::call_site(), | |
178 | ); | |
179 | let lookup_fn_name = Ident::new( | |
180 | &format!("lookup_{}", method.sig.ident.to_string()), | |
181 | method.sig.ident.span(), | |
182 | ); | |
183 | let keys = &keys; | |
184 | let lookup_value: Type = parse_quote!((#(#keys),*)); | |
185 | let lookup_keys = vec![value.clone()]; | |
186 | Some(Query { | |
187 | query_type: lookup_query_type, | |
3dfed10e | 188 | query_name: format!("lookup_{}", query_name), |
f035d41b XL |
189 | fn_name: lookup_fn_name, |
190 | attrs: vec![], // FIXME -- some automatically generated docs on this method? | |
191 | storage: QueryStorage::InternedLookup { | |
192 | intern_query_type: query_type.clone(), | |
193 | }, | |
194 | keys: lookup_keys, | |
195 | value: lookup_value, | |
196 | invoke: None, | |
197 | cycle: cycle.clone(), | |
198 | }) | |
199 | } else { | |
200 | None | |
201 | }; | |
202 | ||
203 | queries.push(Query { | |
204 | query_type, | |
3dfed10e | 205 | query_name, |
f035d41b XL |
206 | fn_name: method.sig.ident, |
207 | attrs, | |
208 | storage, | |
209 | keys, | |
210 | value, | |
211 | invoke, | |
212 | cycle, | |
213 | }); | |
214 | ||
215 | queries.extend(lookup_query); | |
216 | } | |
217 | _ => (), | |
218 | } | |
219 | } | |
220 | ||
f035d41b XL |
221 | let group_storage = Ident::new( |
222 | &format!("{}GroupStorage__", trait_name.to_string()), | |
223 | Span::call_site(), | |
224 | ); | |
225 | ||
226 | let mut query_fn_declarations = proc_macro2::TokenStream::new(); | |
227 | let mut query_fn_definitions = proc_macro2::TokenStream::new(); | |
f035d41b | 228 | let mut storage_fields = proc_macro2::TokenStream::new(); |
3dfed10e | 229 | let mut queries_with_storage = vec![]; |
f035d41b XL |
230 | for query in &queries { |
231 | let key_names: &Vec<_> = &(0..query.keys.len()) | |
232 | .map(|i| Ident::new(&format!("key{}", i), Span::call_site())) | |
233 | .collect(); | |
234 | let keys = &query.keys; | |
235 | let value = &query.value; | |
236 | let fn_name = &query.fn_name; | |
237 | let qt = &query.query_type; | |
238 | let attrs = &query.attrs; | |
239 | ||
240 | query_fn_declarations.extend(quote! { | |
241 | #(#attrs)* | |
242 | fn #fn_name(&self, #(#key_names: #keys),*) -> #value; | |
243 | }); | |
244 | ||
245 | // Special case: transparent queries don't create actual storage, | |
246 | // just inline the definition | |
247 | if let QueryStorage::Transparent = query.storage { | |
248 | let invoke = query.invoke_tt(); | |
249 | query_fn_definitions.extend(quote! { | |
250 | fn #fn_name(&self, #(#key_names: #keys),*) -> #value { | |
251 | #invoke(self, #(#key_names),*) | |
252 | } | |
253 | }); | |
254 | continue; | |
255 | } | |
256 | ||
3dfed10e XL |
257 | queries_with_storage.push(fn_name); |
258 | ||
f035d41b XL |
259 | query_fn_definitions.extend(quote! { |
260 | fn #fn_name(&self, #(#key_names: #keys),*) -> #value { | |
3dfed10e XL |
261 | // Create a shim to force the code to be monomorphized in the |
262 | // query crate. Our experiments revealed that this makes a big | |
263 | // difference in total compilation time in rust-analyzer, though | |
264 | // it's not totally obvious why that should be. | |
265 | fn __shim(db: &dyn #trait_name, #(#key_names: #keys),*) -> #value { | |
266 | salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*)) | |
267 | } | |
268 | __shim(self, #(#key_names),*) | |
269 | ||
f035d41b XL |
270 | } |
271 | }); | |
272 | ||
273 | // For input queries, we need `set_foo` etc | |
274 | if let QueryStorage::Input = query.storage { | |
275 | let set_fn_name = Ident::new(&format!("set_{}", fn_name), fn_name.span()); | |
276 | let set_with_durability_fn_name = | |
277 | Ident::new(&format!("set_{}_with_durability", fn_name), fn_name.span()); | |
278 | ||
279 | let set_fn_docs = format!( | |
280 | " | |
281 | Set the value of the `{fn_name}` input. | |
282 | ||
283 | See `{fn_name}` for details. | |
284 | ||
285 | *Note:* Setting values will trigger cancellation | |
286 | of any ongoing queries; this method blocks until | |
287 | those queries have been cancelled. | |
288 | ", | |
289 | fn_name = fn_name | |
290 | ); | |
291 | ||
292 | let set_constant_fn_docs = format!( | |
293 | " | |
294 | Set the value of the `{fn_name}` input and promise | |
295 | that its value will never change again. | |
296 | ||
297 | See `{fn_name}` for details. | |
298 | ||
299 | *Note:* Setting values will trigger cancellation | |
300 | of any ongoing queries; this method blocks until | |
301 | those queries have been cancelled. | |
302 | ", | |
303 | fn_name = fn_name | |
304 | ); | |
305 | ||
306 | query_fn_declarations.extend(quote! { | |
307 | # [doc = #set_fn_docs] | |
308 | fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value); | |
309 | ||
310 | ||
311 | # [doc = #set_constant_fn_docs] | |
312 | fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability); | |
313 | }); | |
314 | ||
315 | query_fn_definitions.extend(quote! { | |
316 | fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value) { | |
3dfed10e XL |
317 | fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value) { |
318 | salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__) | |
319 | } | |
320 | __shim(self, #(#key_names,)* value__) | |
f035d41b XL |
321 | } |
322 | ||
323 | fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { | |
3dfed10e XL |
324 | fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { |
325 | salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__) | |
326 | } | |
327 | __shim(self, #(#key_names,)* value__ ,durability__) | |
f035d41b XL |
328 | } |
329 | }); | |
330 | } | |
331 | ||
f035d41b XL |
332 | // A field for the storage struct |
333 | // | |
334 | // FIXME(#120): the pub should not be necessary once we complete the transition | |
335 | storage_fields.extend(quote! { | |
3dfed10e | 336 | pub #fn_name: std::sync::Arc<<#qt as salsa::Query>::Storage>, |
f035d41b | 337 | }); |
f035d41b XL |
338 | } |
339 | ||
340 | // Emit the trait itself. | |
341 | let mut output = { | |
342 | let bounds = &input.supertraits; | |
343 | quote! { | |
344 | #(#trait_attrs)* | |
3dfed10e XL |
345 | #trait_vis trait #trait_name : |
346 | salsa::Database + | |
347 | salsa::plumbing::HasQueryGroup<#group_struct> + | |
348 | #bounds | |
349 | { | |
f035d41b XL |
350 | #query_fn_declarations |
351 | } | |
352 | } | |
353 | }; | |
354 | ||
355 | // Emit the query group struct and impl of `QueryGroup`. | |
356 | output.extend(quote! { | |
357 | /// Representative struct for the query group. | |
358 | #trait_vis struct #group_struct { } | |
359 | ||
3dfed10e | 360 | impl salsa::plumbing::QueryGroup for #group_struct |
f035d41b | 361 | { |
3dfed10e XL |
362 | type DynDb = #dyn_db; |
363 | type GroupStorage = #group_storage; | |
f035d41b XL |
364 | } |
365 | }); | |
366 | ||
367 | // Emit an impl of the trait | |
368 | output.extend({ | |
3dfed10e | 369 | let bounds = input.supertraits.clone(); |
f035d41b | 370 | quote! { |
3dfed10e | 371 | impl<DB> #trait_name for DB |
f035d41b | 372 | where |
3dfed10e XL |
373 | DB: #bounds, |
374 | DB: salsa::Database, | |
375 | DB: salsa::plumbing::HasQueryGroup<#group_struct>, | |
f035d41b XL |
376 | { |
377 | #query_fn_definitions | |
378 | } | |
379 | } | |
380 | }); | |
381 | ||
3dfed10e XL |
382 | let non_transparent_queries = || { |
383 | queries.iter().filter(|q| match q.storage { | |
384 | QueryStorage::Transparent => false, | |
385 | _ => true, | |
386 | }) | |
387 | }; | |
388 | ||
f035d41b | 389 | // Emit the query types. |
3dfed10e | 390 | for (query, query_index) in non_transparent_queries().zip(0_u16..) { |
f035d41b XL |
391 | let fn_name = &query.fn_name; |
392 | let qt = &query.query_type; | |
393 | ||
f035d41b | 394 | let storage = match &query.storage { |
3dfed10e XL |
395 | QueryStorage::Memoized => quote!(salsa::plumbing::MemoizedStorage<Self>), |
396 | QueryStorage::Dependencies => quote!(salsa::plumbing::DependencyStorage<Self>), | |
397 | QueryStorage::Input => quote!(salsa::plumbing::InputStorage<Self>), | |
398 | QueryStorage::Interned => quote!(salsa::plumbing::InternedStorage<Self>), | |
f035d41b | 399 | QueryStorage::InternedLookup { intern_query_type } => { |
3dfed10e | 400 | quote!(salsa::plumbing::LookupInternedStorage<Self, #intern_query_type>) |
f035d41b | 401 | } |
3dfed10e | 402 | QueryStorage::Transparent => panic!("should have been filtered"), |
f035d41b XL |
403 | }; |
404 | let keys = &query.keys; | |
405 | let value = &query.value; | |
3dfed10e | 406 | let query_name = &query.query_name; |
f035d41b XL |
407 | |
408 | // Emit the query struct and implement the Query trait on it. | |
409 | output.extend(quote! { | |
410 | #[derive(Default, Debug)] | |
411 | #trait_vis struct #qt; | |
3dfed10e | 412 | }); |
f035d41b | 413 | |
3dfed10e XL |
414 | output.extend(quote! { |
415 | impl #qt { | |
416 | /// Get access to extra methods pertaining to this query. For | |
417 | /// example, you can use this to run the GC (`sweep`) across a | |
418 | /// single input. You can also use it to invoke this query, though | |
419 | /// it's more common to use the trait method on the database | |
420 | /// itself. | |
421 | #trait_vis fn in_db(self, db: &#dyn_db) -> salsa::QueryTable<'_, Self> | |
422 | { | |
423 | salsa::plumbing::get_query_table::<#qt>(db) | |
424 | } | |
425 | } | |
426 | }); | |
427 | ||
428 | if query.storage.supports_mut() {} | |
429 | output.extend(quote! { | |
430 | impl #qt { | |
431 | /// Like `in_db`, but gives access to methods for setting the | |
432 | /// value of an input. Not applicable to derived queries. | |
433 | /// | |
434 | /// # Threads, cancellation, and blocking | |
435 | /// | |
436 | /// Mutating the value of a query cannot be done while there are | |
437 | /// still other queries executing. If you are using your database | |
438 | /// within a single thread, this is not a problem: you only have | |
439 | /// `&self` access to the database, but this method requires `&mut | |
440 | /// self`. | |
441 | /// | |
442 | /// However, if you have used `snapshot` to create other threads, | |
443 | /// then attempts to `set` will **block the current thread** until | |
444 | /// those snapshots are dropped (usually when those threads | |
445 | /// complete). This also implies that if you create a snapshot but | |
446 | /// do not send it to another thread, then invoking `set` will | |
447 | /// deadlock. | |
448 | /// | |
449 | /// Before blocking, the thread that is attempting to `set` will | |
450 | /// also set a cancellation flag. In the threads operating on | |
451 | /// snapshots, you can use the [`is_current_revision_canceled`] | |
452 | /// method to check for this flag and bring those operations to a | |
453 | /// close, thus allowing the `set` to succeed. Ignoring this flag | |
454 | /// may lead to "starvation", meaning that the thread attempting | |
455 | /// to `set` has to wait a long, long time. =) | |
456 | /// | |
457 | /// [`is_current_revision_canceled`]: struct.Runtime.html#method.is_current_revision_canceled | |
458 | #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> salsa::QueryTableMut<'_, Self> | |
459 | { | |
460 | salsa::plumbing::get_query_table_mut::<#qt>(db) | |
461 | } | |
462 | } | |
463 | ||
464 | // ANCHOR:Query_impl | |
465 | impl salsa::Query for #qt | |
f035d41b XL |
466 | { |
467 | type Key = (#(#keys),*); | |
468 | type Value = #value; | |
469 | type Storage = #storage; | |
470 | type Group = #group_struct; | |
3dfed10e XL |
471 | type GroupStorage = #group_storage; |
472 | type DynDb = #dyn_db; | |
473 | ||
474 | const QUERY_INDEX: u16 = #query_index; | |
475 | ||
476 | const QUERY_NAME: &'static str = #query_name; | |
f035d41b XL |
477 | |
478 | fn query_storage( | |
479 | group_storage: &Self::GroupStorage, | |
480 | ) -> &std::sync::Arc<Self::Storage> { | |
481 | &group_storage.#fn_name | |
482 | } | |
f035d41b | 483 | } |
3dfed10e | 484 | // ANCHOR_END:Query_impl |
f035d41b XL |
485 | }); |
486 | ||
487 | // Implement the QueryFunction trait for queries which need it. | |
488 | if query.storage.needs_query_function() { | |
489 | let span = query.fn_name.span(); | |
490 | let key_names: &Vec<_> = &(0..query.keys.len()) | |
491 | .map(|i| Ident::new(&format!("key{}", i), Span::call_site())) | |
492 | .collect(); | |
493 | let key_pattern = if query.keys.len() == 1 { | |
494 | quote! { #(#key_names),* } | |
495 | } else { | |
496 | quote! { (#(#key_names),*) } | |
497 | }; | |
498 | let invoke = query.invoke_tt(); | |
499 | ||
500 | let recover = if let Some(cycle_recovery_fn) = &query.cycle { | |
501 | quote! { | |
3dfed10e XL |
502 | fn recover(db: &Self::DynDb, cycle: &[salsa::DatabaseKeyIndex], #key_pattern: &<Self as salsa::Query>::Key) |
503 | -> Option<<Self as salsa::Query>::Value> { | |
f035d41b XL |
504 | Some(#cycle_recovery_fn( |
505 | db, | |
3dfed10e | 506 | &cycle.iter().map(|k| format!("{:?}", k.debug(db))).collect::<Vec<String>>(), |
f035d41b XL |
507 | #(#key_names),* |
508 | )) | |
509 | } | |
510 | } | |
511 | } else { | |
512 | quote! {} | |
513 | }; | |
514 | ||
515 | output.extend(quote_spanned! {span=> | |
3dfed10e XL |
516 | // ANCHOR:QueryFunction_impl |
517 | impl salsa::plumbing::QueryFunction for #qt | |
f035d41b | 518 | { |
3dfed10e XL |
519 | fn execute(db: &Self::DynDb, #key_pattern: <Self as salsa::Query>::Key) |
520 | -> <Self as salsa::Query>::Value { | |
f035d41b XL |
521 | #invoke(db, #(#key_names),*) |
522 | } | |
523 | ||
524 | #recover | |
525 | } | |
3dfed10e | 526 | // ANCHOR_END:QueryFunction_impl |
f035d41b XL |
527 | }); |
528 | } | |
529 | } | |
530 | ||
3dfed10e XL |
531 | let mut fmt_ops = proc_macro2::TokenStream::new(); |
532 | for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { | |
533 | fmt_ops.extend(quote! { | |
534 | #query_index => { | |
535 | salsa::plumbing::QueryStorageOps::fmt_index( | |
536 | &*self.#fn_name, db, input, fmt, | |
537 | ) | |
538 | } | |
539 | }); | |
540 | } | |
541 | ||
542 | let mut maybe_changed_ops = proc_macro2::TokenStream::new(); | |
543 | for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { | |
544 | maybe_changed_ops.extend(quote! { | |
545 | #query_index => { | |
546 | salsa::plumbing::QueryStorageOps::maybe_changed_since( | |
547 | &*self.#fn_name, db, input, revision | |
548 | ) | |
549 | } | |
550 | }); | |
551 | } | |
f035d41b XL |
552 | |
553 | let mut for_each_ops = proc_macro2::TokenStream::new(); | |
3dfed10e | 554 | for Query { fn_name, .. } in non_transparent_queries() { |
f035d41b XL |
555 | for_each_ops.extend(quote! { |
556 | op(&*self.#fn_name); | |
557 | }); | |
558 | } | |
559 | ||
560 | // Emit query group storage struct | |
f035d41b | 561 | output.extend(quote! { |
3dfed10e | 562 | #trait_vis struct #group_storage { |
f035d41b XL |
563 | #storage_fields |
564 | } | |
565 | ||
3dfed10e XL |
566 | // ANCHOR:group_storage_new |
567 | impl #group_storage { | |
568 | #trait_vis fn new(group_index: u16) -> Self { | |
f035d41b | 569 | #group_storage { |
3dfed10e XL |
570 | #( |
571 | #queries_with_storage: | |
572 | std::sync::Arc::new(salsa::plumbing::QueryStorageOps::new(group_index)), | |
573 | )* | |
f035d41b XL |
574 | } |
575 | } | |
576 | } | |
3dfed10e XL |
577 | // ANCHOR_END:group_storage_new |
578 | ||
579 | // ANCHOR:group_storage_methods | |
580 | impl #group_storage { | |
581 | #trait_vis fn fmt_index( | |
582 | &self, | |
583 | db: &#dyn_db, | |
584 | input: salsa::DatabaseKeyIndex, | |
585 | fmt: &mut std::fmt::Formatter<'_>, | |
586 | ) -> std::fmt::Result { | |
587 | match input.query_index() { | |
588 | #fmt_ops | |
589 | i => panic!("salsa: impossible query index {}", i), | |
590 | } | |
591 | } | |
592 | ||
593 | #trait_vis fn maybe_changed_since( | |
594 | &self, | |
595 | db: &#dyn_db, | |
596 | input: salsa::DatabaseKeyIndex, | |
597 | revision: salsa::Revision, | |
598 | ) -> bool { | |
599 | match input.query_index() { | |
600 | #maybe_changed_ops | |
601 | i => panic!("salsa: impossible query index {}", i), | |
602 | } | |
603 | } | |
f035d41b | 604 | |
f035d41b XL |
605 | #trait_vis fn for_each_query( |
606 | &self, | |
3dfed10e XL |
607 | _runtime: &salsa::Runtime, |
608 | mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps), | |
f035d41b XL |
609 | ) { |
610 | #for_each_ops | |
611 | } | |
612 | } | |
3dfed10e | 613 | // ANCHOR_END:group_storage_methods |
f035d41b XL |
614 | }); |
615 | ||
616 | if std::env::var("SALSA_DUMP").is_ok() { | |
617 | println!("~~~ query_group"); | |
618 | println!("{}", output.to_string()); | |
619 | println!("~~~ query_group"); | |
620 | } | |
621 | ||
622 | output.into() | |
623 | } | |
624 | ||
625 | struct SalsaAttr { | |
626 | name: String, | |
627 | tts: TokenStream, | |
3dfed10e XL |
628 | span: Span, |
629 | } | |
630 | ||
631 | impl std::fmt::Debug for SalsaAttr { | |
632 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
633 | write!(fmt, "{:?}", self.name) | |
634 | } | |
f035d41b XL |
635 | } |
636 | ||
637 | impl TryFrom<syn::Attribute> for SalsaAttr { | |
638 | type Error = syn::Attribute; | |
3dfed10e | 639 | |
f035d41b XL |
640 | fn try_from(attr: syn::Attribute) -> Result<SalsaAttr, syn::Attribute> { |
641 | if is_not_salsa_attr_path(&attr.path) { | |
642 | return Err(attr); | |
643 | } | |
644 | ||
3dfed10e | 645 | let span = attr.span(); |
f035d41b XL |
646 | let name = attr.path.segments[1].ident.to_string(); |
647 | let tts = attr.tokens.into(); | |
3dfed10e XL |
648 | |
649 | Ok(SalsaAttr { name, tts, span }) | |
f035d41b XL |
650 | } |
651 | } | |
652 | ||
653 | fn is_not_salsa_attr_path(path: &syn::Path) -> bool { | |
654 | path.segments | |
655 | .first() | |
656 | .map(|s| s.ident != "salsa") | |
657 | .unwrap_or(true) | |
658 | || path.segments.len() != 2 | |
659 | } | |
660 | ||
661 | fn filter_attrs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<SalsaAttr>) { | |
662 | let mut other = vec![]; | |
663 | let mut salsa = vec![]; | |
664 | // Leave non-salsa attributes untouched. These are | |
665 | // attributes that don't start with `salsa::` or don't have | |
666 | // exactly two segments in their path. | |
667 | // Keep the salsa attributes around. | |
668 | for attr in attrs { | |
669 | match SalsaAttr::try_from(attr) { | |
670 | Ok(it) => salsa.push(it), | |
671 | Err(it) => other.push(it), | |
672 | } | |
673 | } | |
674 | (other, salsa) | |
675 | } | |
676 | ||
677 | #[derive(Debug)] | |
678 | struct Query { | |
679 | fn_name: Ident, | |
3dfed10e | 680 | query_name: String, |
f035d41b XL |
681 | attrs: Vec<syn::Attribute>, |
682 | query_type: Ident, | |
683 | storage: QueryStorage, | |
684 | keys: Vec<syn::Type>, | |
685 | value: syn::Type, | |
686 | invoke: Option<syn::Path>, | |
687 | cycle: Option<syn::Path>, | |
688 | } | |
689 | ||
690 | impl Query { | |
691 | fn invoke_tt(&self) -> proc_macro2::TokenStream { | |
692 | match &self.invoke { | |
693 | Some(i) => i.into_token_stream(), | |
694 | None => self.fn_name.clone().into_token_stream(), | |
695 | } | |
696 | } | |
697 | } | |
698 | ||
699 | #[derive(Debug, Clone, PartialEq, Eq)] | |
700 | enum QueryStorage { | |
701 | Memoized, | |
702 | Dependencies, | |
703 | Input, | |
704 | Interned, | |
705 | InternedLookup { intern_query_type: Ident }, | |
706 | Transparent, | |
707 | } | |
708 | ||
709 | impl QueryStorage { | |
3dfed10e | 710 | /// Do we need a `QueryFunction` impl for this type of query? |
f035d41b XL |
711 | fn needs_query_function(&self) -> bool { |
712 | match self { | |
713 | QueryStorage::Input | |
714 | | QueryStorage::Interned | |
715 | | QueryStorage::InternedLookup { .. } | |
716 | | QueryStorage::Transparent => false, | |
717 | QueryStorage::Memoized | QueryStorage::Dependencies => true, | |
718 | } | |
719 | } | |
3dfed10e XL |
720 | |
721 | /// Does this type of query support `&mut` operations? | |
722 | fn supports_mut(&self) -> bool { | |
723 | match self { | |
724 | QueryStorage::Input => true, | |
725 | QueryStorage::Interned | |
726 | | QueryStorage::InternedLookup { .. } | |
727 | | QueryStorage::Transparent | |
728 | | QueryStorage::Memoized | |
729 | | QueryStorage::Dependencies => false, | |
730 | } | |
731 | } | |
f035d41b | 732 | } |