1
- use std:: cell:: Cell ;
2
- use std:: ptr:: NonNull ;
3
-
4
1
use rustc_ast:: tokenstream:: TokenStream ;
5
2
use rustc_data_structures:: svh:: Svh ;
6
3
use rustc_errors:: ErrorGuaranteed ;
@@ -36,7 +33,7 @@ impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
36
33
}
37
34
}
38
35
39
- pub fn exec_strategy ( sess : & Session ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
36
+ fn exec_strategy ( sess : & Session ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
40
37
pm:: bridge:: server:: MaybeCrossThread :: < MessagePipe < _ > > :: new (
41
38
sess. opts . unstable_opts . proc_macro_execution_strategy
42
39
== ProcMacroExecutionStrategy :: CrossThread ,
@@ -107,7 +104,7 @@ impl base::AttrProcMacro for AttrProcMacro {
107
104
}
108
105
109
106
pub struct DeriveProcMacro {
110
- pub client : pm :: bridge :: client :: Client < pm :: TokenStream , pm :: TokenStream > ,
107
+ pub client : DeriveClient ,
111
108
}
112
109
113
110
impl MultiItemModifier for DeriveProcMacro {
@@ -136,32 +133,31 @@ impl MultiItemModifier for DeriveProcMacro {
136
133
// altogether. See #73345.
137
134
crate :: base:: ann_pretty_printing_compatibility_hack ( & item, & ecx. sess . psess ) ;
138
135
let input = item. to_tokens ( ) ;
139
- let res = ty:: tls:: with ( |tcx| {
140
- let input = tcx. arena . alloc ( input) as & TokenStream ;
141
- let invoc_id = ecx. current_expansion . id ;
142
- let invoc_expn_data = invoc_id. expn_data ( ) ;
143
-
144
- assert_eq ! ( invoc_expn_data. call_site, span) ;
145
-
146
- // FIXME(pr-time): Is this the correct way to check for incremental compilation (as
147
- // well as for `cache_proc_macros`)?
148
- if tcx. sess . opts . incremental . is_some ( )
149
- && tcx. sess . opts . unstable_opts . cache_derive_macros
150
- {
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| {
151
143
// FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has
152
144
// changed. How to *correctly* depend on exactly the macro definition?
153
145
// I.e., depending on the crate hash is just a HACK, and ideally the dependency would be
154
146
// more narrow.
147
+ let invoc_expn_data = invoc_id. expn_data ( ) ;
155
148
let macro_def_id = invoc_expn_data. macro_def_id . unwrap ( ) ;
156
149
let proc_macro_crate_hash = tcx. crate_hash ( macro_def_id. krate ) ;
157
150
151
+ let input = tcx. arena . alloc ( input) as & TokenStream ;
158
152
let key = ( invoc_id, proc_macro_crate_hash, input) ;
159
153
160
- enter_context ( ( ecx, self . client ) , move || tcx. derive_macro_expansion ( key) . cloned ( ) )
161
- } else {
162
- expand_derive_macro ( tcx, invoc_id, input, ecx, self . client ) . cloned ( )
163
- }
164
- } ) ;
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
+ } ;
165
161
166
162
let Ok ( output) = res else {
167
163
// error will already have been emitted
@@ -205,36 +201,36 @@ pub(super) fn provide_derive_macro_expansion<'tcx>(
205
201
) -> Result < & ' tcx TokenStream , ( ) > {
206
202
let ( invoc_id, _macro_crate_hash, input) = key;
207
203
208
- with_context ( |( ecx, client) | expand_derive_macro ( tcx, invoc_id, input, ecx, * client) )
204
+ QueryDeriveExpandCtx :: with ( |ecx, client| {
205
+ expand_derive_macro ( invoc_id, input. clone ( ) , ecx, client)
206
+ . map ( |ts| tcx. arena . alloc ( ts) as & TokenStream )
207
+ } )
209
208
}
210
209
211
- type CLIENT = pm:: bridge:: client:: Client < pm:: TokenStream , pm:: TokenStream > ;
210
+ type DeriveClient = pm:: bridge:: client:: Client < pm:: TokenStream , pm:: TokenStream > ;
212
211
213
- fn expand_derive_macro < ' tcx > (
214
- tcx : TyCtxt < ' tcx > ,
212
+ fn expand_derive_macro (
215
213
invoc_id : LocalExpnId ,
216
- input : & ' tcx TokenStream ,
214
+ input : TokenStream ,
217
215
ecx : & mut ExtCtxt < ' _ > ,
218
- client : CLIENT ,
219
- ) -> Result < & ' tcx TokenStream , ( ) > {
216
+ client : DeriveClient ,
217
+ ) -> Result < TokenStream , ( ) > {
220
218
let invoc_expn_data = invoc_id. expn_data ( ) ;
221
219
let span = invoc_expn_data. call_site ;
222
220
let event_arg = invoc_expn_data. kind . descr ( ) ;
223
- let _timer = tcx. sess . prof . generic_activity_with_arg_recorder (
224
- "expand_derive_proc_macro_inner" ,
225
- |recorder| {
226
- recorder. record_arg_with_span ( tcx. sess . source_map ( ) , event_arg. clone ( ) , span) ;
227
- } ,
228
- ) ;
221
+ let _timer =
222
+ ecx. sess . prof . generic_activity_with_arg_recorder ( "expand_proc_macro" , |recorder| {
223
+ recorder. record_arg_with_span ( ecx. sess . source_map ( ) , event_arg. clone ( ) , span) ;
224
+ } ) ;
229
225
230
226
let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
231
- let strategy = crate :: proc_macro :: exec_strategy ( tcx . sess ) ;
232
- let server = crate :: proc_macro_server:: Rustc :: new ( ecx) ;
227
+ let strategy = exec_strategy ( ecx . sess ) ;
228
+ let server = proc_macro_server:: Rustc :: new ( ecx) ;
233
229
234
- match client. run ( & strategy, server, input. clone ( ) , proc_macro_backtrace) {
235
- Ok ( stream) => Ok ( tcx . arena . alloc ( stream) as & TokenStream ) ,
230
+ match client. run ( & strategy, server, input, proc_macro_backtrace) {
231
+ Ok ( stream) => Ok ( stream) ,
236
232
Err ( e) => {
237
- tcx . dcx ( ) . emit_err ( {
233
+ ecx . dcx ( ) . emit_err ( {
238
234
errors:: ProcMacroDerivePanicked {
239
235
span,
240
236
message : e. as_str ( ) . map ( |message| errors:: ProcMacroDerivePanickedHelp {
@@ -247,53 +243,47 @@ fn expand_derive_macro<'tcx>(
247
243
}
248
244
}
249
245
250
- // based on rust/compiler/rustc_middle/src/ty/context/tls.rs
251
- thread_local ! {
252
- /// A thread local variable that stores a pointer to the current `CONTEXT`.
253
- static TLV : Cell <( * mut ( ) , Option <CLIENT >) > = const { Cell :: new( ( std:: ptr:: null_mut( ) , None ) ) } ;
254
- }
255
-
256
- /// Sets `context` as the new current `CONTEXT` for the duration of the function `f`.
257
- #[ inline]
258
- pub ( crate ) fn enter_context < ' a , F , R > ( context : ( & mut ExtCtxt < ' a > , CLIENT ) , f : F ) -> R
259
- where
260
- F : FnOnce ( ) -> R ,
261
- {
262
- let ( ectx, client) = context;
263
- let erased = ( ectx as * mut _ as * mut ( ) , Some ( client) ) ;
264
- TLV . with ( |tlv| {
265
- let old = tlv. replace ( erased) ;
266
- let _reset = rustc_data_structures:: defer ( move || tlv. set ( old) ) ;
267
- f ( )
268
- } )
246
+ /// Stores the context necessary to expand a derive proc macro via a query.
247
+ struct QueryDeriveExpandCtx {
248
+ /// Type-erased version of `&mut ExtCtxt`
249
+ expansion_ctx : * mut ( ) ,
250
+ client : DeriveClient ,
269
251
}
270
252
271
- /// Allows access to the current `CONTEXT`.
272
- /// Panics if there is no `CONTEXT` available.
273
- #[ inline]
274
- #[ track_caller]
275
- fn with_context < F , R > ( f : F ) -> R
276
- where
277
- F : for <' a , ' b > FnOnce ( & ' b mut ( & mut ExtCtxt < ' a > , CLIENT ) ) -> R ,
278
- {
279
- let ( ectx, client_opt) = TLV . get ( ) ;
280
- let ectx = NonNull :: new ( ectx) . expect ( "no CONTEXT stored in tls" ) ;
281
-
282
- // We could get an `CONTEXT` pointer from another thread.
283
- // Ensure that `CONTEXT` is `DynSync`.
284
- // FIXME(pr-time): we should not be able to?
285
- // sync::assert_dyn_sync::<CONTEXT<'_>>();
286
-
287
- // prevent double entering, as that would allow creating two `&mut ExtCtxt`s
288
- // FIXME(pr-time): probably use a RefCell instead (which checks this properly)?
289
- TLV . with ( |tlv| {
290
- let old = tlv. replace ( ( std:: ptr:: null_mut ( ) , None ) ) ;
291
- let _reset = rustc_data_structures:: defer ( move || tlv. set ( old) ) ;
292
- let ectx = {
293
- let mut casted = ectx. cast :: < ExtCtxt < ' _ > > ( ) ;
294
- unsafe { casted. as_mut ( ) }
295
- } ;
253
+ impl QueryDeriveExpandCtx {
254
+ /// Store the extension context and the client into the thread local value.
255
+ /// It will be accessible via the `with` method while `f` is active.
256
+ fn enter < F , R > ( ecx : & mut ExtCtxt < ' _ > , client : DeriveClient , f : F ) -> R
257
+ where
258
+ F : FnOnce ( ) -> R ,
259
+ {
260
+ // We need erasure to get rid of the lifetime
261
+ let ctx = Self { expansion_ctx : ecx as * mut _ as * mut ( ) , client } ;
262
+ DERIVE_EXPAND_CTX . set ( & ctx, || f ( ) )
263
+ }
296
264
297
- f ( & mut ( ectx, client_opt. unwrap ( ) ) )
298
- } )
265
+ /// Accesses the thread local value of the derive expansion context.
266
+ /// Must be called while the `enter` function is active.
267
+ fn with < F , R > ( f : F ) -> R
268
+ where
269
+ F : for <' a , ' b > FnOnce ( & ' b mut ExtCtxt < ' a > , DeriveClient ) -> R ,
270
+ {
271
+ DERIVE_EXPAND_CTX . with ( |ctx| {
272
+ let ectx = {
273
+ let casted = ctx. expansion_ctx . cast :: < ExtCtxt < ' _ > > ( ) ;
274
+ // SAFETY: We can only get the value from `with` while the `enter` function
275
+ // is active (on the callstack), and that function's signature ensures that the
276
+ // lifetime is valid.
277
+ // If `with` is called at some other time, it will panic due to usage of
278
+ // `scoped_tls::with`.
279
+ unsafe { casted. as_mut ( ) . unwrap ( ) }
280
+ } ;
281
+
282
+ f ( ectx, ctx. client )
283
+ } )
284
+ }
299
285
}
286
+
287
+ // When we invoke a query to expand a derive proc macro, we need to provide it with the expansion
288
+ // context and derive Client. We do that using a thread-local.
289
+ scoped_tls:: scoped_thread_local!( static DERIVE_EXPAND_CTX : QueryDeriveExpandCtx ) ;
0 commit comments