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" {
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
<()> {
133 if !attr
.path
.is_ident("doc") {
134 return Err(Error
::new(attr
.span(), "attributes not supported on queries"));
140 /// A compiler query. `query ... { ... }`
142 modifiers
: List
<QueryModifier
>,
149 impl Parse
for Query
{
150 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
151 check_attributes(input
.call(Attribute
::parse_outer
)?
)?
;
153 // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
154 input
.parse
::<kw
::query
>()?
;
155 let name
: Ident
= input
.parse()?
;
157 parenthesized
!(arg_content
in input
);
158 let key
= arg_content
.parse()?
;
159 arg_content
.parse
::<Token
![:]>()?
;
160 let arg
= arg_content
.parse()?
;
161 let result
= input
.parse()?
;
163 // Parse the query modifiers
165 braced
!(content
in input
);
166 let modifiers
= content
.parse()?
;
168 Ok(Query { modifiers, name, key, arg, result }
)
172 /// A type used to greedily parse another type until the input is empty.
173 struct List
<T
>(Vec
<T
>);
175 impl<T
: Parse
> Parse
for List
<T
> {
176 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
177 let mut list
= Vec
::new();
178 while !input
.is_empty() {
179 list
.push(input
.parse()?
);
185 /// A named group containing queries.
188 queries
: List
<Query
>,
191 impl Parse
for Group
{
192 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
193 let name
: Ident
= input
.parse()?
;
195 braced
!(content
in input
);
196 Ok(Group { name, queries: content.parse()? }
)
200 struct QueryModifiers
{
201 /// The description of the query.
202 desc
: (Option
<Ident
>, Punctuated
<Expr
, Token
![,]>),
204 /// Use this type for the in-memory cache.
205 storage
: Option
<Type
>,
207 /// Cache the query to disk if the `Block` returns true.
208 cache
: Option
<(Option
<(IdentOrWild
, IdentOrWild
)>, Block
)>,
210 /// Custom code to load the query from disk.
211 load_cached
: Option
<(Ident
, Ident
, Block
)>,
213 /// A cycle error for this query aborting the compilation with a fatal error.
216 /// A cycle error results in a delay_bug call
217 cycle_delay_bug
: bool
,
219 /// Don't hash the result, instead just mark a query red if it runs
222 /// Generate a dep node based on the dependencies of the query
225 // Always evaluate the query, ignoring its dependencies
229 /// Process query modifiers into a struct, erroring on duplicates
230 fn process_modifiers(query
: &mut Query
) -> QueryModifiers
{
231 let mut load_cached
= None
;
232 let mut storage
= None
;
233 let mut cache
= None
;
235 let mut fatal_cycle
= false;
236 let mut cycle_delay_bug
= false;
237 let mut no_hash
= false;
238 let mut anon
= false;
239 let mut eval_always
= false;
240 for modifier
in query
.modifiers
.0.drain(..) {
242 QueryModifier
::LoadCached(tcx
, id
, block
) => {
243 if load_cached
.is_some() {
244 panic
!("duplicate modifier `load_cached` for query `{}`", query
.name
);
246 load_cached
= Some((tcx
, id
, block
));
248 QueryModifier
::Storage(ty
) => {
249 if storage
.is_some() {
250 panic
!("duplicate modifier `storage` for query `{}`", query
.name
);
254 QueryModifier
::Cache(args
, expr
) => {
256 panic
!("duplicate modifier `cache` for query `{}`", query
.name
);
258 cache
= Some((args
, expr
));
260 QueryModifier
::Desc(tcx
, list
) => {
262 panic
!("duplicate modifier `desc` for query `{}`", query
.name
);
264 desc
= Some((tcx
, list
));
266 QueryModifier
::FatalCycle
=> {
268 panic
!("duplicate modifier `fatal_cycle` for query `{}`", query
.name
);
272 QueryModifier
::CycleDelayBug
=> {
274 panic
!("duplicate modifier `cycle_delay_bug` for query `{}`", query
.name
);
276 cycle_delay_bug
= true;
278 QueryModifier
::NoHash
=> {
280 panic
!("duplicate modifier `no_hash` for query `{}`", query
.name
);
284 QueryModifier
::Anon
=> {
286 panic
!("duplicate modifier `anon` for query `{}`", query
.name
);
290 QueryModifier
::EvalAlways
=> {
292 panic
!("duplicate modifier `eval_always` for query `{}`", query
.name
);
298 let desc
= desc
.unwrap_or_else(|| {
299 panic
!("no description provided for query `{}`", query
.name
);
314 /// Add the impl of QueryDescription for the query to `impls` if one is requested
315 fn add_query_description_impl(
317 modifiers
: QueryModifiers
,
318 impls
: &mut proc_macro2
::TokenStream
,
320 let name
= &query
.name
;
321 let arg
= &query
.arg
;
322 let key
= &query
.key
.0;
324 // Find out if we should cache the query on disk
325 let cache
= if let Some((args
, expr
)) = modifiers
.cache
.as_ref() {
326 let try_load_from_disk
= if let Some((tcx
, id
, block
)) = modifiers
.load_cached
.as_ref() {
327 // Use custom code to load the query from disk
330 fn try_load_from_disk(
332 #id: SerializedDepNodeIndex
333 ) -> Option
<Self::Value
> {
338 // Use the default code to load the query from disk
341 fn try_load_from_disk(
343 id
: SerializedDepNodeIndex
344 ) -> Option
<Self::Value
> {
345 tcx
.queries
.on_disk_cache
.try_load_query_result(tcx
, id
)
356 .unwrap_or(quote
! { _ }
);
363 .unwrap_or(quote
! { _ }
);
364 // expr is a `Block`, meaning that `{ #expr }` gets expanded
365 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
368 #[allow(unused_variables, unused_braces)]
372 #value: Option<&Self::Value>
380 if modifiers
.load_cached
.is_some() {
381 panic
!("load_cached modifier on query `{}` without a cache modifier", name
);
386 let (tcx
, desc
) = modifiers
.desc
;
387 let tcx
= tcx
.as_ref().map(|t
| quote
! { #t }
).unwrap_or(quote
! { _ }
);
390 #[allow(unused_variables)]
394 ) -> Cow
<'
static, str> {
395 format
!(#desc).into()
399 impls
.extend(quote
! {
400 impl<'tcx
> QueryDescription
<TyCtxt
<'tcx
>> for queries
::#name<'tcx> {
407 pub fn rustc_queries(input
: TokenStream
) -> TokenStream
{
408 let groups
= parse_macro_input
!(input
as List
<Group
>);
410 let mut query_stream
= quote
! {}
;
411 let mut query_description_stream
= quote
! {}
;
412 let mut dep_node_def_stream
= quote
! {}
;
413 let mut dep_node_force_stream
= quote
! {}
;
414 let mut try_load_from_on_disk_cache_stream
= quote
! {}
;
415 let mut cached_queries
= quote
! {}
;
417 for group
in groups
.0 {
418 let mut group_stream
= quote
! {}
;
419 for mut query
in group
.queries
.0 {
420 let modifiers
= process_modifiers(&mut query
);
421 let name
= &query
.name
;
422 let arg
= &query
.arg
;
423 let result_full
= &query
.result
;
424 let result
= match query
.result
{
425 ReturnType
::Default
=> quote
! { -> () }
,
426 _
=> quote
! { #result_full }
,
429 if modifiers
.cache
.is_some() {
430 cached_queries
.extend(quote
! {
434 try_load_from_on_disk_cache_stream
.extend(quote
! {
435 ::rustc_middle
::dep_graph
::DepKind
::#name => {
436 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
437 debug_assert
!($tcx
.dep_graph
438 .node_color($dep_node
)
439 .map(|c
| c
.is_green())
442 let key
= <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
443 if queries
::#name::cache_on_disk($tcx, &key, None) {
444 let _
= $tcx
.#name(key);
451 let mut attributes
= Vec
::new();
453 // Pass on the fatal_cycle modifier
454 if modifiers
.fatal_cycle
{
455 attributes
.push(quote
! { fatal_cycle }
);
457 // Pass on the storage modifier
458 if let Some(ref ty
) = modifiers
.storage
{
459 attributes
.push(quote
! { storage(#ty) }
);
461 // Pass on the cycle_delay_bug modifier
462 if modifiers
.cycle_delay_bug
{
463 attributes
.push(quote
! { cycle_delay_bug }
);
465 // Pass on the no_hash modifier
466 if modifiers
.no_hash
{
467 attributes
.push(quote
! { no_hash }
);
469 // Pass on the anon modifier
471 attributes
.push(quote
! { anon }
);
473 // Pass on the eval_always modifier
474 if modifiers
.eval_always
{
475 attributes
.push(quote
! { eval_always }
);
478 let attribute_stream
= quote
! {#(#attributes),*}
;
480 // Add the query to the group
481 group_stream
.extend(quote
! {
482 [#attribute_stream] fn #name: #name(#arg) #result,
485 // Create a dep node for the query
486 dep_node_def_stream
.extend(quote
! {
487 [#attribute_stream] #name(#arg),
490 // Add a match arm to force the query given the dep node
491 dep_node_force_stream
.extend(quote
! {
492 ::rustc_middle
::dep_graph
::DepKind
::#name => {
493 if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
494 if let Some(key
) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
495 force_query
::<crate::ty
::query
::queries
::#name<'_>, _>(
507 add_query_description_impl(&query
, modifiers
, &mut query_description_stream
);
509 let name
= &group
.name
;
510 query_stream
.extend(quote
! {
511 #name { #group_stream },
515 dep_node_force_stream
.extend(quote
! {
516 ::rustc_middle
::dep_graph
::DepKind
::Null
=> {
517 bug
!("Cannot force dep node: {:?}", $dep_node
)
521 TokenStream
::from(quote
! {
522 macro_rules
! rustc_query_append
{
523 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
532 macro_rules
! rustc_dep_node_append
{
533 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
541 macro_rules
! rustc_dep_node_force
{
542 ([$dep_node
:expr
, $tcx
:expr
] $
($other
:tt
)*) => {
543 match $dep_node
.kind
{
546 #dep_node_force_stream
550 macro_rules
! rustc_cached_queries
{
552 $
($
macro)*(#cached_queries);
556 #query_description_stream
558 macro_rules
! rustc_dep_node_try_load_from_on_disk_cache
{
559 ($dep_node
:expr
, $tcx
:expr
) => {
560 match $dep_node
.kind
{
561 #try_load_from_on_disk_cache_stream