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