1
1
use rustc_ast:: tokenstream:: TokenStream ;
2
+ use rustc_data_structures:: svh:: Svh ;
2
3
use rustc_errors:: ErrorGuaranteed ;
4
+ use rustc_middle:: ty:: { self , TyCtxt } ;
3
5
use rustc_parse:: parser:: { ForceCollect , Parser } ;
6
+ use rustc_session:: Session ;
4
7
use rustc_session:: config:: ProcMacroExecutionStrategy ;
5
- use rustc_span:: Span ;
6
8
use rustc_span:: profiling:: SpannedEventArgRecorder ;
9
+ use rustc_span:: { LocalExpnId , Span } ;
7
10
use { rustc_ast as ast, rustc_proc_macro as pm} ;
8
11
9
12
use crate :: base:: { self , * } ;
@@ -30,9 +33,9 @@ impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
30
33
}
31
34
}
32
35
33
- fn exec_strategy ( ecx : & ExtCtxt < ' _ > ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
36
+ fn exec_strategy ( sess : & Session ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
34
37
pm:: bridge:: server:: MaybeCrossThread :: < MessagePipe < _ > > :: new (
35
- ecx . sess . opts . unstable_opts . proc_macro_execution_strategy
38
+ sess. opts . unstable_opts . proc_macro_execution_strategy
36
39
== ProcMacroExecutionStrategy :: CrossThread ,
37
40
)
38
41
}
@@ -54,7 +57,7 @@ impl base::BangProcMacro for BangProcMacro {
54
57
} ) ;
55
58
56
59
let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
57
- let strategy = exec_strategy ( ecx) ;
60
+ let strategy = exec_strategy ( ecx. sess ) ;
58
61
let server = proc_macro_server:: Rustc :: new ( ecx) ;
59
62
self . client . run ( & strategy, server, input, proc_macro_backtrace) . map_err ( |e| {
60
63
ecx. dcx ( ) . emit_err ( errors:: ProcMacroPanicked {
@@ -85,7 +88,7 @@ impl base::AttrProcMacro for AttrProcMacro {
85
88
} ) ;
86
89
87
90
let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
88
- let strategy = exec_strategy ( ecx) ;
91
+ let strategy = exec_strategy ( ecx. sess ) ;
89
92
let server = proc_macro_server:: Rustc :: new ( ecx) ;
90
93
self . client . run ( & strategy, server, annotation, annotated, proc_macro_backtrace) . map_err (
91
94
|e| {
@@ -101,7 +104,7 @@ impl base::AttrProcMacro for AttrProcMacro {
101
104
}
102
105
103
106
pub struct DeriveProcMacro {
104
- pub client : pm :: bridge :: client :: Client < pm :: TokenStream , pm :: TokenStream > ,
107
+ pub client : DeriveClient ,
105
108
}
106
109
107
110
impl MultiItemModifier for DeriveProcMacro {
@@ -113,6 +116,13 @@ impl MultiItemModifier for DeriveProcMacro {
113
116
item : Annotatable ,
114
117
_is_derive_const : bool ,
115
118
) -> ExpandResult < Vec < Annotatable > , Annotatable > {
119
+ let _timer = ecx. sess . prof . generic_activity_with_arg_recorder (
120
+ "expand_derive_proc_macro_outer" ,
121
+ |recorder| {
122
+ recorder. record_arg_with_span ( ecx. sess . source_map ( ) , ecx. expansion_descr ( ) , span) ;
123
+ } ,
124
+ ) ;
125
+
116
126
// We need special handling for statement items
117
127
// (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
118
128
let is_stmt = matches ! ( item, Annotatable :: Stmt ( ..) ) ;
@@ -123,36 +133,39 @@ impl MultiItemModifier for DeriveProcMacro {
123
133
// altogether. See #73345.
124
134
crate :: base:: ann_pretty_printing_compatibility_hack ( & item, & ecx. sess . psess ) ;
125
135
let input = item. to_tokens ( ) ;
126
- let stream = {
127
- let _timer =
128
- ecx. sess . prof . generic_activity_with_arg_recorder ( "expand_proc_macro" , |recorder| {
129
- recorder. record_arg_with_span (
130
- ecx. sess . source_map ( ) ,
131
- ecx. expansion_descr ( ) ,
132
- span,
133
- ) ;
134
- } ) ;
135
- let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
136
- let strategy = exec_strategy ( ecx) ;
137
- let server = proc_macro_server:: Rustc :: new ( ecx) ;
138
- match self . client . run ( & strategy, server, input, proc_macro_backtrace) {
139
- Ok ( stream) => stream,
140
- Err ( e) => {
141
- ecx. dcx ( ) . emit_err ( {
142
- errors:: ProcMacroDerivePanicked {
143
- span,
144
- message : e. as_str ( ) . map ( |message| {
145
- errors:: ProcMacroDerivePanickedHelp { message : message. into ( ) }
146
- } ) ,
147
- }
148
- } ) ;
149
- return ExpandResult :: Ready ( vec ! [ ] ) ;
150
- }
151
- }
136
+
137
+ let invoc_id = ecx. current_expansion . id ;
138
+
139
+ let res = if ecx. sess . opts . incremental . is_some ( )
140
+ && ecx. sess . opts . unstable_opts . cache_derive_macros
141
+ {
142
+ ty:: tls:: with ( |tcx| {
143
+ // FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has
144
+ // changed. How to *correctly* depend on exactly the macro definition?
145
+ // I.e., depending on the crate hash is just a HACK, and ideally the dependency would be
146
+ // more narrow.
147
+ let invoc_expn_data = invoc_id. expn_data ( ) ;
148
+ let macro_def_id = invoc_expn_data. macro_def_id . unwrap ( ) ;
149
+ let proc_macro_crate_hash = tcx. crate_hash ( macro_def_id. krate ) ;
150
+
151
+ let input = tcx. arena . alloc ( input) as & TokenStream ;
152
+ let key = ( invoc_id, proc_macro_crate_hash, input) ;
153
+
154
+ QueryDeriveExpandCtx :: enter ( ecx, self . client , move || {
155
+ tcx. derive_macro_expansion ( key) . cloned ( )
156
+ } )
157
+ } )
158
+ } else {
159
+ expand_derive_macro ( invoc_id, input, ecx, self . client )
160
+ } ;
161
+
162
+ let Ok ( output) = res else {
163
+ // error will already have been emitted
164
+ return ExpandResult :: Ready ( vec ! [ ] ) ;
152
165
} ;
153
166
154
167
let error_count_before = ecx. dcx ( ) . err_count ( ) ;
155
- let mut parser = Parser :: new ( & ecx. sess . psess , stream , Some ( "proc-macro derive" ) ) ;
168
+ let mut parser = Parser :: new ( & ecx. sess . psess , output , Some ( "proc-macro derive" ) ) ;
156
169
let mut items = vec ! [ ] ;
157
170
158
171
loop {
@@ -180,3 +193,99 @@ impl MultiItemModifier for DeriveProcMacro {
180
193
ExpandResult :: Ready ( items)
181
194
}
182
195
}
196
+
197
+ /// Provide a query for computing the output of a derive macro.
198
+ pub ( super ) fn provide_derive_macro_expansion < ' tcx > (
199
+ tcx : TyCtxt < ' tcx > ,
200
+ key : ( LocalExpnId , Svh , & ' tcx TokenStream ) ,
201
+ ) -> Result < & ' tcx TokenStream , ( ) > {
202
+ let ( invoc_id, _macro_crate_hash, input) = key;
203
+
204
+ eprintln ! ( "Expanding derive macro in a query" ) ;
205
+
206
+ QueryDeriveExpandCtx :: with ( |ecx, client| {
207
+ expand_derive_macro ( invoc_id, input. clone ( ) , ecx, client)
208
+ . map ( |ts| tcx. arena . alloc ( ts) as & TokenStream )
209
+ } )
210
+ }
211
+
212
+ type DeriveClient = pm:: bridge:: client:: Client < pm:: TokenStream , pm:: TokenStream > ;
213
+
214
+ fn expand_derive_macro (
215
+ invoc_id : LocalExpnId ,
216
+ input : TokenStream ,
217
+ ecx : & mut ExtCtxt < ' _ > ,
218
+ client : DeriveClient ,
219
+ ) -> Result < TokenStream , ( ) > {
220
+ let invoc_expn_data = invoc_id. expn_data ( ) ;
221
+ let span = invoc_expn_data. call_site ;
222
+ let event_arg = invoc_expn_data. kind . descr ( ) ;
223
+ let _timer =
224
+ ecx. sess . prof . generic_activity_with_arg_recorder ( "expand_proc_macro" , |recorder| {
225
+ recorder. record_arg_with_span ( ecx. sess . source_map ( ) , event_arg. clone ( ) , span) ;
226
+ } ) ;
227
+
228
+ let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
229
+ let strategy = exec_strategy ( ecx. sess ) ;
230
+ let server = proc_macro_server:: Rustc :: new ( ecx) ;
231
+
232
+ match client. run ( & strategy, server, input, proc_macro_backtrace) {
233
+ Ok ( stream) => Ok ( stream) ,
234
+ Err ( e) => {
235
+ ecx. dcx ( ) . emit_err ( {
236
+ errors:: ProcMacroDerivePanicked {
237
+ span,
238
+ message : e. as_str ( ) . map ( |message| errors:: ProcMacroDerivePanickedHelp {
239
+ message : message. into ( ) ,
240
+ } ) ,
241
+ }
242
+ } ) ;
243
+ Err ( ( ) )
244
+ }
245
+ }
246
+ }
247
+
248
+ /// Stores the context necessary to expand a derive proc macro via a query.
249
+ struct QueryDeriveExpandCtx {
250
+ /// Type-erased version of `&mut ExtCtxt`
251
+ expansion_ctx : * mut ( ) ,
252
+ client : DeriveClient ,
253
+ }
254
+
255
+ impl QueryDeriveExpandCtx {
256
+ /// Store the extension context and the client into the thread local value.
257
+ /// It will be accessible via the `with` method while `f` is active.
258
+ fn enter < F , R > ( ecx : & mut ExtCtxt < ' _ > , client : DeriveClient , f : F ) -> R
259
+ where
260
+ F : FnOnce ( ) -> R ,
261
+ {
262
+ // We need erasure to get rid of the lifetime
263
+ let ctx = Self { expansion_ctx : ecx as * mut _ as * mut ( ) , client } ;
264
+ DERIVE_EXPAND_CTX . set ( & ctx, || f ( ) )
265
+ }
266
+
267
+ /// Accesses the thread local value of the derive expansion context.
268
+ /// Must be called while the `enter` function is active.
269
+ fn with < F , R > ( f : F ) -> R
270
+ where
271
+ F : for <' a , ' b > FnOnce ( & ' b mut ExtCtxt < ' a > , DeriveClient ) -> R ,
272
+ {
273
+ DERIVE_EXPAND_CTX . with ( |ctx| {
274
+ let ectx = {
275
+ let casted = ctx. expansion_ctx . cast :: < ExtCtxt < ' _ > > ( ) ;
276
+ // SAFETY: We can only get the value from `with` while the `enter` function
277
+ // is active (on the callstack), and that function's signature ensures that the
278
+ // lifetime is valid.
279
+ // If `with` is called at some other time, it will panic due to usage of
280
+ // `scoped_tls::with`.
281
+ unsafe { casted. as_mut ( ) . unwrap ( ) }
282
+ } ;
283
+
284
+ f ( ectx, ctx. client )
285
+ } )
286
+ }
287
+ }
288
+
289
+ // When we invoke a query to expand a derive proc macro, we need to provide it with the expansion
290
+ // context and derive Client. We do that using a thread-local.
291
+ scoped_tls:: scoped_thread_local!( static DERIVE_EXPAND_CTX : QueryDeriveExpandCtx ) ;
0 commit comments