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 /// 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" {
110 let ty
= input
.parse()?
;
111 Ok(QueryModifier
::Storage(ty
))
112 } else if modifier
== "fatal_cycle" {
113 Ok(QueryModifier
::FatalCycle
)
114 } else if modifier
== "cycle_delay_bug" {
115 Ok(QueryModifier
::CycleDelayBug
)
116 } else if modifier
== "no_hash" {
117 Ok(QueryModifier
::NoHash
)
118 } else if modifier
== "anon" {
119 Ok(QueryModifier
::Anon
)
120 } else if modifier
== "eval_always" {
121 Ok(QueryModifier
::EvalAlways
)
123 Err(Error
::new(modifier
.span(), "unknown query modifier"))
128 /// Ensures only doc comment attributes are used
129 fn check_attributes(attrs
: Vec
<Attribute
>) -> Result
<()> {
131 if !attr
.path
.is_ident("doc") {
132 return Err(Error
::new(attr
.span(), "attributes not supported on queries"));
138 /// A compiler query. `query ... { ... }`
140 modifiers
: List
<QueryModifier
>,
147 impl Parse
for Query
{
148 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
149 check_attributes(input
.call(Attribute
::parse_outer
)?
)?
;
151 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
152 input
.parse
::<kw
::query
>()?
;
153 let name
: Ident
= input
.parse()?
;
155 parenthesized
!(arg_content
in input
);
156 let key
= arg_content
.parse()?
;
157 arg_content
.parse
::<Token
![:]>()?
;
158 let arg
= arg_content
.parse()?
;
159 let result
= input
.parse()?
;
161 // Parse the query modifiers
163 braced
!(content
in input
);
164 let modifiers
= content
.parse()?
;
166 Ok(Query { modifiers, name, key, arg, result }
)
170 /// A type used to greedily parse another type until the input is empty.
171 struct List
<T
>(Vec
<T
>);
173 impl<T
: Parse
> Parse
for List
<T
> {
174 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
175 let mut list
= Vec
::new();
176 while !input
.is_empty() {
177 list
.push(input
.parse()?
);
183 /// A named group containing queries.
186 queries
: List
<Query
>,
189 impl Parse
for Group
{
190 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
191 let name
: Ident
= input
.parse()?
;
193 braced
!(content
in input
);
194 Ok(Group { name, queries: content.parse()? }
)
198 struct QueryModifiers
{
199 /// The description of the query.
200 desc
: Option
<(Option
<Ident
>, Punctuated
<Expr
, Token
![,]>)>,
202 /// Use this type for the in-memory cache.
203 storage
: Option
<Type
>,
205 /// Cache the query to disk if the `Block` returns true.
206 cache
: Option
<(Option
<(IdentOrWild
, IdentOrWild
)>, Block
)>,
208 /// Custom code to load the query from disk.
209 load_cached
: Option
<(Ident
, Ident
, Block
)>,
211 /// A cycle error for this query aborting the compilation with a fatal error.
214 /// A cycle error results in a delay_bug call
215 cycle_delay_bug
: bool
,
217 /// Don't hash the result, instead just mark a query red if it runs
220 /// Generate a dep node based on the dependencies of the query
223 // Always evaluate the query, ignoring its dependencies
227 /// Process query modifiers into a struct, erroring on duplicates
228 fn process_modifiers(query
: &mut Query
) -> QueryModifiers
{
229 let mut load_cached
= None
;
230 let mut storage
= None
;
231 let mut cache
= None
;
233 let mut fatal_cycle
= false;
234 let mut cycle_delay_bug
= false;
235 let mut no_hash
= false;
236 let mut anon
= false;
237 let mut eval_always
= false;
238 for modifier
in query
.modifiers
.0.drain(..) {
240 QueryModifier
::LoadCached(tcx
, id
, block
) => {
241 if load_cached
.is_some() {
242 panic
!("duplicate modifier `load_cached` for query `{}`", query
.name
);
244 load_cached
= Some((tcx
, id
, block
));
246 QueryModifier
::Storage(ty
) => {
247 if storage
.is_some() {
248 panic
!("duplicate modifier `storage` for query `{}`", query
.name
);
252 QueryModifier
::Cache(args
, expr
) => {
254 panic
!("duplicate modifier `cache` for query `{}`", query
.name
);
256 cache
= Some((args
, expr
));
258 QueryModifier
::Desc(tcx
, list
) => {
260 panic
!("duplicate modifier `desc` for query `{}`", query
.name
);
262 desc
= Some((tcx
, list
));
264 QueryModifier
::FatalCycle
=> {
266 panic
!("duplicate modifier `fatal_cycle` for query `{}`", query
.name
);
270 QueryModifier
::CycleDelayBug
=> {
272 panic
!("duplicate modifier `cycle_delay_bug` for query `{}`", query
.name
);
274 cycle_delay_bug
= true;
276 QueryModifier
::NoHash
=> {
278 panic
!("duplicate modifier `no_hash` for query `{}`", query
.name
);
282 QueryModifier
::Anon
=> {
284 panic
!("duplicate modifier `anon` for query `{}`", query
.name
);
288 QueryModifier
::EvalAlways
=> {
290 panic
!("duplicate modifier `eval_always` for query `{}`", query
.name
);
309 /// Add the impl of QueryDescription for the query to `impls` if one is requested
310 fn add_query_description_impl(
312 modifiers
: QueryModifiers
,
313 impls
: &mut proc_macro2
::TokenStream
,
315 let name
= &query
.name
;
316 let arg
= &query
.arg
;
317 let key
= &query
.key
.0;
319 // Find out if we should cache the query on disk
320 let cache
= modifiers
.cache
.as_ref().map(|(args
, expr
)| {
321 let try_load_from_disk
= if let Some((tcx
, id
, block
)) = modifiers
.load_cached
.as_ref() {
322 // Use custom code to load the query from disk
325 fn try_load_from_disk(
327 #id: SerializedDepNodeIndex
328 ) -> Option
<Self::Value
> {
333 // Use the default code to load the query from disk
336 fn try_load_from_disk(
338 id
: SerializedDepNodeIndex
339 ) -> Option
<Self::Value
> {
340 tcx
.queries
.on_disk_cache
.try_load_query_result(tcx
, id
)
351 .unwrap_or(quote
! { _ }
);
358 .unwrap_or(quote
! { _ }
);
359 // expr is a `Block`, meaning that `{ #expr }` gets expanded
360 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
363 #[allow(unused_variables, unused_braces)]
367 #value: Option<&Self::Value>
376 if cache
.is_none() && modifiers
.load_cached
.is_some() {
377 panic
!("load_cached modifier on query `{}` without a cache modifier", name
);
380 let desc
= modifiers
.desc
.as_ref().map(|(tcx
, desc
)| {
381 let tcx
= tcx
.as_ref().map(|t
| quote
! { #t }
).unwrap_or(quote
! { _ }
);
383 #[allow(unused_variables)]
387 ) -> Cow
<'
static, str> {
388 format
!(#desc).into()
393 if desc
.is_some() || cache
.is_some() {
394 let cache
= cache
.unwrap_or(quote
! {}
);
395 let desc
= desc
.unwrap_or(quote
! {}
);
397 impls
.extend(quote
! {
398 impl<'tcx
> QueryDescription
<TyCtxt
<'tcx
>> for queries
::#name<'tcx> {
406 pub fn rustc_queries(input
: TokenStream
) -> TokenStream
{
407 let groups
= parse_macro_input
!(input
as List
<Group
>);
409 let mut query_stream
= quote
! {}
;
410 let mut query_description_stream
= quote
! {}
;
411 let mut dep_node_def_stream
= quote
! {}
;
412 let mut dep_node_force_stream
= quote
! {}
;
413 let mut try_load_from_on_disk_cache_stream
= quote
! {}
;
414 let mut cached_queries
= quote
! {}
;
416 for group
in groups
.0 {
417 let mut group_stream
= quote
! {}
;
418 for mut query
in group
.queries
.0 {
419 let modifiers
= process_modifiers(&mut query
);
420 let name
= &query
.name
;
421 let arg
= &query
.arg
;
422 let result_full
= &query
.result
;
423 let result
= match query
.result
{
424 ReturnType
::Default
=> quote
! { -> () }
,
425 _
=> quote
! { #result_full }
,
428 if modifiers
.cache
.is_some() {
429 cached_queries
.extend(quote
! {
433 try_load_from_on_disk_cache_stream
.extend(quote
! {
434 ::rustc_middle
::dep_graph
::DepKind
::#name => {
435 if <#arg as DepNodeParams<TyCtxt<'_>>>::CAN_RECONSTRUCT_QUERY_KEY {
436 debug_assert
!($tcx
.dep_graph
437 .node_color($dep_node
)
438 .map(|c
| c
.is_green())
441 let key
= <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
442 if queries
::#name::cache_on_disk($tcx, key, None) {
443 let _
= $tcx
.#name(key);
450 let mut attributes
= Vec
::new();
452 // Pass on the fatal_cycle modifier
453 if modifiers
.fatal_cycle
{
454 attributes
.push(quote
! { fatal_cycle }
);
456 // Pass on the storage modifier
457 if let Some(ref ty
) = modifiers
.storage
{
458 attributes
.push(quote
! { storage(#ty) }
);
460 // Pass on the cycle_delay_bug modifier
461 if modifiers
.cycle_delay_bug
{
462 attributes
.push(quote
! { cycle_delay_bug }
);
464 // Pass on the no_hash modifier
465 if modifiers
.no_hash
{
466 attributes
.push(quote
! { no_hash }
);
468 // Pass on the anon modifier
470 attributes
.push(quote
! { anon }
);
472 // Pass on the eval_always modifier
473 if modifiers
.eval_always
{
474 attributes
.push(quote
! { eval_always }
);
477 let attribute_stream
= quote
! {#(#attributes),*}
;
479 // Add the query to the group
480 group_stream
.extend(quote
! {
481 [#attribute_stream] fn #name: #name(#arg) #result,
484 // Create a dep node for the query
485 dep_node_def_stream
.extend(quote
! {
486 [#attribute_stream] #name(#arg),
489 // Add a match arm to force the query given the dep node
490 dep_node_force_stream
.extend(quote
! {
491 ::rustc_middle
::dep_graph
::DepKind
::#name => {
492 if <#arg as DepNodeParams<TyCtxt<'_>>>::CAN_RECONSTRUCT_QUERY_KEY {
493 if let Some(key
) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
494 force_query
::<crate::ty
::query
::queries
::#name<'_>, _>(
506 add_query_description_impl(&query
, modifiers
, &mut query_description_stream
);
508 let name
= &group
.name
;
509 query_stream
.extend(quote
! {
510 #name { #group_stream },
514 dep_node_force_stream
.extend(quote
! {
515 ::rustc_middle
::dep_graph
::DepKind
::Null
=> {
516 bug
!("Cannot force dep node: {:?}", $dep_node
)
520 TokenStream
::from(quote
! {
521 macro_rules
! rustc_query_append
{
522 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
531 macro_rules
! rustc_dep_node_append
{
532 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
540 macro_rules
! rustc_dep_node_force
{
541 ([$dep_node
:expr
, $tcx
:expr
] $
($other
:tt
)*) => {
542 match $dep_node
.kind
{
545 #dep_node_force_stream
549 macro_rules
! rustc_cached_queries
{
551 $
($
macro)*(#cached_queries);
555 #query_description_stream
557 macro_rules
! rustc_dep_node_try_load_from_on_disk_cache
{
558 ($dep_node
:expr
, $tcx
:expr
) => {
559 match $dep_node
.kind
{
560 #try_load_from_on_disk_cache_stream