]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_macros/src/query.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / compiler / rustc_macros / src / query.rs
CommitLineData
532ac7d7 1use proc_macro::TokenStream;
dfeec247 2use proc_macro2::{Delimiter, TokenTree};
94222f64 3use quote::{quote, quote_spanned};
dfeec247
XL
4use syn::parse::{Parse, ParseStream, Result};
5use syn::punctuated::Punctuated;
6use syn::spanned::Spanned;
532ac7d7 7use syn::{
5869c6ff
XL
8 braced, parenthesized, parse_macro_input, parse_quote, AttrStyle, Attribute, Block, Error,
9 Expr, Ident, ReturnType, Token, Type,
532ac7d7 10};
532ac7d7 11
532ac7d7
XL
12mod kw {
13 syn::custom_keyword!(query);
14}
15
16/// Ident or a wildcard `_`.
17struct IdentOrWild(Ident);
18
19impl 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
31enum QueryModifier {
32 /// The description of the query.
33 Desc(Option<Ident>, Punctuated<Expr, Token![,]>),
34
74b04a01
XL
35 /// Use this type for the in-memory cache.
36 Storage(Type),
37
532ac7d7 38 /// Cache the query to disk if the `Expr` returns true.
3c0e092e 39 Cache(Option<IdentOrWild>, Block),
532ac7d7
XL
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.
94222f64 45 FatalCycle(Ident),
532ac7d7
XL
46
47 /// A cycle error results in a delay_bug call
94222f64 48 CycleDelayBug(Ident),
532ac7d7
XL
49
50 /// Don't hash the result, instead just mark a query red if it runs
94222f64 51 NoHash(Ident),
532ac7d7 52
532ac7d7 53 /// Generate a dep node based on the dependencies of the query
94222f64 54 Anon(Ident),
532ac7d7 55
74b04a01 56 /// Always evaluate the query, ignoring its dependencies
94222f64 57 EvalAlways(Ident),
3c0e092e
XL
58
59 /// Use a separate query provider for local and extern crates
60 SeparateProvideExtern(Ident),
a2a8927a
XL
61
62 /// Always remap the ParamEnv's constness before hashing and passing to the query provider
63 RemapEnvConstness(Ident),
532ac7d7
XL
64}
65
66impl Parse for QueryModifier {
67 fn parse(input: ParseStream<'_>) -> Result<Self> {
68 let modifier: Ident = input.parse()?;
69 if modifier == "desc" {
70 // Parse a description modifier like:
71 // `desc { |tcx| "foo {}", tcx.item_path(key) }`
72 let attr_content;
73 braced!(attr_content in input);
74 let tcx = if attr_content.peek(Token![|]) {
75 attr_content.parse::<Token![|]>()?;
76 let tcx = attr_content.parse()?;
77 attr_content.parse::<Token![|]>()?;
78 Some(tcx)
79 } else {
80 None
81 };
82 let desc = attr_content.parse_terminated(Expr::parse)?;
83 Ok(QueryModifier::Desc(tcx, desc))
dc9dc135 84 } else if modifier == "cache_on_disk_if" {
532ac7d7 85 // Parse a cache modifier like:
dc9dc135
XL
86 // `cache(tcx, value) { |tcx| key.is_local() }`
87 let has_args = if let TokenTree::Group(group) = input.fork().parse()? {
88 group.delimiter() == Delimiter::Parenthesis
89 } else {
90 false
91 };
92 let args = if has_args {
93 let args;
94 parenthesized!(args in input);
95 let tcx = args.parse()?;
3c0e092e 96 Some(tcx)
532ac7d7
XL
97 } else {
98 None
99 };
dc9dc135
XL
100 let block = input.parse()?;
101 Ok(QueryModifier::Cache(args, block))
532ac7d7
XL
102 } else if modifier == "load_cached" {
103 // Parse a load_cached modifier like:
6a06907d 104 // `load_cached(tcx, id) { tcx.on_disk_cache.try_load_query_result(tcx, id) }`
532ac7d7
XL
105 let args;
106 parenthesized!(args in input);
107 let tcx = args.parse()?;
108 args.parse::<Token![,]>()?;
109 let id = args.parse()?;
110 let block = input.parse()?;
111 Ok(QueryModifier::LoadCached(tcx, id, block))
74b04a01 112 } else if modifier == "storage" {
f9f354fc
XL
113 let args;
114 parenthesized!(args in input);
115 let ty = args.parse()?;
74b04a01 116 Ok(QueryModifier::Storage(ty))
532ac7d7 117 } else if modifier == "fatal_cycle" {
94222f64 118 Ok(QueryModifier::FatalCycle(modifier))
532ac7d7 119 } else if modifier == "cycle_delay_bug" {
94222f64 120 Ok(QueryModifier::CycleDelayBug(modifier))
532ac7d7 121 } else if modifier == "no_hash" {
94222f64 122 Ok(QueryModifier::NoHash(modifier))
532ac7d7 123 } else if modifier == "anon" {
94222f64 124 Ok(QueryModifier::Anon(modifier))
532ac7d7 125 } else if modifier == "eval_always" {
94222f64 126 Ok(QueryModifier::EvalAlways(modifier))
3c0e092e
XL
127 } else if modifier == "separate_provide_extern" {
128 Ok(QueryModifier::SeparateProvideExtern(modifier))
a2a8927a
XL
129 } else if modifier == "remap_env_constness" {
130 Ok(QueryModifier::RemapEnvConstness(modifier))
532ac7d7
XL
131 } else {
132 Err(Error::new(modifier.span(), "unknown query modifier"))
133 }
134 }
135}
136
137/// Ensures only doc comment attributes are used
1b1a35ee
XL
138fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
139 let inner = |attr: Attribute| {
532ac7d7 140 if !attr.path.is_ident("doc") {
1b1a35ee
XL
141 Err(Error::new(attr.span(), "attributes not supported on queries"))
142 } else if attr.style != AttrStyle::Outer {
143 Err(Error::new(
144 attr.span(),
145 "attributes must be outer attributes (`///`), not inner attributes",
146 ))
147 } else {
148 Ok(attr)
532ac7d7 149 }
1b1a35ee
XL
150 };
151 attrs.into_iter().map(inner).collect()
532ac7d7
XL
152}
153
154/// A compiler query. `query ... { ... }`
155struct Query {
1b1a35ee 156 doc_comments: Vec<Attribute>,
532ac7d7
XL
157 modifiers: List<QueryModifier>,
158 name: Ident,
159 key: IdentOrWild,
160 arg: Type,
161 result: ReturnType,
162}
163
164impl Parse for Query {
165 fn parse(input: ParseStream<'_>) -> Result<Self> {
1b1a35ee 166 let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
532ac7d7
XL
167
168 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
169 input.parse::<kw::query>()?;
170 let name: Ident = input.parse()?;
171 let arg_content;
172 parenthesized!(arg_content in input);
173 let key = arg_content.parse()?;
174 arg_content.parse::<Token![:]>()?;
175 let arg = arg_content.parse()?;
176 let result = input.parse()?;
177
178 // Parse the query modifiers
179 let content;
180 braced!(content in input);
181 let modifiers = content.parse()?;
182
1b1a35ee 183 Ok(Query { doc_comments, modifiers, name, key, arg, result })
532ac7d7
XL
184 }
185}
186
187/// A type used to greedily parse another type until the input is empty.
188struct List<T>(Vec<T>);
189
190impl<T: Parse> Parse for List<T> {
191 fn parse(input: ParseStream<'_>) -> Result<Self> {
192 let mut list = Vec::new();
193 while !input.is_empty() {
194 list.push(input.parse()?);
195 }
196 Ok(List(list))
197 }
198}
199
532ac7d7
XL
200struct QueryModifiers {
201 /// The description of the query.
f9f354fc 202 desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
532ac7d7 203
74b04a01
XL
204 /// Use this type for the in-memory cache.
205 storage: Option<Type>,
206
dc9dc135 207 /// Cache the query to disk if the `Block` returns true.
3c0e092e 208 cache: Option<(Option<IdentOrWild>, Block)>,
532ac7d7
XL
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.
94222f64 214 fatal_cycle: Option<Ident>,
532ac7d7
XL
215
216 /// A cycle error results in a delay_bug call
94222f64 217 cycle_delay_bug: Option<Ident>,
532ac7d7
XL
218
219 /// Don't hash the result, instead just mark a query red if it runs
94222f64 220 no_hash: Option<Ident>,
532ac7d7 221
532ac7d7 222 /// Generate a dep node based on the dependencies of the query
94222f64 223 anon: Option<Ident>,
532ac7d7 224
74b04a01 225 // Always evaluate the query, ignoring its dependencies
94222f64 226 eval_always: Option<Ident>,
3c0e092e
XL
227
228 /// Use a separate query provider for local and extern crates
229 separate_provide_extern: Option<Ident>,
a2a8927a
XL
230
231 /// Always remap the ParamEnv's constness before hashing.
232 remap_env_constness: Option<Ident>,
532ac7d7
XL
233}
234
235/// Process query modifiers into a struct, erroring on duplicates
236fn process_modifiers(query: &mut Query) -> QueryModifiers {
237 let mut load_cached = None;
74b04a01 238 let mut storage = None;
532ac7d7
XL
239 let mut cache = None;
240 let mut desc = None;
94222f64
XL
241 let mut fatal_cycle = None;
242 let mut cycle_delay_bug = None;
243 let mut no_hash = None;
244 let mut anon = None;
245 let mut eval_always = None;
3c0e092e 246 let mut separate_provide_extern = None;
a2a8927a 247 let mut remap_env_constness = None;
532ac7d7
XL
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 }
74b04a01
XL
256 QueryModifier::Storage(ty) => {
257 if storage.is_some() {
258 panic!("duplicate modifier `storage` for query `{}`", query.name);
259 }
260 storage = Some(ty);
261 }
dc9dc135 262 QueryModifier::Cache(args, expr) => {
532ac7d7
XL
263 if cache.is_some() {
264 panic!("duplicate modifier `cache` for query `{}`", query.name);
265 }
dc9dc135 266 cache = Some((args, expr));
532ac7d7
XL
267 }
268 QueryModifier::Desc(tcx, list) => {
269 if desc.is_some() {
270 panic!("duplicate modifier `desc` for query `{}`", query.name);
271 }
5869c6ff
XL
272 // If there are no doc-comments, give at least some idea of what
273 // it does by showing the query description.
274 if query.doc_comments.is_empty() {
275 use ::syn::*;
276 let mut list = list.iter();
277 let format_str: String = match list.next() {
278 Some(&Expr::Lit(ExprLit { lit: Lit::Str(ref lit_str), .. })) => {
279 lit_str.value().replace("`{}`", "{}") // We add them later anyways for consistency
280 }
281 _ => panic!("Expected a string literal"),
282 };
283 let mut fmt_fragments = format_str.split("{}");
284 let mut doc_string = fmt_fragments.next().unwrap().to_string();
285 list.map(::quote::ToTokens::to_token_stream).zip(fmt_fragments).for_each(
286 |(tts, next_fmt_fragment)| {
287 use ::core::fmt::Write;
288 write!(
289 &mut doc_string,
290 " `{}` {}",
291 tts.to_string().replace(" . ", "."),
292 next_fmt_fragment,
293 )
294 .unwrap();
295 },
296 );
297 let doc_string = format!(
298 "[query description - consider adding a doc-comment!] {}",
299 doc_string
300 );
301 let comment = parse_quote! {
302 #[doc = #doc_string]
303 };
304 query.doc_comments.push(comment);
305 }
532ac7d7
XL
306 desc = Some((tcx, list));
307 }
94222f64
XL
308 QueryModifier::FatalCycle(ident) => {
309 if fatal_cycle.is_some() {
532ac7d7
XL
310 panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name);
311 }
94222f64 312 fatal_cycle = Some(ident);
532ac7d7 313 }
94222f64
XL
314 QueryModifier::CycleDelayBug(ident) => {
315 if cycle_delay_bug.is_some() {
532ac7d7
XL
316 panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name);
317 }
94222f64 318 cycle_delay_bug = Some(ident);
532ac7d7 319 }
94222f64
XL
320 QueryModifier::NoHash(ident) => {
321 if no_hash.is_some() {
532ac7d7
XL
322 panic!("duplicate modifier `no_hash` for query `{}`", query.name);
323 }
94222f64 324 no_hash = Some(ident);
532ac7d7 325 }
94222f64
XL
326 QueryModifier::Anon(ident) => {
327 if anon.is_some() {
532ac7d7
XL
328 panic!("duplicate modifier `anon` for query `{}`", query.name);
329 }
94222f64 330 anon = Some(ident);
532ac7d7 331 }
94222f64
XL
332 QueryModifier::EvalAlways(ident) => {
333 if eval_always.is_some() {
532ac7d7
XL
334 panic!("duplicate modifier `eval_always` for query `{}`", query.name);
335 }
94222f64 336 eval_always = Some(ident);
532ac7d7 337 }
3c0e092e
XL
338 QueryModifier::SeparateProvideExtern(ident) => {
339 if separate_provide_extern.is_some() {
340 panic!(
341 "duplicate modifier `separate_provide_extern` for query `{}`",
342 query.name
343 );
344 }
345 separate_provide_extern = Some(ident);
346 }
a2a8927a
XL
347 QueryModifier::RemapEnvConstness(ident) => {
348 if remap_env_constness.is_some() {
349 panic!("duplicate modifier `remap_env_constness` for query `{}`", query.name);
350 }
351 remap_env_constness = Some(ident)
352 }
532ac7d7
XL
353 }
354 }
f9f354fc
XL
355 let desc = desc.unwrap_or_else(|| {
356 panic!("no description provided for query `{}`", query.name);
357 });
532ac7d7
XL
358 QueryModifiers {
359 load_cached,
74b04a01 360 storage,
532ac7d7
XL
361 cache,
362 desc,
363 fatal_cycle,
364 cycle_delay_bug,
365 no_hash,
532ac7d7
XL
366 anon,
367 eval_always,
3c0e092e 368 separate_provide_extern,
a2a8927a 369 remap_env_constness,
532ac7d7
XL
370 }
371}
372
373/// Add the impl of QueryDescription for the query to `impls` if one is requested
374fn add_query_description_impl(
375 query: &Query,
376 modifiers: QueryModifiers,
dc9dc135 377 impls: &mut proc_macro2::TokenStream,
532ac7d7
XL
378) {
379 let name = &query.name;
532ac7d7
XL
380 let key = &query.key.0;
381
382 // Find out if we should cache the query on disk
f9f354fc 383 let cache = if let Some((args, expr)) = modifiers.cache.as_ref() {
532ac7d7
XL
384 let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() {
385 // Use custom code to load the query from disk
386 quote! {
3c0e092e
XL
387 const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>>
388 = Some(|#tcx, #id| { #block });
532ac7d7
XL
389 }
390 } else {
391 // Use the default code to load the query from disk
392 quote! {
3c0e092e
XL
393 const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>>
394 = Some(|tcx, id| tcx.on_disk_cache().as_ref()?.try_load_query_result(*tcx, id));
532ac7d7
XL
395 }
396 };
397
dfeec247
XL
398 let tcx = args
399 .as_ref()
400 .map(|t| {
3c0e092e 401 let t = &t.0;
dfeec247
XL
402 quote! { #t }
403 })
6a06907d 404 .unwrap_or_else(|| quote! { _ });
ba9703b0
XL
405 // expr is a `Block`, meaning that `{ #expr }` gets expanded
406 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
532ac7d7 407 quote! {
ba9703b0 408 #[allow(unused_variables, unused_braces)]
3c0e092e
XL
409 #[inline]
410 fn cache_on_disk(#tcx: TyCtxt<'tcx>, #key: &Self::Key) -> bool {
532ac7d7
XL
411 #expr
412 }
413
414 #try_load_from_disk
415 }
f9f354fc
XL
416 } else {
417 if modifiers.load_cached.is_some() {
418 panic!("load_cached modifier on query `{}` without a cache modifier", name);
419 }
3c0e092e
XL
420 quote! {
421 #[inline]
422 fn cache_on_disk(_: TyCtxt<'tcx>, _: &Self::Key) -> bool {
423 false
424 }
425
426 const TRY_LOAD_FROM_DISK: Option<fn(QueryCtxt<$tcx>, SerializedDepNodeIndex) -> Option<Self::Value>> = None;
427 }
f9f354fc
XL
428 };
429
430 let (tcx, desc) = modifiers.desc;
6a06907d 431 let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
f9f354fc
XL
432
433 let desc = quote! {
434 #[allow(unused_variables)]
3c0e092e 435 fn describe(tcx: QueryCtxt<$tcx>, key: Self::Key) -> String {
6a06907d 436 let (#tcx, #key) = (*tcx, key);
1b1a35ee 437 ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into())
f9f354fc
XL
438 }
439 };
532ac7d7 440
f9f354fc 441 impls.extend(quote! {
3c0e092e 442 (#name<$tcx:tt>) => {
f9f354fc
XL
443 #desc
444 #cache
3c0e092e 445 };
532ac7d7 446 });
532ac7d7
XL
447}
448
449pub fn rustc_queries(input: TokenStream) -> TokenStream {
5869c6ff 450 let queries = parse_macro_input!(input as List<Query>);
532ac7d7
XL
451
452 let mut query_stream = quote! {};
453 let mut query_description_stream = quote! {};
454 let mut dep_node_def_stream = quote! {};
416331ca 455 let mut cached_queries = quote! {};
532ac7d7 456
5869c6ff
XL
457 for mut query in queries.0 {
458 let modifiers = process_modifiers(&mut query);
459 let name = &query.name;
460 let arg = &query.arg;
461 let result_full = &query.result;
462 let result = match query.result {
463 ReturnType::Default => quote! { -> () },
464 _ => quote! { #result_full },
465 };
532ac7d7 466
5869c6ff
XL
467 if modifiers.cache.is_some() {
468 cached_queries.extend(quote! {
469 #name,
470 });
471 }
dc9dc135 472
5869c6ff 473 let mut attributes = Vec::new();
532ac7d7 474
5869c6ff 475 // Pass on the fatal_cycle modifier
94222f64 476 if let Some(fatal_cycle) = &modifiers.fatal_cycle {
c295e0f8 477 attributes.push(quote! { (#fatal_cycle) });
5869c6ff
XL
478 };
479 // Pass on the storage modifier
480 if let Some(ref ty) = modifiers.storage {
94222f64 481 let span = ty.span();
c295e0f8 482 attributes.push(quote_spanned! {span=> (storage #ty) });
5869c6ff
XL
483 };
484 // Pass on the cycle_delay_bug modifier
94222f64 485 if let Some(cycle_delay_bug) = &modifiers.cycle_delay_bug {
c295e0f8 486 attributes.push(quote! { (#cycle_delay_bug) });
5869c6ff
XL
487 };
488 // Pass on the no_hash modifier
94222f64 489 if let Some(no_hash) = &modifiers.no_hash {
c295e0f8 490 attributes.push(quote! { (#no_hash) });
5869c6ff
XL
491 };
492 // Pass on the anon modifier
94222f64 493 if let Some(anon) = &modifiers.anon {
c295e0f8 494 attributes.push(quote! { (#anon) });
5869c6ff
XL
495 };
496 // Pass on the eval_always modifier
94222f64 497 if let Some(eval_always) = &modifiers.eval_always {
c295e0f8 498 attributes.push(quote! { (#eval_always) });
5869c6ff 499 };
3c0e092e
XL
500 // Pass on the separate_provide_extern modifier
501 if let Some(separate_provide_extern) = &modifiers.separate_provide_extern {
502 attributes.push(quote! { (#separate_provide_extern) });
503 }
a2a8927a
XL
504 // Pass on the remap_env_constness modifier
505 if let Some(remap_env_constness) = &modifiers.remap_env_constness {
506 attributes.push(quote! { (#remap_env_constness) });
507 }
532ac7d7 508
94222f64
XL
509 // This uses the span of the query definition for the commas,
510 // which can be important if we later encounter any ambiguity
511 // errors with any of the numerous macro_rules! macros that
512 // we use. Using the call-site span would result in a span pointing
513 // at the entire `rustc_queries!` invocation, which wouldn't
514 // be very useful.
515 let span = name.span();
516 let attribute_stream = quote_spanned! {span=> #(#attributes),*};
5869c6ff
XL
517 let doc_comments = query.doc_comments.iter();
518 // Add the query to the group
519 query_stream.extend(quote! {
520 #(#doc_comments)*
521 [#attribute_stream] fn #name(#arg) #result,
522 });
dc9dc135 523
5869c6ff
XL
524 // Create a dep node for the query
525 dep_node_def_stream.extend(quote! {
526 [#attribute_stream] #name(#arg),
527 });
532ac7d7 528
5869c6ff 529 add_query_description_impl(&query, modifiers, &mut query_description_stream);
532ac7d7
XL
530 }
531
532ac7d7 532 TokenStream::from(quote! {
6a06907d 533 #[macro_export]
532ac7d7
XL
534 macro_rules! rustc_query_append {
535 ([$($macro:tt)*][$($other:tt)*]) => {
536 $($macro)* {
537 $($other)*
538
539 #query_stream
540
541 }
542 }
543 }
544 macro_rules! rustc_dep_node_append {
545 ([$($macro:tt)*][$($other:tt)*]) => {
546 $($macro)*(
547 $($other)*
548
549 #dep_node_def_stream
550 );
551 }
552 }
6a06907d 553 #[macro_export]
416331ca
XL
554 macro_rules! rustc_cached_queries {
555 ($($macro:tt)*) => {
556 $($macro)*(#cached_queries);
557 }
558 }
6a06907d
XL
559 #[macro_export]
560 macro_rules! rustc_query_description {
3c0e092e 561 #query_description_stream
6a06907d 562 }
532ac7d7
XL
563 })
564}