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