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
,
13 syn
::custom_keyword
!(query
);
16 /// Ident or a wildcard `_`.
17 struct IdentOrWild(Ident
);
19 impl 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()))
25 IdentOrWild(input
.parse()?
)
30 /// A modifier for a query
32 /// The description of the query.
33 Desc(Option
<Ident
>, Punctuated
<Expr
, Token
![,]>),
35 /// Use this type for the in-memory cache.
38 /// Cache the query to disk if the `Expr` returns true.
39 Cache(Option
<(IdentOrWild
, IdentOrWild
)>, Block
),
41 /// Custom code to load the query from disk.
42 LoadCached(Ident
, Ident
, Block
),
44 /// A cycle error for this query aborting the compilation with a fatal error.
47 /// A cycle error results in a delay_bug call
50 /// Don't hash the result, instead just mark a query red if it runs
53 /// Generate a dep node based on the dependencies of the query
56 /// Always evaluate the query, ignoring its dependencies
60 impl Parse
for QueryModifier
{
61 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
62 let modifier
: Ident
= input
.parse()?
;
63 if modifier
== "desc" {
64 // Parse a description modifier like:
65 // `desc { |tcx| "foo {}", tcx.item_path(key) }`
67 braced
!(attr_content
in input
);
68 let tcx
= if attr_content
.peek(Token
![|]) {
69 attr_content
.parse
::<Token
![|]>()?
;
70 let tcx
= attr_content
.parse()?
;
71 attr_content
.parse
::<Token
![|]>()?
;
76 let desc
= attr_content
.parse_terminated(Expr
::parse
)?
;
77 Ok(QueryModifier
::Desc(tcx
, desc
))
78 } else if modifier
== "cache_on_disk_if" {
79 // Parse a cache modifier like:
80 // `cache(tcx, value) { |tcx| key.is_local() }`
81 let has_args
= if let TokenTree
::Group(group
) = input
.fork().parse()?
{
82 group
.delimiter() == Delimiter
::Parenthesis
86 let args
= if has_args
{
88 parenthesized
!(args
in input
);
89 let tcx
= args
.parse()?
;
90 args
.parse
::<Token
![,]>()?
;
91 let value
= args
.parse()?
;
96 let block
= input
.parse()?
;
97 Ok(QueryModifier
::Cache(args
, block
))
98 } else if modifier
== "load_cached" {
99 // Parse a load_cached modifier like:
100 // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }`
102 parenthesized
!(args
in input
);
103 let tcx
= args
.parse()?
;
104 args
.parse
::<Token
![,]>()?
;
105 let id
= args
.parse()?
;
106 let block
= input
.parse()?
;
107 Ok(QueryModifier
::LoadCached(tcx
, id
, block
))
108 } else if modifier
== "storage" {
110 parenthesized
!(args
in input
);
111 let ty
= args
.parse()?
;
112 Ok(QueryModifier
::Storage(ty
))
113 } else if modifier
== "fatal_cycle" {
114 Ok(QueryModifier
::FatalCycle
)
115 } else if modifier
== "cycle_delay_bug" {
116 Ok(QueryModifier
::CycleDelayBug
)
117 } else if modifier
== "no_hash" {
118 Ok(QueryModifier
::NoHash
)
119 } else if modifier
== "anon" {
120 Ok(QueryModifier
::Anon
)
121 } else if modifier
== "eval_always" {
122 Ok(QueryModifier
::EvalAlways
)
124 Err(Error
::new(modifier
.span(), "unknown query modifier"))
129 /// Ensures only doc comment attributes are used
130 fn check_attributes(attrs
: Vec
<Attribute
>) -> Result
<Vec
<Attribute
>> {
131 let inner
= |attr
: Attribute
| {
132 if !attr
.path
.is_ident("doc") {
133 Err(Error
::new(attr
.span(), "attributes not supported on queries"))
134 } else if attr
.style
!= AttrStyle
::Outer
{
137 "attributes must be outer attributes (`///`), not inner attributes",
143 attrs
.into_iter().map(inner
).collect()
146 /// A compiler query. `query ... { ... }`
148 doc_comments
: Vec
<Attribute
>,
149 modifiers
: List
<QueryModifier
>,
156 impl Parse
for Query
{
157 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
158 let doc_comments
= check_attributes(input
.call(Attribute
::parse_outer
)?
)?
;
160 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
161 input
.parse
::<kw
::query
>()?
;
162 let name
: Ident
= input
.parse()?
;
164 parenthesized
!(arg_content
in input
);
165 let key
= arg_content
.parse()?
;
166 arg_content
.parse
::<Token
![:]>()?
;
167 let arg
= arg_content
.parse()?
;
168 let result
= input
.parse()?
;
170 // Parse the query modifiers
172 braced
!(content
in input
);
173 let modifiers
= content
.parse()?
;
175 Ok(Query { doc_comments, modifiers, name, key, arg, result }
)
179 /// A type used to greedily parse another type until the input is empty.
180 struct List
<T
>(Vec
<T
>);
182 impl<T
: Parse
> Parse
for List
<T
> {
183 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
184 let mut list
= Vec
::new();
185 while !input
.is_empty() {
186 list
.push(input
.parse()?
);
192 /// A named group containing queries.
195 queries
: List
<Query
>,
198 impl Parse
for Group
{
199 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
200 let name
: Ident
= input
.parse()?
;
202 braced
!(content
in input
);
203 Ok(Group { name, queries: content.parse()? }
)
207 struct QueryModifiers
{
208 /// The description of the query.
209 desc
: (Option
<Ident
>, Punctuated
<Expr
, Token
![,]>),
211 /// Use this type for the in-memory cache.
212 storage
: Option
<Type
>,
214 /// Cache the query to disk if the `Block` returns true.
215 cache
: Option
<(Option
<(IdentOrWild
, IdentOrWild
)>, Block
)>,
217 /// Custom code to load the query from disk.
218 load_cached
: Option
<(Ident
, Ident
, Block
)>,
220 /// A cycle error for this query aborting the compilation with a fatal error.
223 /// A cycle error results in a delay_bug call
224 cycle_delay_bug
: bool
,
226 /// Don't hash the result, instead just mark a query red if it runs
229 /// Generate a dep node based on the dependencies of the query
232 // Always evaluate the query, ignoring its dependencies
236 /// Process query modifiers into a struct, erroring on duplicates
237 fn process_modifiers(query
: &mut Query
) -> QueryModifiers
{
238 let mut load_cached
= None
;
239 let mut storage
= None
;
240 let mut cache
= None
;
242 let mut fatal_cycle
= false;
243 let mut cycle_delay_bug
= false;
244 let mut no_hash
= 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
::Anon
=> {
293 panic
!("duplicate modifier `anon` for query `{}`", query
.name
);
297 QueryModifier
::EvalAlways
=> {
299 panic
!("duplicate modifier `eval_always` for query `{}`", query
.name
);
305 let desc
= desc
.unwrap_or_else(|| {
306 panic
!("no description provided for query `{}`", query
.name
);
321 /// Add the impl of QueryDescription for the query to `impls` if one is requested
322 fn add_query_description_impl(
324 modifiers
: QueryModifiers
,
325 impls
: &mut proc_macro2
::TokenStream
,
327 let name
= &query
.name
;
328 let arg
= &query
.arg
;
329 let key
= &query
.key
.0;
331 // Find out if we should cache the query on disk
332 let cache
= if let Some((args
, expr
)) = modifiers
.cache
.as_ref() {
333 let try_load_from_disk
= if let Some((tcx
, id
, block
)) = modifiers
.load_cached
.as_ref() {
334 // Use custom code to load the query from disk
337 fn try_load_from_disk(
339 #id: SerializedDepNodeIndex
340 ) -> Option
<Self::Value
> {
345 // Use the default code to load the query from disk
348 fn try_load_from_disk(
350 id
: SerializedDepNodeIndex
351 ) -> Option
<Self::Value
> {
352 tcx
.queries
.on_disk_cache
.try_load_query_result(tcx
, id
)
363 .unwrap_or(quote
! { _ }
);
370 .unwrap_or(quote
! { _ }
);
371 // expr is a `Block`, meaning that `{ #expr }` gets expanded
372 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
375 #[allow(unused_variables, unused_braces)]
379 #value: Option<&Self::Value>
387 if modifiers
.load_cached
.is_some() {
388 panic
!("load_cached modifier on query `{}` without a cache modifier", name
);
393 let (tcx
, desc
) = modifiers
.desc
;
394 let tcx
= tcx
.as_ref().map(|t
| quote
! { #t }
).unwrap_or(quote
! { _ }
);
397 #[allow(unused_variables)]
401 ) -> Cow
<'
static, str> {
402 ::rustc_middle
::ty
::print
::with_no_trimmed_paths(|| format
!(#desc).into())
406 impls
.extend(quote
! {
407 impl<'tcx
> QueryDescription
<TyCtxt
<'tcx
>> for queries
::#name<'tcx> {
414 pub fn rustc_queries(input
: TokenStream
) -> TokenStream
{
415 let groups
= parse_macro_input
!(input
as List
<Group
>);
417 let mut query_stream
= quote
! {}
;
418 let mut query_description_stream
= quote
! {}
;
419 let mut dep_node_def_stream
= quote
! {}
;
420 let mut dep_node_force_stream
= quote
! {}
;
421 let mut try_load_from_on_disk_cache_stream
= quote
! {}
;
422 let mut cached_queries
= quote
! {}
;
424 for group
in groups
.0 {
425 let mut group_stream
= quote
! {}
;
426 for mut query
in group
.queries
.0 {
427 let modifiers
= process_modifiers(&mut query
);
428 let name
= &query
.name
;
429 let arg
= &query
.arg
;
430 let result_full
= &query
.result
;
431 let result
= match query
.result
{
432 ReturnType
::Default
=> quote
! { -> () }
,
433 _
=> quote
! { #result_full }
,
436 if modifiers
.cache
.is_some() {
437 cached_queries
.extend(quote
! {
441 try_load_from_on_disk_cache_stream
.extend(quote
! {
442 ::rustc_middle
::dep_graph
::DepKind
::#name => {
443 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
444 debug_assert
!($tcx
.dep_graph
445 .node_color($dep_node
)
446 .map(|c
| c
.is_green())
449 let key
= <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
450 if queries
::#name::cache_on_disk($tcx, &key, None) {
451 let _
= $tcx
.#name(key);
458 let mut attributes
= Vec
::new();
460 // Pass on the fatal_cycle modifier
461 if modifiers
.fatal_cycle
{
462 attributes
.push(quote
! { fatal_cycle }
);
464 // Pass on the storage modifier
465 if let Some(ref ty
) = modifiers
.storage
{
466 attributes
.push(quote
! { storage(#ty) }
);
468 // Pass on the cycle_delay_bug modifier
469 if modifiers
.cycle_delay_bug
{
470 attributes
.push(quote
! { cycle_delay_bug }
);
472 // Pass on the no_hash modifier
473 if modifiers
.no_hash
{
474 attributes
.push(quote
! { no_hash }
);
476 // Pass on the anon modifier
478 attributes
.push(quote
! { anon }
);
480 // Pass on the eval_always modifier
481 if modifiers
.eval_always
{
482 attributes
.push(quote
! { eval_always }
);
485 let attribute_stream
= quote
! {#(#attributes),*}
;
486 let doc_comments
= query
.doc_comments
.iter();
487 // Add the query to the group
488 group_stream
.extend(quote
! {
490 [#attribute_stream] fn #name: #name(#arg) #result,
493 // Create a dep node for the query
494 dep_node_def_stream
.extend(quote
! {
495 [#attribute_stream] #name(#arg),
498 // Add a match arm to force the query given the dep node
499 dep_node_force_stream
.extend(quote
! {
500 ::rustc_middle
::dep_graph
::DepKind
::#name => {
501 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
502 if let Some(key
) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
503 force_query
::<crate::ty
::query
::queries
::#name<'_>, _>(
515 add_query_description_impl(&query
, modifiers
, &mut query_description_stream
);
517 let name
= &group
.name
;
518 query_stream
.extend(quote
! {
519 #name { #group_stream },
523 dep_node_force_stream
.extend(quote
! {
524 ::rustc_middle
::dep_graph
::DepKind
::Null
=> {
525 bug
!("Cannot force dep node: {:?}", $dep_node
)
529 TokenStream
::from(quote
! {
530 macro_rules
! rustc_query_append
{
531 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
540 macro_rules
! rustc_dep_node_append
{
541 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
549 macro_rules
! rustc_dep_node_force
{
550 ([$dep_node
:expr
, $tcx
:expr
] $
($other
:tt
)*) => {
551 match $dep_node
.kind
{
554 #dep_node_force_stream
558 macro_rules
! rustc_cached_queries
{
560 $
($
macro)*(#cached_queries);
564 #query_description_stream
566 macro_rules
! rustc_dep_node_try_load_from_on_disk_cache
{
567 ($dep_node
:expr
, $tcx
:expr
) => {
568 match $dep_node
.kind
{
569 #try_load_from_on_disk_cache_stream