]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_macros/src/query.rs
Rollup merge of #77106 - matthiaskrgr:changelog_seen, r=Mark-Simulacrum
[rust.git] / compiler / rustc_macros / src / query.rs
1 use proc_macro::TokenStream;
2 use proc_macro2::{Delimiter, TokenTree};
3 use quote::quote;
4 use syn::parse::{Parse, ParseStream, Result};
5 use syn::punctuated::Punctuated;
6 use syn::spanned::Spanned;
7 use syn::{
8     braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident,
9     ReturnType, Token, Type,
10 };
11
12 #[allow(non_camel_case_types)]
13 mod kw {
14     syn::custom_keyword!(query);
15 }
16
17 /// Ident or a wildcard `_`.
18 struct IdentOrWild(Ident);
19
20 impl Parse for IdentOrWild {
21     fn parse(input: ParseStream<'_>) -> Result<Self> {
22         Ok(if input.peek(Token![_]) {
23             let underscore = input.parse::<Token![_]>()?;
24             IdentOrWild(Ident::new("_", underscore.span()))
25         } else {
26             IdentOrWild(input.parse()?)
27         })
28     }
29 }
30
31 /// A modifier for a query
32 enum QueryModifier {
33     /// The description of the query.
34     Desc(Option<Ident>, Punctuated<Expr, Token![,]>),
35
36     /// Use this type for the in-memory cache.
37     Storage(Type),
38
39     /// Cache the query to disk if the `Expr` returns true.
40     Cache(Option<(IdentOrWild, IdentOrWild)>, Block),
41
42     /// Custom code to load the query from disk.
43     LoadCached(Ident, Ident, Block),
44
45     /// A cycle error for this query aborting the compilation with a fatal error.
46     FatalCycle,
47
48     /// A cycle error results in a delay_bug call
49     CycleDelayBug,
50
51     /// Don't hash the result, instead just mark a query red if it runs
52     NoHash,
53
54     /// Generate a dep node based on the dependencies of the query
55     Anon,
56
57     /// Always evaluate the query, ignoring its dependencies
58     EvalAlways,
59 }
60
61 impl Parse for QueryModifier {
62     fn parse(input: ParseStream<'_>) -> Result<Self> {
63         let modifier: Ident = input.parse()?;
64         if modifier == "desc" {
65             // Parse a description modifier like:
66             // `desc { |tcx| "foo {}", tcx.item_path(key) }`
67             let attr_content;
68             braced!(attr_content in input);
69             let tcx = if attr_content.peek(Token![|]) {
70                 attr_content.parse::<Token![|]>()?;
71                 let tcx = attr_content.parse()?;
72                 attr_content.parse::<Token![|]>()?;
73                 Some(tcx)
74             } else {
75                 None
76             };
77             let desc = attr_content.parse_terminated(Expr::parse)?;
78             Ok(QueryModifier::Desc(tcx, desc))
79         } else if modifier == "cache_on_disk_if" {
80             // Parse a cache modifier like:
81             // `cache(tcx, value) { |tcx| key.is_local() }`
82             let has_args = if let TokenTree::Group(group) = input.fork().parse()? {
83                 group.delimiter() == Delimiter::Parenthesis
84             } else {
85                 false
86             };
87             let args = if has_args {
88                 let args;
89                 parenthesized!(args in input);
90                 let tcx = args.parse()?;
91                 args.parse::<Token![,]>()?;
92                 let value = args.parse()?;
93                 Some((tcx, value))
94             } else {
95                 None
96             };
97             let block = input.parse()?;
98             Ok(QueryModifier::Cache(args, block))
99         } else if modifier == "load_cached" {
100             // Parse a load_cached modifier like:
101             // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }`
102             let args;
103             parenthesized!(args in input);
104             let tcx = args.parse()?;
105             args.parse::<Token![,]>()?;
106             let id = args.parse()?;
107             let block = input.parse()?;
108             Ok(QueryModifier::LoadCached(tcx, id, block))
109         } else if modifier == "storage" {
110             let args;
111             parenthesized!(args in input);
112             let ty = args.parse()?;
113             Ok(QueryModifier::Storage(ty))
114         } else if modifier == "fatal_cycle" {
115             Ok(QueryModifier::FatalCycle)
116         } else if modifier == "cycle_delay_bug" {
117             Ok(QueryModifier::CycleDelayBug)
118         } else if modifier == "no_hash" {
119             Ok(QueryModifier::NoHash)
120         } else if modifier == "anon" {
121             Ok(QueryModifier::Anon)
122         } else if modifier == "eval_always" {
123             Ok(QueryModifier::EvalAlways)
124         } else {
125             Err(Error::new(modifier.span(), "unknown query modifier"))
126         }
127     }
128 }
129
130 /// Ensures only doc comment attributes are used
131 fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
132     let inner = |attr: Attribute| {
133         if !attr.path.is_ident("doc") {
134             Err(Error::new(attr.span(), "attributes not supported on queries"))
135         } else if attr.style != AttrStyle::Outer {
136             Err(Error::new(
137                 attr.span(),
138                 "attributes must be outer attributes (`///`), not inner attributes",
139             ))
140         } else {
141             Ok(attr)
142         }
143     };
144     attrs.into_iter().map(inner).collect()
145 }
146
147 /// A compiler query. `query ... { ... }`
148 struct Query {
149     doc_comments: Vec<Attribute>,
150     modifiers: List<QueryModifier>,
151     name: Ident,
152     key: IdentOrWild,
153     arg: Type,
154     result: ReturnType,
155 }
156
157 impl Parse for Query {
158     fn parse(input: ParseStream<'_>) -> Result<Self> {
159         let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
160
161         // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
162         input.parse::<kw::query>()?;
163         let name: Ident = input.parse()?;
164         let arg_content;
165         parenthesized!(arg_content in input);
166         let key = arg_content.parse()?;
167         arg_content.parse::<Token![:]>()?;
168         let arg = arg_content.parse()?;
169         let result = input.parse()?;
170
171         // Parse the query modifiers
172         let content;
173         braced!(content in input);
174         let modifiers = content.parse()?;
175
176         Ok(Query { doc_comments, modifiers, name, key, arg, result })
177     }
178 }
179
180 /// A type used to greedily parse another type until the input is empty.
181 struct List<T>(Vec<T>);
182
183 impl<T: Parse> Parse for List<T> {
184     fn parse(input: ParseStream<'_>) -> Result<Self> {
185         let mut list = Vec::new();
186         while !input.is_empty() {
187             list.push(input.parse()?);
188         }
189         Ok(List(list))
190     }
191 }
192
193 /// A named group containing queries.
194 struct Group {
195     name: Ident,
196     queries: List<Query>,
197 }
198
199 impl Parse for Group {
200     fn parse(input: ParseStream<'_>) -> Result<Self> {
201         let name: Ident = input.parse()?;
202         let content;
203         braced!(content in input);
204         Ok(Group { name, queries: content.parse()? })
205     }
206 }
207
208 struct QueryModifiers {
209     /// The description of the query.
210     desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
211
212     /// Use this type for the in-memory cache.
213     storage: Option<Type>,
214
215     /// Cache the query to disk if the `Block` returns true.
216     cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>,
217
218     /// Custom code to load the query from disk.
219     load_cached: Option<(Ident, Ident, Block)>,
220
221     /// A cycle error for this query aborting the compilation with a fatal error.
222     fatal_cycle: bool,
223
224     /// A cycle error results in a delay_bug call
225     cycle_delay_bug: bool,
226
227     /// Don't hash the result, instead just mark a query red if it runs
228     no_hash: bool,
229
230     /// Generate a dep node based on the dependencies of the query
231     anon: bool,
232
233     // Always evaluate the query, ignoring its dependencies
234     eval_always: bool,
235 }
236
237 /// Process query modifiers into a struct, erroring on duplicates
238 fn process_modifiers(query: &mut Query) -> QueryModifiers {
239     let mut load_cached = None;
240     let mut storage = None;
241     let mut cache = None;
242     let mut desc = None;
243     let mut fatal_cycle = false;
244     let mut cycle_delay_bug = false;
245     let mut no_hash = false;
246     let mut anon = false;
247     let mut eval_always = false;
248     for modifier in query.modifiers.0.drain(..) {
249         match modifier {
250             QueryModifier::LoadCached(tcx, id, block) => {
251                 if load_cached.is_some() {
252                     panic!("duplicate modifier `load_cached` for query `{}`", query.name);
253                 }
254                 load_cached = Some((tcx, id, block));
255             }
256             QueryModifier::Storage(ty) => {
257                 if storage.is_some() {
258                     panic!("duplicate modifier `storage` for query `{}`", query.name);
259                 }
260                 storage = Some(ty);
261             }
262             QueryModifier::Cache(args, expr) => {
263                 if cache.is_some() {
264                     panic!("duplicate modifier `cache` for query `{}`", query.name);
265                 }
266                 cache = Some((args, expr));
267             }
268             QueryModifier::Desc(tcx, list) => {
269                 if desc.is_some() {
270                     panic!("duplicate modifier `desc` for query `{}`", query.name);
271                 }
272                 desc = Some((tcx, list));
273             }
274             QueryModifier::FatalCycle => {
275                 if fatal_cycle {
276                     panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name);
277                 }
278                 fatal_cycle = true;
279             }
280             QueryModifier::CycleDelayBug => {
281                 if cycle_delay_bug {
282                     panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name);
283                 }
284                 cycle_delay_bug = true;
285             }
286             QueryModifier::NoHash => {
287                 if no_hash {
288                     panic!("duplicate modifier `no_hash` for query `{}`", query.name);
289                 }
290                 no_hash = true;
291             }
292             QueryModifier::Anon => {
293                 if anon {
294                     panic!("duplicate modifier `anon` for query `{}`", query.name);
295                 }
296                 anon = true;
297             }
298             QueryModifier::EvalAlways => {
299                 if eval_always {
300                     panic!("duplicate modifier `eval_always` for query `{}`", query.name);
301                 }
302                 eval_always = true;
303             }
304         }
305     }
306     let desc = desc.unwrap_or_else(|| {
307         panic!("no description provided for query `{}`", query.name);
308     });
309     QueryModifiers {
310         load_cached,
311         storage,
312         cache,
313         desc,
314         fatal_cycle,
315         cycle_delay_bug,
316         no_hash,
317         anon,
318         eval_always,
319     }
320 }
321
322 /// Add the impl of QueryDescription for the query to `impls` if one is requested
323 fn add_query_description_impl(
324     query: &Query,
325     modifiers: QueryModifiers,
326     impls: &mut proc_macro2::TokenStream,
327 ) {
328     let name = &query.name;
329     let arg = &query.arg;
330     let key = &query.key.0;
331
332     // Find out if we should cache the query on disk
333     let cache = if let Some((args, expr)) = modifiers.cache.as_ref() {
334         let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() {
335             // Use custom code to load the query from disk
336             quote! {
337                 #[inline]
338                 fn try_load_from_disk(
339                     #tcx: TyCtxt<'tcx>,
340                     #id: SerializedDepNodeIndex
341                 ) -> Option<Self::Value> {
342                     #block
343                 }
344             }
345         } else {
346             // Use the default code to load the query from disk
347             quote! {
348                 #[inline]
349                 fn try_load_from_disk(
350                     tcx: TyCtxt<'tcx>,
351                     id: SerializedDepNodeIndex
352                 ) -> Option<Self::Value> {
353                     tcx.queries.on_disk_cache.try_load_query_result(tcx, id)
354                 }
355             }
356         };
357
358         let tcx = args
359             .as_ref()
360             .map(|t| {
361                 let t = &(t.0).0;
362                 quote! { #t }
363             })
364             .unwrap_or(quote! { _ });
365         let value = args
366             .as_ref()
367             .map(|t| {
368                 let t = &(t.1).0;
369                 quote! { #t }
370             })
371             .unwrap_or(quote! { _ });
372         // expr is a `Block`, meaning that `{ #expr }` gets expanded
373         // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
374         quote! {
375             #[inline]
376             #[allow(unused_variables, unused_braces)]
377             fn cache_on_disk(
378                 #tcx: TyCtxt<'tcx>,
379                 #key: &Self::Key,
380                 #value: Option<&Self::Value>
381             ) -> bool {
382                 #expr
383             }
384
385             #try_load_from_disk
386         }
387     } else {
388         if modifiers.load_cached.is_some() {
389             panic!("load_cached modifier on query `{}` without a cache modifier", name);
390         }
391         quote! {}
392     };
393
394     let (tcx, desc) = modifiers.desc;
395     let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ });
396
397     let desc = quote! {
398         #[allow(unused_variables)]
399         fn describe(
400             #tcx: TyCtxt<'tcx>,
401             #key: #arg,
402         ) -> Cow<'static, str> {
403             ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into())
404         }
405     };
406
407     impls.extend(quote! {
408         impl<'tcx> QueryDescription<TyCtxt<'tcx>> for queries::#name<'tcx> {
409             #desc
410             #cache
411         }
412     });
413 }
414
415 pub fn rustc_queries(input: TokenStream) -> TokenStream {
416     let groups = parse_macro_input!(input as List<Group>);
417
418     let mut query_stream = quote! {};
419     let mut query_description_stream = quote! {};
420     let mut dep_node_def_stream = quote! {};
421     let mut dep_node_force_stream = quote! {};
422     let mut try_load_from_on_disk_cache_stream = quote! {};
423     let mut cached_queries = quote! {};
424
425     for group in groups.0 {
426         let mut group_stream = quote! {};
427         for mut query in group.queries.0 {
428             let modifiers = process_modifiers(&mut query);
429             let name = &query.name;
430             let arg = &query.arg;
431             let result_full = &query.result;
432             let result = match query.result {
433                 ReturnType::Default => quote! { -> () },
434                 _ => quote! { #result_full },
435             };
436
437             if modifiers.cache.is_some() {
438                 cached_queries.extend(quote! {
439                     #name,
440                 });
441
442                 try_load_from_on_disk_cache_stream.extend(quote! {
443                     ::rustc_middle::dep_graph::DepKind::#name => {
444                         if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
445                             debug_assert!($tcx.dep_graph
446                                             .node_color($dep_node)
447                                             .map(|c| c.is_green())
448                                             .unwrap_or(false));
449
450                             let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
451                             if queries::#name::cache_on_disk($tcx, &key, None) {
452                                 let _ = $tcx.#name(key);
453                             }
454                         }
455                     }
456                 });
457             }
458
459             let mut attributes = Vec::new();
460
461             // Pass on the fatal_cycle modifier
462             if modifiers.fatal_cycle {
463                 attributes.push(quote! { fatal_cycle });
464             };
465             // Pass on the storage modifier
466             if let Some(ref ty) = modifiers.storage {
467                 attributes.push(quote! { storage(#ty) });
468             };
469             // Pass on the cycle_delay_bug modifier
470             if modifiers.cycle_delay_bug {
471                 attributes.push(quote! { cycle_delay_bug });
472             };
473             // Pass on the no_hash modifier
474             if modifiers.no_hash {
475                 attributes.push(quote! { no_hash });
476             };
477             // Pass on the anon modifier
478             if modifiers.anon {
479                 attributes.push(quote! { anon });
480             };
481             // Pass on the eval_always modifier
482             if modifiers.eval_always {
483                 attributes.push(quote! { eval_always });
484             };
485
486             let attribute_stream = quote! {#(#attributes),*};
487             let doc_comments = query.doc_comments.iter();
488             // Add the query to the group
489             group_stream.extend(quote! {
490                 #(#doc_comments)*
491                 [#attribute_stream] fn #name: #name(#arg) #result,
492             });
493
494             // Create a dep node for the query
495             dep_node_def_stream.extend(quote! {
496                 [#attribute_stream] #name(#arg),
497             });
498
499             // Add a match arm to force the query given the dep node
500             dep_node_force_stream.extend(quote! {
501                 ::rustc_middle::dep_graph::DepKind::#name => {
502                     if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
503                         if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
504                             force_query::<crate::ty::query::queries::#name<'_>, _>(
505                                 $tcx,
506                                 key,
507                                 DUMMY_SP,
508                                 *$dep_node
509                             );
510                             return true;
511                         }
512                     }
513                 }
514             });
515
516             add_query_description_impl(&query, modifiers, &mut query_description_stream);
517         }
518         let name = &group.name;
519         query_stream.extend(quote! {
520             #name { #group_stream },
521         });
522     }
523
524     dep_node_force_stream.extend(quote! {
525         ::rustc_middle::dep_graph::DepKind::Null => {
526             bug!("Cannot force dep node: {:?}", $dep_node)
527         }
528     });
529
530     TokenStream::from(quote! {
531         macro_rules! rustc_query_append {
532             ([$($macro:tt)*][$($other:tt)*]) => {
533                 $($macro)* {
534                     $($other)*
535
536                     #query_stream
537
538                 }
539             }
540         }
541         macro_rules! rustc_dep_node_append {
542             ([$($macro:tt)*][$($other:tt)*]) => {
543                 $($macro)*(
544                     $($other)*
545
546                     #dep_node_def_stream
547                 );
548             }
549         }
550         macro_rules! rustc_dep_node_force {
551             ([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
552                 match $dep_node.kind {
553                     $($other)*
554
555                     #dep_node_force_stream
556                 }
557             }
558         }
559         macro_rules! rustc_cached_queries {
560             ($($macro:tt)*) => {
561                 $($macro)*(#cached_queries);
562             }
563         }
564
565         #query_description_stream
566
567         macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
568             ($dep_node:expr, $tcx:expr) => {
569                 match $dep_node.kind {
570                     #try_load_from_on_disk_cache_stream
571                     _ => (),
572                 }
573             }
574         }
575     })
576 }