1- use std:: cell:: Cell ;
2- use std:: ptr:: NonNull ;
3-
41use rustc_ast:: tokenstream:: TokenStream ;
52use rustc_data_structures:: svh:: Svh ;
63use rustc_errors:: ErrorGuaranteed ;
@@ -36,7 +33,7 @@ impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
3633 }
3734}
3835
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 {
4037 pm:: bridge:: server:: MaybeCrossThread :: < MessagePipe < _ > > :: new (
4138 sess. opts . unstable_opts . proc_macro_execution_strategy
4239 == ProcMacroExecutionStrategy :: CrossThread ,
@@ -107,7 +104,7 @@ impl base::AttrProcMacro for AttrProcMacro {
107104}
108105
109106pub struct DeriveProcMacro {
110- pub client : pm :: bridge :: client :: Client < pm :: TokenStream , pm :: TokenStream > ,
107+ pub client : DeriveClient ,
111108}
112109
113110impl MultiItemModifier for DeriveProcMacro {
@@ -136,32 +133,31 @@ impl MultiItemModifier for DeriveProcMacro {
136133 // altogether. See #73345.
137134 crate :: base:: ann_pretty_printing_compatibility_hack ( & item, & ecx. sess . psess ) ;
138135 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| {
151143 // FIXME(pr-time): Just using the crate hash to notice when the proc-macro code has
152144 // changed. How to *correctly* depend on exactly the macro definition?
153145 // I.e., depending on the crate hash is just a HACK, and ideally the dependency would be
154146 // more narrow.
147+ let invoc_expn_data = invoc_id. expn_data ( ) ;
155148 let macro_def_id = invoc_expn_data. macro_def_id . unwrap ( ) ;
156149 let proc_macro_crate_hash = tcx. crate_hash ( macro_def_id. krate ) ;
157150
151+ let input = tcx. arena . alloc ( input) as & TokenStream ;
158152 let key = ( invoc_id, proc_macro_crate_hash, input) ;
159153
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+ } ;
165161
166162 let Ok ( output) = res else {
167163 // error will already have been emitted
@@ -205,36 +201,36 @@ pub(super) fn provide_derive_macro_expansion<'tcx>(
205201) -> Result < & ' tcx TokenStream , ( ) > {
206202 let ( invoc_id, _macro_crate_hash, input) = key;
207203
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+ } )
209208}
210209
211- type CLIENT = pm:: bridge:: client:: Client < pm:: TokenStream , pm:: TokenStream > ;
210+ type DeriveClient = pm:: bridge:: client:: Client < pm:: TokenStream , pm:: TokenStream > ;
212211
213- fn expand_derive_macro < ' tcx > (
214- tcx : TyCtxt < ' tcx > ,
212+ fn expand_derive_macro (
215213 invoc_id : LocalExpnId ,
216- input : & ' tcx TokenStream ,
214+ input : TokenStream ,
217215 ecx : & mut ExtCtxt < ' _ > ,
218- client : CLIENT ,
219- ) -> Result < & ' tcx TokenStream , ( ) > {
216+ client : DeriveClient ,
217+ ) -> Result < TokenStream , ( ) > {
220218 let invoc_expn_data = invoc_id. expn_data ( ) ;
221219 let span = invoc_expn_data. call_site ;
222220 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+ } ) ;
229225
230226 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) ;
233229
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) ,
236232 Err ( e) => {
237- tcx . dcx ( ) . emit_err ( {
233+ ecx . dcx ( ) . emit_err ( {
238234 errors:: ProcMacroDerivePanicked {
239235 span,
240236 message : e. as_str ( ) . map ( |message| errors:: ProcMacroDerivePanickedHelp {
@@ -247,53 +243,47 @@ fn expand_derive_macro<'tcx>(
247243 }
248244}
249245
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 ,
269251}
270252
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+ }
296264
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+ }
299285}
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