44use crate :: imports:: ImportResolver ;
55use crate :: Namespace :: * ;
66use crate :: { AmbiguityError , AmbiguityErrorMisc , AmbiguityKind , BuiltinMacroState , Determinacy } ;
7- use crate :: { CrateLint , ParentScope , ResolutionError , Resolver , Scope , ScopeSet , Weak } ;
7+ use crate :: { CrateLint , DeriveData , ParentScope , ResolutionError , Resolver , Scope , ScopeSet , Weak } ;
88use crate :: { ModuleKind , ModuleOrUniformRoot , NameBinding , PathResult , Segment , ToNameBinding } ;
99use rustc_ast:: { self as ast, Inline , ItemKind , ModKind , NodeId } ;
1010use rustc_ast_lowering:: ResolverAstLowering ;
@@ -14,8 +14,8 @@ use rustc_data_structures::fx::FxHashSet;
1414use rustc_data_structures:: ptr_key:: PtrKey ;
1515use rustc_data_structures:: sync:: Lrc ;
1616use rustc_errors:: struct_span_err;
17- use rustc_expand:: base:: Annotatable ;
18- use rustc_expand:: base:: { Indeterminate , ResolverExpand , SyntaxExtension , SyntaxExtensionKind } ;
17+ use rustc_expand:: base:: { Annotatable , DeriveResolutions , Indeterminate , ResolverExpand } ;
18+ use rustc_expand:: base:: { SyntaxExtension , SyntaxExtensionKind } ;
1919use rustc_expand:: compile_declarative_macro;
2020use rustc_expand:: expand:: { AstFragment , Invocation , InvocationKind , SupportsMacroExpansion } ;
2121use rustc_feature:: is_builtin_attr_name;
@@ -359,58 +359,69 @@ impl<'a> ResolverExpand for Resolver<'a> {
359359 fn resolve_derives (
360360 & mut self ,
361361 expn_id : ExpnId ,
362- derives : Vec < ast:: Path > ,
363362 force : bool ,
363+ derive_paths : & dyn Fn ( ) -> DeriveResolutions ,
364364 ) -> Result < ( ) , Indeterminate > {
365365 // Block expansion of the container until we resolve all derives in it.
366366 // This is required for two reasons:
367367 // - Derive helper attributes are in scope for the item to which the `#[derive]`
368368 // is applied, so they have to be produced by the container's expansion rather
369369 // than by individual derives.
370370 // - Derives in the container need to know whether one of them is a built-in `Copy`.
371- // FIXME: Try to cache intermediate results to avoid resolving same derives multiple times.
371+ // Temporarily take the data to avoid borrow checker conflicts.
372+ let mut derive_data = mem:: take ( & mut self . derive_data ) ;
373+ let entry = derive_data. entry ( expn_id) . or_insert_with ( || DeriveData {
374+ resolutions : derive_paths ( ) ,
375+ helper_attrs : Vec :: new ( ) ,
376+ has_derive_copy : false ,
377+ } ) ;
372378 let parent_scope = self . invocation_parent_scopes [ & expn_id] ;
373- let mut exts = Vec :: new ( ) ;
374- let mut helper_attrs = Vec :: new ( ) ;
375- let mut has_derive_copy = false ;
376- for path in derives {
377- exts. push ( (
378- match self . resolve_macro_path (
379- & path,
380- Some ( MacroKind :: Derive ) ,
381- & parent_scope,
382- true ,
383- force,
384- ) {
385- Ok ( ( Some ( ext) , _) ) => {
386- let span =
387- path. segments . last ( ) . unwrap ( ) . ident . span . normalize_to_macros_2_0 ( ) ;
388- helper_attrs
389- . extend ( ext. helper_attrs . iter ( ) . map ( |name| Ident :: new ( * name, span) ) ) ;
390- has_derive_copy |= ext. builtin_name == Some ( sym:: Copy ) ;
391- ext
392- }
393- Ok ( _) | Err ( Determinacy :: Determined ) => self . dummy_ext ( MacroKind :: Derive ) ,
394- Err ( Determinacy :: Undetermined ) => return Err ( Indeterminate ) ,
395- } ,
396- path,
397- ) )
379+ for ( path, opt_ext) in & mut entry. resolutions {
380+ if opt_ext. is_none ( ) {
381+ * opt_ext = Some (
382+ match self . resolve_macro_path (
383+ & path,
384+ Some ( MacroKind :: Derive ) ,
385+ & parent_scope,
386+ true ,
387+ force,
388+ ) {
389+ Ok ( ( Some ( ext) , _) ) => {
390+ if !ext. helper_attrs . is_empty ( ) {
391+ let last_seg = path. segments . last ( ) . unwrap ( ) ;
392+ let span = last_seg. ident . span . normalize_to_macros_2_0 ( ) ;
393+ entry. helper_attrs . extend (
394+ ext. helper_attrs . iter ( ) . map ( |name| Ident :: new ( * name, span) ) ,
395+ ) ;
396+ }
397+ entry. has_derive_copy |= ext. builtin_name == Some ( sym:: Copy ) ;
398+ ext
399+ }
400+ Ok ( _) | Err ( Determinacy :: Determined ) => self . dummy_ext ( MacroKind :: Derive ) ,
401+ Err ( Determinacy :: Undetermined ) => {
402+ assert ! ( self . derive_data. is_empty( ) ) ;
403+ self . derive_data = derive_data;
404+ return Err ( Indeterminate ) ;
405+ }
406+ } ,
407+ ) ;
408+ }
398409 }
399- self . derive_resolutions . insert ( expn_id, exts) ;
400- self . helper_attrs . insert ( expn_id, helper_attrs) ;
410+ // If we get to here, then `derive_data` for the given `expn_id` will only be accessed by
411+ // `take_derive_resolutions` later, so we can steal `helper_attrs` instead of cloning them.
412+ self . helper_attrs . insert ( expn_id, mem:: take ( & mut entry. helper_attrs ) ) ;
401413 // Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive
402414 // has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`.
403- if has_derive_copy || self . has_derive_copy ( parent_scope. expansion ) {
415+ if entry . has_derive_copy || self . has_derive_copy ( parent_scope. expansion ) {
404416 self . containers_deriving_copy . insert ( expn_id) ;
405417 }
418+ assert ! ( self . derive_data. is_empty( ) ) ;
419+ self . derive_data = derive_data;
406420 Ok ( ( ) )
407421 }
408422
409- fn take_derive_resolutions (
410- & mut self ,
411- expn_id : ExpnId ,
412- ) -> Option < Vec < ( Lrc < SyntaxExtension > , ast:: Path ) > > {
413- self . derive_resolutions . remove ( & expn_id)
423+ fn take_derive_resolutions ( & mut self , expn_id : ExpnId ) -> Option < DeriveResolutions > {
424+ self . derive_data . remove ( & expn_id) . map ( |data| data. resolutions )
414425 }
415426
416427 // The function that implements the resolution logic of `#[cfg_accessible(path)]`.
0 commit comments