1
1
use proc_macro:: TokenStream ;
2
2
use proc_macro2:: Span ;
3
3
use syn:: {
4
- Token , Ident , Type , Attribute , ReturnType , Expr , Block ,
4
+ Token , Ident , Type , Attribute , ReturnType , Expr , Block , Error ,
5
5
braced, parenthesized, parse_macro_input,
6
6
} ;
7
+ use syn:: spanned:: Spanned ;
7
8
use syn:: parse:: { Result , Parse , ParseStream } ;
8
9
use syn:: punctuated:: Punctuated ;
10
+ use syn;
9
11
use quote:: quote;
10
- use crate :: tt:: TS ;
11
12
13
+ #[ allow( non_camel_case_types) ]
14
+ mod kw {
15
+ syn:: custom_keyword!( query) ;
16
+ }
17
+
18
+ /// Ident or a wildcard `_`.
12
19
struct IdentOrWild ( Ident ) ;
13
20
14
21
impl Parse for IdentOrWild {
@@ -22,17 +29,27 @@ impl Parse for IdentOrWild {
22
29
}
23
30
}
24
31
25
- enum QueryAttribute {
32
+ /// A modifier for a query
33
+ enum QueryModifier {
34
+ /// The description of the query
26
35
Desc ( Option < Ident > , Punctuated < Expr , Token ! [ , ] > ) ,
36
+
37
+ /// Cache the query to disk if the `Expr` returns true.
27
38
Cache ( Option < Ident > , Expr ) ,
39
+
40
+ /// Custom code to load the query from disk.
28
41
LoadCached ( Ident , Ident , Block ) ,
42
+
43
+ /// A cycle error for this query aborting the compilation with a fatal error.
29
44
FatalCycle ,
30
45
}
31
46
32
- impl Parse for QueryAttribute {
47
+ impl Parse for QueryModifier {
33
48
fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
34
- let attr: Ident = input. parse ( ) ?;
35
- if attr == "desc" {
49
+ let modifier: Ident = input. parse ( ) ?;
50
+ if modifier == "desc" {
51
+ // Parse a description modifier like:
52
+ // `desc { |tcx| "foo {}", tcx.item_path(key) }`
36
53
let attr_content;
37
54
braced ! ( attr_content in input) ;
38
55
let tcx = if attr_content. peek ( Token ! [ |] ) {
@@ -44,11 +61,10 @@ impl Parse for QueryAttribute {
44
61
None
45
62
} ;
46
63
let desc = attr_content. parse_terminated ( Expr :: parse) ?;
47
- if !attr_content. is_empty ( ) {
48
- panic ! ( "unexpected tokens in block" ) ;
49
- } ;
50
- Ok ( QueryAttribute :: Desc ( tcx, desc) )
51
- } else if attr == "cache" {
64
+ Ok ( QueryModifier :: Desc ( tcx, desc) )
65
+ } else if modifier == "cache" {
66
+ // Parse a cache modifier like:
67
+ // `cache { |tcx| key.is_local() }`
52
68
let attr_content;
53
69
braced ! ( attr_content in input) ;
54
70
let tcx = if attr_content. peek ( Token ! [ |] ) {
@@ -60,68 +76,59 @@ impl Parse for QueryAttribute {
60
76
None
61
77
} ;
62
78
let expr = attr_content. parse ( ) ?;
63
- if !attr_content. is_empty ( ) {
64
- panic ! ( "unexpected tokens in block" ) ;
65
- } ;
66
- Ok ( QueryAttribute :: Cache ( tcx, expr) )
67
- } else if attr == "load_cached" {
79
+ Ok ( QueryModifier :: Cache ( tcx, expr) )
80
+ } else if modifier == "load_cached" {
81
+ // Parse a load_cached modifier like:
82
+ // `load_cached(tcx, id) { tcx.queries.on_disk_cache.try_load_query_result(tcx, id) }`
68
83
let args;
69
84
parenthesized ! ( args in input) ;
70
85
let tcx = args. parse ( ) ?;
71
86
args. parse :: < Token ! [ , ] > ( ) ?;
72
87
let id = args. parse ( ) ?;
73
- if !args. is_empty ( ) {
74
- panic ! ( "unexpected tokens in arguments" ) ;
75
- } ;
76
88
let block = input. parse ( ) ?;
77
- Ok ( QueryAttribute :: LoadCached ( tcx, id, block) )
78
- } else if attr == "fatal_cycle" {
79
- Ok ( QueryAttribute :: FatalCycle )
89
+ Ok ( QueryModifier :: LoadCached ( tcx, id, block) )
90
+ } else if modifier == "fatal_cycle" {
91
+ Ok ( QueryModifier :: FatalCycle )
80
92
} else {
81
- panic ! ( "unknown query modifier {}" , attr )
93
+ Err ( Error :: new ( modifier . span ( ) , "unknown query modifier" ) )
82
94
}
83
95
}
84
96
}
85
97
98
+ /// Ensures only doc comment attributes are used
99
+ fn check_attributes ( attrs : Vec < Attribute > ) -> Result < ( ) > {
100
+ for attr in attrs {
101
+ if !attr. path . is_ident ( "doc" ) {
102
+ return Err ( Error :: new ( attr. span ( ) , "attributes not supported on queries" ) ) ;
103
+ }
104
+ }
105
+ Ok ( ( ) )
106
+ }
107
+
108
+ /// A compiler query. `query ... { ... }`
86
109
struct Query {
87
- attrs : List < QueryAttribute > ,
110
+ attrs : List < QueryModifier > ,
88
111
name : Ident ,
89
112
key : IdentOrWild ,
90
113
arg : Type ,
91
114
result : ReturnType ,
92
115
}
93
116
94
- fn check_attributes ( attrs : Vec < Attribute > ) {
95
- for attr in attrs {
96
- let path = attr. path ;
97
- let path = quote ! { #path } ;
98
- let path = TS ( & path) ;
99
-
100
- if path != TS ( & quote ! { doc } ) {
101
- panic ! ( "attribute `{}` not supported on queries" , path. 0 )
102
- }
103
- }
104
- }
105
-
106
117
impl Parse for Query {
107
118
fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
108
- check_attributes ( input. call ( Attribute :: parse_outer) ?) ;
119
+ check_attributes ( input. call ( Attribute :: parse_outer) ?) ? ;
109
120
110
- let query: Ident = input. parse ( ) ?;
111
- if query != "query" {
112
- panic ! ( "expected `query`" ) ;
113
- }
121
+ // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
122
+ input. parse :: < kw:: query > ( ) ?;
114
123
let name: Ident = input. parse ( ) ?;
115
124
let arg_content;
116
125
parenthesized ! ( arg_content in input) ;
117
126
let key = arg_content. parse ( ) ?;
118
127
arg_content. parse :: < Token ! [ : ] > ( ) ?;
119
128
let arg = arg_content. parse ( ) ?;
120
- if !arg_content. is_empty ( ) {
121
- panic ! ( "expected only one query argument" ) ;
122
- } ;
123
129
let result = input. parse ( ) ?;
124
130
131
+ // Parse the query modifiers
125
132
let content;
126
133
braced ! ( content in input) ;
127
134
let attrs = content. parse ( ) ?;
@@ -136,6 +143,7 @@ impl Parse for Query {
136
143
}
137
144
}
138
145
146
+ /// A type used to greedily parse another type until the input is empty.
139
147
struct List < T > ( Vec < T > ) ;
140
148
141
149
impl < T : Parse > Parse for List < T > {
@@ -148,6 +156,7 @@ impl<T: Parse> Parse for List<T> {
148
156
}
149
157
}
150
158
159
+ /// A named group containing queries.
151
160
struct Group {
152
161
name : Ident ,
153
162
queries : List < Query > ,
@@ -165,6 +174,88 @@ impl Parse for Group {
165
174
}
166
175
}
167
176
177
+ /// Add the impl of QueryDescription for the query to `impls` if one is requested
178
+ fn add_query_description_impl ( query : & Query , impls : & mut proc_macro2:: TokenStream ) {
179
+ let name = & query. name ;
180
+ let arg = & query. arg ;
181
+ let key = & query. key . 0 ;
182
+
183
+ // Find custom code to load the query from disk
184
+ let load_cached = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
185
+ QueryModifier :: LoadCached ( tcx, id, block) => Some ( ( tcx, id, block) ) ,
186
+ _ => None ,
187
+ } ) ;
188
+
189
+ // Find out if we should cache the query on disk
190
+ let cache = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
191
+ QueryModifier :: Cache ( tcx, expr) => Some ( ( tcx, expr) ) ,
192
+ _ => None ,
193
+ } ) . map ( |( tcx, expr) | {
194
+ let try_load_from_disk = if let Some ( ( tcx, id, block) ) = load_cached {
195
+ quote ! {
196
+ #[ inline]
197
+ fn try_load_from_disk(
198
+ #tcx: TyCtxt <' _, ' tcx, ' tcx>,
199
+ #id: SerializedDepNodeIndex
200
+ ) -> Option <Self :: Value > {
201
+ #block
202
+ }
203
+ }
204
+ } else {
205
+ quote ! {
206
+ #[ inline]
207
+ fn try_load_from_disk(
208
+ tcx: TyCtxt <' _, ' tcx, ' tcx>,
209
+ id: SerializedDepNodeIndex
210
+ ) -> Option <Self :: Value > {
211
+ tcx. queries. on_disk_cache. try_load_query_result( tcx, id)
212
+ }
213
+ }
214
+ } ;
215
+
216
+ let tcx = tcx. as_ref ( ) . map ( |t| quote ! { #t } ) . unwrap_or ( quote ! { _ } ) ;
217
+ quote ! {
218
+ #[ inline]
219
+ fn cache_on_disk( #tcx: TyCtxt <' _, ' tcx, ' tcx>, #key: Self :: Key ) -> bool {
220
+ #expr
221
+ }
222
+
223
+ #try_load_from_disk
224
+ }
225
+ } ) ;
226
+
227
+ if cache. is_none ( ) && load_cached. is_some ( ) {
228
+ panic ! ( "load_cached modifier on query `{}` without a cache modifier" , name) ;
229
+ }
230
+
231
+ let desc = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
232
+ QueryModifier :: Desc ( tcx, desc) => Some ( ( tcx, desc) ) ,
233
+ _ => None ,
234
+ } ) . map ( |( tcx, desc) | {
235
+ let tcx = tcx. as_ref ( ) . map ( |t| quote ! { #t } ) . unwrap_or ( quote ! { _ } ) ;
236
+ quote ! {
237
+ fn describe(
238
+ #tcx: TyCtxt <' _, ' _, ' _>,
239
+ #key: #arg,
240
+ ) -> Cow <' static , str > {
241
+ format!( #desc) . into( )
242
+ }
243
+ }
244
+ } ) ;
245
+
246
+ if desc. is_some ( ) || cache. is_some ( ) {
247
+ let cache = cache. unwrap_or ( quote ! { } ) ;
248
+ let desc = desc. unwrap_or ( quote ! { } ) ;
249
+
250
+ impls. extend ( quote ! {
251
+ impl <' tcx> QueryDescription <' tcx> for queries:: #name<' tcx> {
252
+ #desc
253
+ #cache
254
+ }
255
+ } ) ;
256
+ }
257
+ }
258
+
168
259
pub fn rustc_queries ( input : TokenStream ) -> TokenStream {
169
260
let groups = parse_macro_input ! ( input as List <Group >) ;
170
261
@@ -178,99 +269,31 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
178
269
for query in & group. queries . 0 {
179
270
let name = & query. name ;
180
271
let arg = & query. arg ;
181
- let key = & query. key . 0 ;
182
272
let result_full = & query. result ;
183
273
let result = match query. result {
184
274
ReturnType :: Default => quote ! { -> ( ) } ,
185
275
_ => quote ! { #result_full } ,
186
276
} ;
187
277
188
- let load_cached = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
189
- QueryAttribute :: LoadCached ( tcx, id, block) => Some ( ( tcx, id, block) ) ,
190
- _ => None ,
191
- } ) ;
192
-
193
- // Find out if we should cache the query on disk
194
- let cache = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
195
- QueryAttribute :: Cache ( tcx, expr) => Some ( ( tcx, expr) ) ,
196
- _ => None ,
197
- } ) . map ( |( tcx, expr) | {
198
- let try_load_from_disk = if let Some ( ( tcx, id, block) ) = load_cached {
199
- quote ! {
200
- #[ inline]
201
- fn try_load_from_disk(
202
- #tcx: TyCtxt <' _, ' tcx, ' tcx>,
203
- #id: SerializedDepNodeIndex
204
- ) -> Option <Self :: Value > {
205
- #block
206
- }
207
- }
208
- } else {
209
- quote ! {
210
- #[ inline]
211
- fn try_load_from_disk(
212
- tcx: TyCtxt <' _, ' tcx, ' tcx>,
213
- id: SerializedDepNodeIndex
214
- ) -> Option <Self :: Value > {
215
- tcx. queries. on_disk_cache. try_load_query_result( tcx, id)
216
- }
217
- }
218
- } ;
219
-
220
- let tcx = tcx. as_ref ( ) . map ( |t| quote ! { #t } ) . unwrap_or ( quote ! { _ } ) ;
221
- quote ! {
222
- #[ inline]
223
- fn cache_on_disk( #tcx: TyCtxt <' _, ' tcx, ' tcx>, #key: Self :: Key ) -> bool {
224
- #expr
225
- }
226
-
227
- #try_load_from_disk
228
- }
229
- } ) ;
230
-
231
- if cache. is_none ( ) && load_cached. is_some ( ) {
232
- panic ! ( "load_cached modifier on query `{}` without a cache modifier" , name) ;
233
- }
234
-
278
+ // Look for a fatal_cycle modifier to pass on
235
279
let fatal_cycle = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
236
- QueryAttribute :: FatalCycle => Some ( ( ) ) ,
280
+ QueryModifier :: FatalCycle => Some ( ( ) ) ,
237
281
_ => None ,
238
282
} ) . map ( |_| quote ! { fatal_cycle } ) . unwrap_or ( quote ! { } ) ;
239
283
284
+ // Add the query to the group
240
285
group_stream. extend ( quote ! {
241
286
[ #fatal_cycle] fn #name: #name( #arg) #result,
242
287
} ) ;
243
288
244
- let desc = query. attrs . 0 . iter ( ) . find_map ( |attr| match attr {
245
- QueryAttribute :: Desc ( tcx, desc) => Some ( ( tcx, desc) ) ,
246
- _ => None ,
247
- } ) . map ( |( tcx, desc) | {
248
- let tcx = tcx. as_ref ( ) . map ( |t| quote ! { #t } ) . unwrap_or ( quote ! { _ } ) ;
249
- quote ! {
250
- fn describe(
251
- #tcx: TyCtxt <' _, ' _, ' _>,
252
- #key: #arg,
253
- ) -> Cow <' static , str > {
254
- format!( #desc) . into( )
255
- }
256
- }
257
- } ) ;
258
-
259
- if desc. is_some ( ) || cache. is_some ( ) {
260
- let cache = cache. unwrap_or ( quote ! { } ) ;
261
- let desc = desc. unwrap_or ( quote ! { } ) ;
262
-
263
- query_description_stream. extend ( quote ! {
264
- impl <' tcx> QueryDescription <' tcx> for queries:: #name<' tcx> {
265
- #desc
266
- #cache
267
- }
268
- } ) ;
269
- }
289
+ add_query_description_impl ( query, & mut query_description_stream) ;
270
290
291
+ // Create a dep node for the query
271
292
dep_node_def_stream. extend ( quote ! {
272
293
[ ] #name( #arg) ,
273
294
} ) ;
295
+
296
+ // Add a match arm to force the query given the dep node
274
297
dep_node_force_stream. extend ( quote ! {
275
298
DepKind :: #name => {
276
299
if let Some ( key) = RecoverKey :: recover( $tcx, $dep_node) {
0 commit comments