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.
194 /// For now, the name is not used any more, but the capability remains interesting for future
195 /// developments of the query system.
199 queries
: List
<Query
>,
202 impl Parse
for Group
{
203 fn parse(input
: ParseStream
<'_
>) -> Result
<Self> {
204 let name
: Ident
= input
.parse()?
;
206 braced
!(content
in input
);
207 Ok(Group { name, queries: content.parse()? }
)
211 struct QueryModifiers
{
212 /// The description of the query.
213 desc
: (Option
<Ident
>, Punctuated
<Expr
, Token
![,]>),
215 /// Use this type for the in-memory cache.
216 storage
: Option
<Type
>,
218 /// Cache the query to disk if the `Block` returns true.
219 cache
: Option
<(Option
<(IdentOrWild
, IdentOrWild
)>, Block
)>,
221 /// Custom code to load the query from disk.
222 load_cached
: Option
<(Ident
, Ident
, Block
)>,
224 /// A cycle error for this query aborting the compilation with a fatal error.
227 /// A cycle error results in a delay_bug call
228 cycle_delay_bug
: bool
,
230 /// Don't hash the result, instead just mark a query red if it runs
233 /// Generate a dep node based on the dependencies of the query
236 // Always evaluate the query, ignoring its dependencies
240 /// Process query modifiers into a struct, erroring on duplicates
241 fn process_modifiers(query
: &mut Query
) -> QueryModifiers
{
242 let mut load_cached
= None
;
243 let mut storage
= None
;
244 let mut cache
= None
;
246 let mut fatal_cycle
= false;
247 let mut cycle_delay_bug
= false;
248 let mut no_hash
= false;
249 let mut anon
= false;
250 let mut eval_always
= false;
251 for modifier
in query
.modifiers
.0.drain(..) {
253 QueryModifier
::LoadCached(tcx
, id
, block
) => {
254 if load_cached
.is_some() {
255 panic
!("duplicate modifier `load_cached` for query `{}`", query
.name
);
257 load_cached
= Some((tcx
, id
, block
));
259 QueryModifier
::Storage(ty
) => {
260 if storage
.is_some() {
261 panic
!("duplicate modifier `storage` for query `{}`", query
.name
);
265 QueryModifier
::Cache(args
, expr
) => {
267 panic
!("duplicate modifier `cache` for query `{}`", query
.name
);
269 cache
= Some((args
, expr
));
271 QueryModifier
::Desc(tcx
, list
) => {
273 panic
!("duplicate modifier `desc` for query `{}`", query
.name
);
275 desc
= Some((tcx
, list
));
277 QueryModifier
::FatalCycle
=> {
279 panic
!("duplicate modifier `fatal_cycle` for query `{}`", query
.name
);
283 QueryModifier
::CycleDelayBug
=> {
285 panic
!("duplicate modifier `cycle_delay_bug` for query `{}`", query
.name
);
287 cycle_delay_bug
= true;
289 QueryModifier
::NoHash
=> {
291 panic
!("duplicate modifier `no_hash` for query `{}`", query
.name
);
295 QueryModifier
::Anon
=> {
297 panic
!("duplicate modifier `anon` for query `{}`", query
.name
);
301 QueryModifier
::EvalAlways
=> {
303 panic
!("duplicate modifier `eval_always` for query `{}`", query
.name
);
309 let desc
= desc
.unwrap_or_else(|| {
310 panic
!("no description provided 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
= if let Some((args
, expr
)) = modifiers
.cache
.as_ref() {
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
.as_ref().and_then(|c
| c
.try_load_query_result(tcx
, id
))
367 .unwrap_or(quote
! { _ }
);
374 .unwrap_or(quote
! { _ }
);
375 // expr is a `Block`, meaning that `{ #expr }` gets expanded
376 // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
379 #[allow(unused_variables, unused_braces)]
383 #value: Option<&Self::Value>
391 if modifiers
.load_cached
.is_some() {
392 panic
!("load_cached modifier on query `{}` without a cache modifier", name
);
397 let (tcx
, desc
) = modifiers
.desc
;
398 let tcx
= tcx
.as_ref().map(|t
| quote
! { #t }
).unwrap_or(quote
! { _ }
);
401 #[allow(unused_variables)]
405 ) -> Cow
<'
static, str> {
406 ::rustc_middle
::ty
::print
::with_no_trimmed_paths(|| format
!(#desc).into())
410 impls
.extend(quote
! {
411 impl<'tcx
> QueryDescription
<TyCtxt
<'tcx
>> for queries
::#name<'tcx> {
418 pub fn rustc_queries(input
: TokenStream
) -> TokenStream
{
419 let groups
= parse_macro_input
!(input
as List
<Group
>);
421 let mut query_stream
= quote
! {}
;
422 let mut query_description_stream
= quote
! {}
;
423 let mut dep_node_def_stream
= quote
! {}
;
424 let mut cached_queries
= quote
! {}
;
426 for group
in groups
.0 {
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
! {
443 let mut attributes
= Vec
::new();
445 // Pass on the fatal_cycle modifier
446 if modifiers
.fatal_cycle
{
447 attributes
.push(quote
! { fatal_cycle }
);
449 // Pass on the storage modifier
450 if let Some(ref ty
) = modifiers
.storage
{
451 attributes
.push(quote
! { storage(#ty) }
);
453 // Pass on the cycle_delay_bug modifier
454 if modifiers
.cycle_delay_bug
{
455 attributes
.push(quote
! { cycle_delay_bug }
);
457 // Pass on the no_hash modifier
458 if modifiers
.no_hash
{
459 attributes
.push(quote
! { no_hash }
);
461 // Pass on the anon modifier
463 attributes
.push(quote
! { anon }
);
465 // Pass on the eval_always modifier
466 if modifiers
.eval_always
{
467 attributes
.push(quote
! { eval_always }
);
470 let attribute_stream
= quote
! {#(#attributes),*}
;
471 let doc_comments
= query
.doc_comments
.iter();
472 // Add the query to the group
473 query_stream
.extend(quote
! {
475 [#attribute_stream] fn #name(#arg) #result,
478 // Create a dep node for the query
479 dep_node_def_stream
.extend(quote
! {
480 [#attribute_stream] #name(#arg),
483 add_query_description_impl(&query
, modifiers
, &mut query_description_stream
);
487 TokenStream
::from(quote
! {
488 macro_rules
! rustc_query_append
{
489 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
498 macro_rules
! rustc_dep_node_append
{
499 ([$
($
macro:tt
)*][$
($other
:tt
)*]) => {
507 macro_rules
! rustc_cached_queries
{
509 $
($
macro)*(#cached_queries);
513 #query_description_stream