11use rustc_ast:: tokenstream:: TokenStream ;
2+ use rustc_data_structures:: svh:: Svh ;
23use rustc_errors:: ErrorGuaranteed ;
4+ use rustc_middle:: ty:: { self , TyCtxt } ;
35use rustc_parse:: parser:: { ForceCollect , Parser } ;
6+ use rustc_session:: Session ;
47use rustc_session:: config:: ProcMacroExecutionStrategy ;
5- use rustc_span:: Span ;
68use rustc_span:: profiling:: SpannedEventArgRecorder ;
9+ use rustc_span:: { LocalExpnId , Span } ;
710use { rustc_ast as ast, rustc_proc_macro as pm} ;
811
912use crate :: base:: { self , * } ;
@@ -30,9 +33,9 @@ impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
3033 }
3134}
3235
33- fn exec_strategy ( ecx : & ExtCtxt < ' _ > ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
36+ fn exec_strategy ( sess : & Session ) -> impl pm:: bridge:: server:: ExecutionStrategy + ' static {
3437 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
3639 == ProcMacroExecutionStrategy :: CrossThread ,
3740 )
3841}
@@ -54,7 +57,7 @@ impl base::BangProcMacro for BangProcMacro {
5457 } ) ;
5558
5659 let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
57- let strategy = exec_strategy ( ecx) ;
60+ let strategy = exec_strategy ( ecx. sess ) ;
5861 let server = proc_macro_server:: Rustc :: new ( ecx) ;
5962 self . client . run ( & strategy, server, input, proc_macro_backtrace) . map_err ( |e| {
6063 ecx. dcx ( ) . emit_err ( errors:: ProcMacroPanicked {
@@ -85,7 +88,7 @@ impl base::AttrProcMacro for AttrProcMacro {
8588 } ) ;
8689
8790 let proc_macro_backtrace = ecx. ecfg . proc_macro_backtrace ;
88- let strategy = exec_strategy ( ecx) ;
91+ let strategy = exec_strategy ( ecx. sess ) ;
8992 let server = proc_macro_server:: Rustc :: new ( ecx) ;
9093 self . client . run ( & strategy, server, annotation, annotated, proc_macro_backtrace) . map_err (
9194 |e| {
@@ -101,7 +104,7 @@ impl base::AttrProcMacro for AttrProcMacro {
101104}
102105
103106pub struct DeriveProcMacro {
104- pub client : pm :: bridge :: client :: Client < pm :: TokenStream , pm :: TokenStream > ,
107+ pub client : DeriveClient ,
105108}
106109
107110impl MultiItemModifier for DeriveProcMacro {
@@ -113,6 +116,13 @@ impl MultiItemModifier for DeriveProcMacro {
113116 item : Annotatable ,
114117 _is_derive_const : bool ,
115118 ) -> 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+
116126 // We need special handling for statement items
117127 // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
118128 let is_stmt = matches ! ( item, Annotatable :: Stmt ( ..) ) ;
@@ -123,36 +133,39 @@ impl MultiItemModifier for DeriveProcMacro {
123133 // altogether. See #73345.
124134 crate :: base:: ann_pretty_printing_compatibility_hack ( & item, & ecx. sess . psess ) ;
125135 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 ! [ ] ) ;
152165 } ;
153166
154167 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" ) ) ;
156169 let mut items = vec ! [ ] ;
157170
158171 loop {
@@ -180,3 +193,99 @@ impl MultiItemModifier for DeriveProcMacro {
180193 ExpandResult :: Ready ( items)
181194 }
182195}
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