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, Attribute, Block, Error, Expr, Ident, ReturnType,
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 /// Don't force the query
57 /// Generate a dep node based on the dependencies of the query
60 /// Always evaluate the query, ignoring its depdendencies
64 impl Parse for QueryModifier {
65 fn parse(input: ParseStream<'_>) -> Result<Self> {
66 let modifier: Ident = input.parse()?;
67 if modifier == "desc" {
68 // Parse a description modifier like:
69 // `desc { |tcx| "foo {}", tcx.item_path(key) }`
71 braced!(attr_content in input);
72 let tcx = if attr_content.peek(Token![|]) {
73 attr_content.parse::<Token![|]>()?;
74 let tcx = attr_content.parse()?;
75 attr_content.parse::<Token![|]>()?;
80 let desc = attr_content.parse_terminated(Expr::parse)?;
81 Ok(QueryModifier::Desc(tcx, desc))
82 } else if modifier == "cache_on_disk_if" {
83 // Parse a cache modifier like:
84 // `cache(tcx, value) { |tcx| key.is_local() }`
85 let has_args = if let TokenTree::Group(group) = input.fork().parse()? {
86 group.delimiter() == Delimiter::Parenthesis
90 let args = if has_args {
92 parenthesized!(args in input);
93 let tcx = args.parse()?;
94 args.parse::<Token![,]>()?;
95 let value = args.parse()?;
100 let block = input.parse()?;
101 Ok(QueryModifier::Cache(args, block))
102 } else if modifier == "load_cached" {
103 // Parse a load_cached modifier like:
104 // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }`
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))
112 } else if modifier == "storage" {
113 let ty = input.parse()?;
114 Ok(QueryModifier::Storage(ty))
115 } else if modifier == "fatal_cycle" {
116 Ok(QueryModifier::FatalCycle)
117 } else if modifier == "cycle_delay_bug" {
118 Ok(QueryModifier::CycleDelayBug)
119 } else if modifier == "no_hash" {
120 Ok(QueryModifier::NoHash)
121 } else if modifier == "no_force" {
122 Ok(QueryModifier::NoForce)
123 } else if modifier == "anon" {
124 Ok(QueryModifier::Anon)
125 } else if modifier == "eval_always" {
126 Ok(QueryModifier::EvalAlways)
128 Err(Error::new(modifier.span(), "unknown query modifier"))
133 /// Ensures only doc comment attributes are used
134 fn check_attributes(attrs: Vec<Attribute>) -> Result<()> {
136 if !attr.path.is_ident("doc") {
137 return Err(Error::new(attr.span(), "attributes not supported on queries"));
143 /// A compiler query. `query ... { ... }`
145 modifiers: List<QueryModifier>,
152 impl Parse for Query {
153 fn parse(input: ParseStream<'_>) -> Result<Self> {
154 check_attributes(input.call(Attribute::parse_outer)?)?;
156 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
157 input.parse::<kw::query>()?;
158 let name: Ident = input.parse()?;
160 parenthesized!(arg_content in input);
161 let key = arg_content.parse()?;
162 arg_content.parse::<Token![:]>()?;
163 let arg = arg_content.parse()?;
164 let result = input.parse()?;
166 // Parse the query modifiers
168 braced!(content in input);
169 let modifiers = content.parse()?;
171 Ok(Query { modifiers, name, key, arg, result })
175 /// A type used to greedily parse another type until the input is empty.
176 struct List<T>(Vec<T>);
178 impl<T: Parse> Parse for List<T> {
179 fn parse(input: ParseStream<'_>) -> Result<Self> {
180 let mut list = Vec::new();
181 while !input.is_empty() {
182 list.push(input.parse()?);
188 /// A named group containing queries.
191 queries: List<Query>,
194 impl Parse for Group {
195 fn parse(input: ParseStream<'_>) -> Result<Self> {
196 let name: Ident = input.parse()?;
198 braced!(content in input);
199 Ok(Group { name, queries: content.parse()? })
203 struct QueryModifiers {
204 /// The description of the query.
205 desc: Option<(Option<Ident>, Punctuated<Expr, Token![,]>)>,
207 /// Use this type for the in-memory cache.
208 storage: Option<Type>,
210 /// Cache the query to disk if the `Block` returns true.
211 cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>,
213 /// Custom code to load the query from disk.
214 load_cached: Option<(Ident, Ident, Block)>,
216 /// A cycle error for this query aborting the compilation with a fatal error.
219 /// A cycle error results in a delay_bug call
220 cycle_delay_bug: bool,
222 /// Don't hash the result, instead just mark a query red if it runs
225 /// Don't force the query
228 /// Generate a dep node based on the dependencies of the query
231 // Always evaluate the query, ignoring its depdendencies
235 /// Process query modifiers into a struct, erroring on duplicates
236 fn process_modifiers(query: &mut Query) -> QueryModifiers {
237 let mut load_cached = None;
238 let mut storage = None;
239 let mut cache = None;
241 let mut fatal_cycle = false;
242 let mut cycle_delay_bug = false;
243 let mut no_hash = false;
244 let mut no_force = false;
245 let mut anon = false;
246 let mut eval_always = false;
247 for modifier in query.modifiers.0.drain(..) {
249 QueryModifier::LoadCached(tcx, id, block) => {
250 if load_cached.is_some() {
251 panic!("duplicate modifier `load_cached` for query `{}`", query.name);
253 load_cached = Some((tcx, id, block));
255 QueryModifier::Storage(ty) => {
256 if storage.is_some() {
257 panic!("duplicate modifier `storage` for query `{}`", query.name);
261 QueryModifier::Cache(args, expr) => {
263 panic!("duplicate modifier `cache` for query `{}`", query.name);
265 cache = Some((args, expr));
267 QueryModifier::Desc(tcx, list) => {
269 panic!("duplicate modifier `desc` for query `{}`", query.name);
271 desc = Some((tcx, list));
273 QueryModifier::FatalCycle => {
275 panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name);
279 QueryModifier::CycleDelayBug => {
281 panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name);
283 cycle_delay_bug = true;
285 QueryModifier::NoHash => {
287 panic!("duplicate modifier `no_hash` for query `{}`", query.name);
291 QueryModifier::NoForce => {
293 panic!("duplicate modifier `no_force` for query `{}`", query.name);
297 QueryModifier::Anon => {
299 panic!("duplicate modifier `anon` for query `{}`", query.name);
303 QueryModifier::EvalAlways => {
305 panic!("duplicate modifier `eval_always` for query `{}`", query.name);
325 /// Add the impl of QueryDescription for the query to `impls` if one is requested
326 fn add_query_description_impl(
328 modifiers: QueryModifiers,
329 impls: &mut proc_macro2::TokenStream,
331 let name = &query.name;
332 let arg = &query.arg;
333 let key = &query.key.0;
335 // Find out if we should cache the query on disk
336 let cache = modifiers.cache.as_ref().map(|(args, expr)| {
337 let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() {
338 // Use custom code to load the query from disk
341 fn try_load_from_disk(
343 #id: SerializedDepNodeIndex
344 ) -> Option<Self::Value> {
349 // Use the default code to load the query from disk
352 fn try_load_from_disk(
354 id: SerializedDepNodeIndex
355 ) -> Option<Self::Value> {
356 tcx.queries.on_disk_cache.try_load_query_result(tcx, id)
367 .unwrap_or(quote! { _ });
374 .unwrap_or(quote! { _ });
377 #[allow(unused_variables)]
381 #value: Option<&Self::Value>
390 if cache.is_none() && modifiers.load_cached.is_some() {
391 panic!("load_cached modifier on query `{}` without a cache modifier", name);
394 let desc = modifiers.desc.as_ref().map(|(tcx, desc)| {
395 let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ });
397 #[allow(unused_variables)]
401 ) -> Cow<'static, str> {
402 format!(#desc).into()
407 if desc.is_some() || cache.is_some() {
408 let cache = cache.unwrap_or(quote! {});
409 let desc = desc.unwrap_or(quote! {});
411 impls.extend(quote! {
412 impl<'tcx> QueryDescription<'tcx> for queries::#name<'tcx> {
420 pub fn rustc_queries(input: TokenStream) -> TokenStream {
421 let groups = parse_macro_input!(input as List<Group>);
423 let mut query_stream = quote! {};
424 let mut query_description_stream = quote! {};
425 let mut dep_node_def_stream = quote! {};
426 let mut dep_node_force_stream = quote! {};
427 let mut try_load_from_on_disk_cache_stream = quote! {};
428 let mut no_force_queries = Vec::new();
429 let mut cached_queries = quote! {};
431 for group in groups.0 {
432 let mut group_stream = quote! {};
433 for mut query in group.queries.0 {
434 let modifiers = process_modifiers(&mut query);
435 let name = &query.name;
436 let arg = &query.arg;
437 let result_full = &query.result;
438 let result = match query.result {
439 ReturnType::Default => quote! { -> () },
440 _ => quote! { #result_full },
443 if modifiers.cache.is_some() {
444 cached_queries.extend(quote! {
449 if modifiers.cache.is_some() && !modifiers.no_force {
450 try_load_from_on_disk_cache_stream.extend(quote! {
452 debug_assert!(tcx.dep_graph
454 .map(|c| c.is_green())
457 let key = RecoverKey::recover(tcx, self).unwrap();
458 if queries::#name::cache_on_disk(tcx, key, None) {
459 let _ = tcx.#name(key);
465 let mut attributes = Vec::new();
467 // Pass on the fatal_cycle modifier
468 if modifiers.fatal_cycle {
469 attributes.push(quote! { fatal_cycle });
471 // Pass on the storage modifier
472 if let Some(ref ty) = modifiers.storage {
473 attributes.push(quote! { storage(#ty) });
475 // Pass on the cycle_delay_bug modifier
476 if modifiers.cycle_delay_bug {
477 attributes.push(quote! { cycle_delay_bug });
479 // Pass on the no_hash modifier
480 if modifiers.no_hash {
481 attributes.push(quote! { no_hash });
483 // Pass on the anon modifier
485 attributes.push(quote! { anon });
487 // Pass on the eval_always modifier
488 if modifiers.eval_always {
489 attributes.push(quote! { eval_always });
492 let attribute_stream = quote! {#(#attributes),*};
494 // Add the query to the group
495 group_stream.extend(quote! {
496 [#attribute_stream] fn #name: #name(#arg) #result,
499 // Create a dep node for the query
500 dep_node_def_stream.extend(quote! {
501 [#attribute_stream] #name(#arg),
504 if modifiers.no_force {
505 no_force_queries.push(name.clone());
507 // Add a match arm to force the query given the dep node
508 dep_node_force_stream.extend(quote! {
510 if let Some(key) = RecoverKey::recover($tcx, $dep_node) {
511 $tcx.force_query::<crate::ty::query::queries::#name<'_>>(
523 add_query_description_impl(&query, modifiers, &mut query_description_stream);
525 let name = &group.name;
526 query_stream.extend(quote! {
527 #name { #group_stream },
531 // Add an arm for the no force queries to panic when trying to force them
532 for query in no_force_queries {
533 dep_node_force_stream.extend(quote! {
537 dep_node_force_stream.extend(quote! {
539 bug!("Cannot force dep node: {:?}", $dep_node)
543 TokenStream::from(quote! {
544 macro_rules! rustc_query_append {
545 ([$($macro:tt)*][$($other:tt)*]) => {
554 macro_rules! rustc_dep_node_append {
555 ([$($macro:tt)*][$($other:tt)*]) => {
563 macro_rules! rustc_dep_node_force {
564 ([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
565 match $dep_node.kind {
568 #dep_node_force_stream
572 macro_rules! rustc_cached_queries {
574 $($macro)*(#cached_queries);
578 #query_description_stream
581 /// Check whether the query invocation corresponding to the given
582 /// DepNode is eligible for on-disk-caching. If so, this is method
583 /// will execute the query corresponding to the given DepNode.
584 /// Also, as a sanity check, it expects that the corresponding query
585 /// invocation has been marked as green already.
586 pub fn try_load_from_on_disk_cache(&self, tcx: TyCtxt<'_>) {
588 #try_load_from_on_disk_cache_stream