]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_macros/src/query.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.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 }