1 use proc_macro::TokenStream;
2 use proc_macro2::{Delimiter, TokenTree};
4 use syn::parse::{Parse, ParseStream, Result};
5 use syn::punctuated::Punctuated;
6 use syn::spanned::Spanned;
8 braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident,
9 ReturnType, Token, Type,
12 #[allow(non_camel_case_types)]
14 syn::custom_keyword!(query);
17 /// Ident or a wildcard `_`.
18 struct IdentOrWild(Ident);
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()))
26 IdentOrWild(input.parse()?)
31 /// A modifier for a query
33 /// The description of the query.
34 Desc(Option<Ident>, Punctuated<Expr, Token![,]>),
36 /// Use this type for the in-memory cache.
39 /// Cache the query to disk if the `Expr` returns true.
40 Cache(Option<(IdentOrWild, IdentOrWild)>, Block),
42 /// Custom code to load the query from disk.
43 LoadCached(Ident, Ident, Block),
45 /// A cycle error for this query aborting the compilation with a fatal error.
48 /// A cycle error results in a delay_bug call
51 /// Don't hash the result, instead just mark a query red if it runs
54 /// Generate a dep node based on the dependencies of the query
57 /// Always evaluate the query, ignoring its dependencies
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) }`
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![|]>()?;
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
87 let args = if has_args {
89 parenthesized!(args in input);
90 let tcx = args.parse()?;
91 args.parse::<Token![,]>()?;
92 let value = args.parse()?;
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) }`
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" {
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)
125 Err(Error::new(modifier.span(), "unknown query modifier"))
130 /// Ensures only doc comment attributes are used
131 fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
132 let inner = |attr: Attribute| {
133 if !attr.path.is_ident("doc") {
134 Err(Error::new(attr.span(), "attributes not supported on queries"))
135 } else if attr.style != AttrStyle::Outer {
138 "attributes must be outer attributes (`///`), not inner attributes",
144 attrs.into_iter().map(inner).collect()
147 /// A compiler query. `query ... { ... }`
149 doc_comments: Vec<Attribute>,
150 modifiers: List<QueryModifier>,
157 impl Parse for Query {
158 fn parse(input: ParseStream<'_>) -> Result<Self> {
159 let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
161 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
162 input.parse::<kw::query>()?;
163 let name: Ident = input.parse()?;
165 parenthesized!(arg_content in input);
166 let key = arg_content.parse()?;
167 arg_content.parse::<Token![:]>()?;
168 let arg = arg_content.parse()?;
169 let result = input.parse()?;
171 // Parse the query modifiers
173 braced!(content in input);
174 let modifiers = content.parse()?;
176 Ok(Query { doc_comments, modifiers, name, key, arg, result })
180 /// A type used to greedily parse another type until the input is empty.
181 struct List<T>(Vec<T>);
183 impl<T: Parse> Parse for List<T> {
184 fn parse(input: ParseStream<'_>) -> Result<Self> {
185 let mut list = Vec::new();
186 while !input.is_empty() {
187 list.push(input.parse()?);
193 /// A named group containing queries.
196 queries: List<Query>,
199 impl Parse for Group {
200 fn parse(input: ParseStream<'_>) -> Result<Self> {
201 let name: Ident = input.parse()?;
203 braced!(content in input);
204 Ok(Group { name, queries: content.parse()? })
208 struct QueryModifiers {
209 /// The description of the query.
210 desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
212 /// Use this type for the in-memory cache.
213 storage: Option<Type>,
215 /// Cache the query to disk if the `Block` returns true.
216 cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>,
218 /// Custom code to load the query from disk.
219 load_cached: Option<(Ident, Ident, Block)>,
221 /// A cycle error for this query aborting the compilation with a fatal error.
224 /// A cycle error results in a delay_bug call
225 cycle_delay_bug: bool,
227 /// Don't hash the result, instead just mark a query red if it runs
230 /// Generate a dep node based on the dependencies of the query
233 // Always evaluate the query, ignoring its dependencies
237 /// Process query modifiers into a struct, erroring on duplicates
238 fn process_modifiers(query: &mut Query) -> QueryModifiers {
239 let mut load_cached = None;
240 let mut storage = None;
241 let mut cache = None;
243 let mut fatal_cycle = false;
244 let mut cycle_delay_bug = false;
245 let mut no_hash = false;
246 let mut anon = false;
247 let mut eval_always = false;
248 for modifier in query.modifiers.0.drain(..) {
250 QueryModifier::LoadCached(tcx, id, block) => {
251 if load_cached.is_some() {
252 panic!("duplicate modifier `load_cached` for query `{}`", query.name);
254 load_cached = Some((tcx, id, block));
256 QueryModifier::Storage(ty) => {
257 if storage.is_some() {
258 panic!("duplicate modifier `storage` for query `{}`", query.name);
262 QueryModifier::Cache(args, expr) => {
264 panic!("duplicate modifier `cache` for query `{}`", query.name);
266 cache = Some((args, expr));
268 QueryModifier::Desc(tcx, list) => {
270 panic!("duplicate modifier `desc` for query `{}`", query.name);
272 desc = Some((tcx, list));
274 QueryModifier::FatalCycle => {
276 panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name);
280 QueryModifier::CycleDelayBug => {
282 panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name);
284 cycle_delay_bug = true;
286 QueryModifier::NoHash => {
288 panic!("duplicate modifier `no_hash` for query `{}`", query.name);
292 QueryModifier::Anon => {
294 panic!("duplicate modifier `anon` for query `{}`", query.name);
298 QueryModifier::EvalAlways => {
300 panic!("duplicate modifier `eval_always` for query `{}`", query.name);
306 let desc = desc.unwrap_or_else(|| {
307 panic!("no description provided for query `{}`", query.name);
322 /// Add the impl of QueryDescription for the query to `impls` if one is requested
323 fn add_query_description_impl(
325 modifiers: QueryModifiers,
326 impls: &mut proc_macro2::TokenStream,
328 let name = &query.name;
329 let arg = &query.arg;
330 let key = &query.key.0;
332 // Find out if we should cache the query on disk
333 let cache = if let Some((args, expr)) = modifiers.cache.as_ref() {
334 let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() {
335 // Use custom code to load the query from disk
338 fn try_load_from_disk(
340 #id: SerializedDepNodeIndex
341 ) -> Option<Self::Value> {
346 // Use the default code to load the query from disk
349 fn try_load_from_disk(
351 id: SerializedDepNodeIndex
352 ) -> Option<Self::Value> {
353 tcx.queries.on_disk_cache.try_load_query_result(tcx, id)
364 .unwrap_or(quote! { _ });
371 .unwrap_or(quote! { _ });
372 // expr is a `Block`, meaning that `{ #expr }` gets expanded
373 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
376 #[allow(unused_variables, unused_braces)]
380 #value: Option<&Self::Value>
388 if modifiers.load_cached.is_some() {
389 panic!("load_cached modifier on query `{}` without a cache modifier", name);
394 let (tcx, desc) = modifiers.desc;
395 let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ });
398 #[allow(unused_variables)]
402 ) -> Cow<'static, str> {
403 ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into())
407 impls.extend(quote! {
408 impl<'tcx> QueryDescription<TyCtxt<'tcx>> for queries::#name<'tcx> {
415 pub fn rustc_queries(input: TokenStream) -> TokenStream {
416 let groups = parse_macro_input!(input as List<Group>);
418 let mut query_stream = quote! {};
419 let mut query_description_stream = quote! {};
420 let mut dep_node_def_stream = quote! {};
421 let mut dep_node_force_stream = quote! {};
422 let mut try_load_from_on_disk_cache_stream = quote! {};
423 let mut cached_queries = quote! {};
425 for group in groups.0 {
426 let mut group_stream = quote! {};
427 for mut query in group.queries.0 {
428 let modifiers = process_modifiers(&mut query);
429 let name = &query.name;
430 let arg = &query.arg;
431 let result_full = &query.result;
432 let result = match query.result {
433 ReturnType::Default => quote! { -> () },
434 _ => quote! { #result_full },
437 if modifiers.cache.is_some() {
438 cached_queries.extend(quote! {
442 try_load_from_on_disk_cache_stream.extend(quote! {
443 ::rustc_middle::dep_graph::DepKind::#name => {
444 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
445 debug_assert!($tcx.dep_graph
446 .node_color($dep_node)
447 .map(|c| c.is_green())
450 let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
451 if queries::#name::cache_on_disk($tcx, &key, None) {
452 let _ = $tcx.#name(key);
459 let mut attributes = Vec::new();
461 // Pass on the fatal_cycle modifier
462 if modifiers.fatal_cycle {
463 attributes.push(quote! { fatal_cycle });
465 // Pass on the storage modifier
466 if let Some(ref ty) = modifiers.storage {
467 attributes.push(quote! { storage(#ty) });
469 // Pass on the cycle_delay_bug modifier
470 if modifiers.cycle_delay_bug {
471 attributes.push(quote! { cycle_delay_bug });
473 // Pass on the no_hash modifier
474 if modifiers.no_hash {
475 attributes.push(quote! { no_hash });
477 // Pass on the anon modifier
479 attributes.push(quote! { anon });
481 // Pass on the eval_always modifier
482 if modifiers.eval_always {
483 attributes.push(quote! { eval_always });
486 let attribute_stream = quote! {#(#attributes),*};
487 let doc_comments = query.doc_comments.iter();
488 // Add the query to the group
489 group_stream.extend(quote! {
491 [#attribute_stream] fn #name: #name(#arg) #result,
494 // Create a dep node for the query
495 dep_node_def_stream.extend(quote! {
496 [#attribute_stream] #name(#arg),
499 // Add a match arm to force the query given the dep node
500 dep_node_force_stream.extend(quote! {
501 ::rustc_middle::dep_graph::DepKind::#name => {
502 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
503 if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
504 force_query::<crate::ty::query::queries::#name<'_>, _>(
516 add_query_description_impl(&query, modifiers, &mut query_description_stream);
518 let name = &group.name;
519 query_stream.extend(quote! {
520 #name { #group_stream },
524 dep_node_force_stream.extend(quote! {
525 ::rustc_middle::dep_graph::DepKind::Null => {
526 bug!("Cannot force dep node: {:?}", $dep_node)
530 TokenStream::from(quote! {
531 macro_rules! rustc_query_append {
532 ([$($macro:tt)*][$($other:tt)*]) => {
541 macro_rules! rustc_dep_node_append {
542 ([$($macro:tt)*][$($other:tt)*]) => {
550 macro_rules! rustc_dep_node_force {
551 ([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
552 match $dep_node.kind {
555 #dep_node_force_stream
559 macro_rules! rustc_cached_queries {
561 $($macro)*(#cached_queries);
565 #query_description_stream
567 macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
568 ($dep_node:expr, $tcx:expr) => {
569 match $dep_node.kind {
570 #try_load_from_on_disk_cache_stream