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