@@ -9,7 +9,7 @@ use std::sync::atomic::Ordering;
99
1010use hir:: def_id:: { LocalDefIdMap , LocalDefIdSet } ;
1111use rustc_abi:: FieldIdx ;
12- use rustc_data_structures:: fx:: { FxHashSet , FxIndexSet } ;
12+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
1313use rustc_errors:: { ErrorGuaranteed , MultiSpan } ;
1414use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
1515use rustc_hir:: def_id:: { DefId , LocalDefId , LocalModId } ;
@@ -113,6 +113,11 @@ struct WorkItem {
113113 own : ComesFromAllowExpect ,
114114}
115115
116+ enum ImplItemCheckResult {
117+ Live ( ComesFromAllowExpect ) ,
118+ Dead { require : LocalDefId } ,
119+ }
120+
116121struct MarkSymbolVisitor < ' tcx > {
117122 worklist : Vec < WorkItem > ,
118123 tcx : TyCtxt < ' tcx > ,
@@ -128,6 +133,7 @@ struct MarkSymbolVisitor<'tcx> {
128133 // macro)
129134 ignored_derived_traits : LocalDefIdMap < FxIndexSet < DefId > > ,
130135 propagated_comes_from_allow_expect : ComesFromAllowExpect ,
136+ unsolved_items : Vec < LocalDefId > ,
131137}
132138
133139impl < ' tcx > MarkSymbolVisitor < ' tcx > {
@@ -418,6 +424,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
418424
419425 if !self . scanned . insert ( ( id, propagated) ) {
420426 continue ;
427+ } else if propagated == ComesFromAllowExpect :: No {
428+ self . scanned . insert ( ( id, ComesFromAllowExpect :: Yes ) ) ;
421429 }
422430
423431 // Avoid accessing the HIR for the synthesized associated type generated for RPITITs.
@@ -544,13 +552,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
544552 /// `local_def_id` points to an impl or an impl item,
545553 /// both impl and impl item that may be passed to this function are of a trait,
546554 /// and added into the unsolved_items during `create_and_seed_worklist`
547- fn check_impl_or_impl_item_live ( & mut self , local_def_id : LocalDefId ) -> bool {
555+ fn check_impl_or_impl_item_live (
556+ & self ,
557+ local_def_id : LocalDefId ,
558+ defer_seeds_come_from_allow : bool ,
559+ ) -> ImplItemCheckResult {
548560 let ( impl_block_id, trait_def_id) = match self . tcx . def_kind ( local_def_id) {
549561 // assoc impl items of traits are live if the corresponding trait items are live
550562 DefKind :: AssocConst { .. } | DefKind :: AssocTy | DefKind :: AssocFn => {
551- let trait_item_id =
563+ let trait_def_id =
552564 self . tcx . trait_item_of ( local_def_id) . and_then ( |def_id| def_id. as_local ( ) ) ;
553- ( self . tcx . local_parent ( local_def_id) , trait_item_id )
565+ ( self . tcx . local_parent ( local_def_id) , trait_def_id )
554566 }
555567 // impl items are live if the corresponding traits are live
556568 DefKind :: Impl { of_trait : true } => {
@@ -559,10 +571,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
559571 _ => bug ! ( ) ,
560572 } ;
561573
562- if let Some ( trait_def_id) = trait_def_id
563- && !self . live_symbols . contains ( & trait_def_id)
564- {
565- return false ;
574+ let mut trait_comes_from_allow = None ;
575+ if let Some ( trait_def_id) = trait_def_id {
576+ if defer_seeds_come_from_allow {
577+ if !self . live_symbols . contains ( & trait_def_id) {
578+ return ImplItemCheckResult :: Dead { require : trait_def_id } ;
579+ }
580+ } else {
581+ trait_comes_from_allow = has_allow_dead_code_or_lang_attr ( self . tcx , trait_def_id) ;
582+
583+ if !self . live_symbols . contains ( & trait_def_id) {
584+ return match trait_comes_from_allow {
585+ Some ( comes_from_allow) => ImplItemCheckResult :: Live ( comes_from_allow) ,
586+ None => ImplItemCheckResult :: Dead { require : trait_def_id } ,
587+ } ;
588+ }
589+ }
566590 }
567591
568592 // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
@@ -571,10 +595,93 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
571595 && let Some ( adt_def_id) = adt. did ( ) . as_local ( )
572596 && !self . live_symbols . contains ( & adt_def_id)
573597 {
574- return false ;
598+ if defer_seeds_come_from_allow {
599+ return ImplItemCheckResult :: Dead { require : adt_def_id } ;
600+ }
601+
602+ return match trait_comes_from_allow {
603+ Some ( comes_from_allow) => ImplItemCheckResult :: Live ( comes_from_allow) ,
604+ None => ImplItemCheckResult :: Dead { require : adt_def_id } ,
605+ } ;
606+ }
607+
608+ ImplItemCheckResult :: Live ( ComesFromAllowExpect :: No )
609+ }
610+
611+ fn collect_live_items_from_unsolved_items (
612+ & mut self ,
613+ defer_seeds_come_from_allow : bool ,
614+ unsolved_items : Vec < LocalDefId > ,
615+ unsolved_map : & mut FxHashMap < LocalDefId , Vec < LocalDefId > > ,
616+ ) -> Vec < ( LocalDefId , ComesFromAllowExpect ) > {
617+ let mut items_to_check = vec ! [ ] ;
618+
619+ for def_id in unsolved_items {
620+ match self . check_impl_or_impl_item_live ( def_id, defer_seeds_come_from_allow) {
621+ ImplItemCheckResult :: Live ( comes_from_allow) => {
622+ items_to_check. push ( ( def_id, comes_from_allow) ) ;
623+ }
624+ ImplItemCheckResult :: Dead { require } => {
625+ unsolved_map. entry ( require) . or_default ( ) . push ( def_id) ;
626+ }
627+ }
628+ }
629+ items_to_check
630+ }
631+
632+ #[ expect(
633+ rustc:: potential_query_instability,
634+ reason = "The order of the unsolved items is not important, so we can just collect them into a vector."
635+ ) ]
636+ fn mark_live_symbols_and_ignored_derived_traits (
637+ & mut self ,
638+ defer_seeds_come_from_allow : bool ,
639+ ) -> Result < ( ) , ErrorGuaranteed > {
640+ if let ControlFlow :: Break ( guar) = self . mark_live_symbols ( ) {
641+ return Err ( guar) ;
642+ }
643+
644+ // We have marked the primary seeds as live. We now need to process unsolved items from traits
645+ // and trait impls: add them to the work list if the trait or the implemented type is live.
646+ let unsolved_items = std:: mem:: take ( & mut self . unsolved_items ) ;
647+ let mut unsolved_map = FxHashMap :: default ( ) ;
648+ let mut items_to_check = self . collect_live_items_from_unsolved_items (
649+ defer_seeds_come_from_allow,
650+ unsolved_items,
651+ & mut unsolved_map,
652+ ) ;
653+
654+ while !items_to_check. is_empty ( ) {
655+ self . worklist . extend ( items_to_check. into_iter ( ) . map ( |( id, comes_from_allow) | {
656+ let own = if defer_seeds_come_from_allow {
657+ ComesFromAllowExpect :: No
658+ } else {
659+ has_allow_dead_code_or_lang_attr ( self . tcx , id)
660+ . unwrap_or ( ComesFromAllowExpect :: No )
661+ } ;
662+
663+ WorkItem { id, propagated : comes_from_allow, own }
664+ } ) ) ;
665+ if let ControlFlow :: Break ( guar) = self . mark_live_symbols ( ) {
666+ return Err ( guar) ;
667+ }
668+
669+ let unsolved_items = unsolved_map
670+ . extract_if ( |require, _| self . live_symbols . contains ( require) )
671+ . map ( |( _, items) | items)
672+ . flatten ( )
673+ . collect ( ) ;
674+
675+ items_to_check = self . collect_live_items_from_unsolved_items (
676+ defer_seeds_come_from_allow,
677+ unsolved_items,
678+ & mut unsolved_map,
679+ ) ;
575680 }
576681
577- true
682+ self . unsolved_items = unsolved_map. values ( ) . into_iter ( ) . cloned ( ) . flatten ( ) . collect ( ) ;
683+
684+ Ok ( ( ) )
578685 }
579686}
580687
@@ -849,19 +956,6 @@ fn maybe_record_as_seed<'tcx>(
849956 match tcx. def_kind ( parent) {
850957 DefKind :: Impl { of_trait : false } | DefKind :: Trait => { }
851958 DefKind :: Impl { of_trait : true } => {
852- if let Some ( trait_item_def_id) =
853- tcx. associated_item ( owner_id. def_id ) . trait_item_def_id ( )
854- && let Some ( trait_item_local_def_id) = trait_item_def_id. as_local ( )
855- && let Some ( comes_from_allow) =
856- has_allow_dead_code_or_lang_attr ( tcx, trait_item_local_def_id)
857- {
858- push_into_worklist ( WorkItem {
859- id : owner_id. def_id ,
860- propagated : comes_from_allow,
861- own : comes_from_allow,
862- } ) ;
863- }
864-
865959 // We only care about associated items of traits,
866960 // because they cannot be visited directly,
867961 // so we later mark them as live if their corresponding traits
@@ -873,22 +967,8 @@ fn maybe_record_as_seed<'tcx>(
873967 }
874968 }
875969 }
876- DefKind :: Impl { of_trait : true } => {
877- if allow_dead_code. is_none ( ) {
878- if let Some ( trait_def_id) =
879- tcx. impl_trait_ref ( owner_id. def_id ) . skip_binder ( ) . def_id . as_local ( )
880- && let Some ( comes_from_allow) =
881- has_allow_dead_code_or_lang_attr ( tcx, trait_def_id)
882- {
883- push_into_worklist ( WorkItem {
884- id : owner_id. def_id ,
885- propagated : comes_from_allow,
886- own : comes_from_allow,
887- } ) ;
888- }
889-
890- unsolved_items. push ( owner_id. def_id ) ;
891- }
970+ DefKind :: Impl { of_trait : true } if allow_dead_code. is_none ( ) => {
971+ unsolved_items. push ( owner_id. def_id ) ;
892972 }
893973 DefKind :: GlobalAsm => {
894974 // global_asm! is always live.
@@ -914,15 +994,21 @@ fn maybe_record_as_seed<'tcx>(
914994 }
915995}
916996
997+ #[ derive( Default ) ]
998+ struct DeferredSeeds {
999+ pub_reachables : Vec < WorkItem > ,
1000+ come_from_allow : Vec < WorkItem > ,
1001+ }
1002+
9171003struct SeedWorklists {
9181004 worklist : Vec < WorkItem > ,
919- deferred_seeds : Vec < WorkItem > ,
1005+ deferred_seeds : DeferredSeeds ,
9201006 unsolved_items : Vec < LocalDefId > ,
9211007}
9221008
9231009fn create_and_seed_worklist ( tcx : TyCtxt < ' _ > ) -> SeedWorklists {
9241010 let mut unsolved_items = Vec :: new ( ) ;
925- let mut deferred_seeds = Vec :: new ( ) ;
1011+ let mut deferred_seeds = DeferredSeeds :: default ( ) ;
9261012 let mut worklist = Vec :: new ( ) ;
9271013
9281014 if let Some ( ( def_id, _) ) = tcx. entry_fn ( ( ) )
@@ -952,7 +1038,7 @@ fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9521038
9531039 for ( id, effective_vis) in tcx. effective_visibilities ( ( ) ) . iter ( ) {
9541040 if effective_vis. is_public_at_level ( Level :: Reachable ) {
955- deferred_seeds. push ( WorkItem {
1041+ deferred_seeds. pub_reachables . push ( WorkItem {
9561042 id : * id,
9571043 propagated : ComesFromAllowExpect :: No ,
9581044 own : ComesFromAllowExpect :: No ,
@@ -961,7 +1047,7 @@ fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9611047 }
9621048
9631049 let mut push_into_worklist = |work_item : WorkItem | match work_item. own {
964- ComesFromAllowExpect :: Yes => deferred_seeds. push ( work_item) ,
1050+ ComesFromAllowExpect :: Yes => deferred_seeds. come_from_allow . push ( work_item) ,
9651051 ComesFromAllowExpect :: No => worklist. push ( work_item) ,
9661052 } ;
9671053 let crate_items = tcx. hir_crate_items ( ( ) ) ;
@@ -976,8 +1062,7 @@ fn live_symbols_and_ignored_derived_traits(
9761062 tcx : TyCtxt < ' _ > ,
9771063 ( ) : ( ) ,
9781064) -> Result < DeadCodeLivenessSummary , ErrorGuaranteed > {
979- let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } =
980- create_and_seed_worklist ( tcx) ;
1065+ let SeedWorklists { worklist, deferred_seeds, unsolved_items } = create_and_seed_worklist ( tcx) ;
9811066 let mut symbol_visitor = MarkSymbolVisitor {
9821067 worklist,
9831068 tcx,
@@ -990,15 +1075,23 @@ fn live_symbols_and_ignored_derived_traits(
9901075 ignore_variant_stack : vec ! [ ] ,
9911076 ignored_derived_traits : Default :: default ( ) ,
9921077 propagated_comes_from_allow_expect : ComesFromAllowExpect :: No ,
1078+ unsolved_items,
9931079 } ;
994- mark_live_symbols_and_ignored_derived_traits ( & mut symbol_visitor , & mut unsolved_items ) ?;
1080+ symbol_visitor . mark_live_symbols_and_ignored_derived_traits ( true ) ?;
9951081 let pre_deferred_seeding = DeadCodeLivenessSnapshot {
9961082 live_symbols : symbol_visitor. live_symbols . clone ( ) ,
9971083 ignored_derived_traits : symbol_visitor. ignored_derived_traits . clone ( ) ,
9981084 } ;
9991085
1000- symbol_visitor. worklist . extend ( deferred_seeds) ;
1001- mark_live_symbols_and_ignored_derived_traits ( & mut symbol_visitor, & mut unsolved_items) ?;
1086+ if !deferred_seeds. pub_reachables . is_empty ( ) {
1087+ symbol_visitor. worklist . extend ( deferred_seeds. pub_reachables ) ;
1088+ symbol_visitor. mark_live_symbols_and_ignored_derived_traits ( true ) ?;
1089+ }
1090+
1091+ if !deferred_seeds. come_from_allow . is_empty ( ) {
1092+ symbol_visitor. worklist . extend ( deferred_seeds. come_from_allow ) ;
1093+ symbol_visitor. mark_live_symbols_and_ignored_derived_traits ( false ) ?;
1094+ }
10021095
10031096 Ok ( DeadCodeLivenessSummary {
10041097 pre_deferred_seeding,
@@ -1009,40 +1102,6 @@ fn live_symbols_and_ignored_derived_traits(
10091102 } )
10101103}
10111104
1012- fn mark_live_symbols_and_ignored_derived_traits (
1013- symbol_visitor : & mut MarkSymbolVisitor < ' _ > ,
1014- unsolved_items : & mut Vec < LocalDefId > ,
1015- ) -> Result < ( ) , ErrorGuaranteed > {
1016- if let ControlFlow :: Break ( guar) = symbol_visitor. mark_live_symbols ( ) {
1017- return Err ( guar) ;
1018- }
1019-
1020- // We have marked the primary seeds as live. We now need to process unsolved items from traits
1021- // and trait impls: add them to the work list if the trait or the implemented type is live.
1022- let mut items_to_check: Vec < _ > = unsolved_items
1023- . extract_if ( .., |& mut local_def_id| {
1024- symbol_visitor. check_impl_or_impl_item_live ( local_def_id)
1025- } )
1026- . collect ( ) ;
1027-
1028- while !items_to_check. is_empty ( ) {
1029- symbol_visitor. worklist . extend ( items_to_check. drain ( ..) . map ( |id| WorkItem {
1030- id,
1031- propagated : ComesFromAllowExpect :: No ,
1032- own : ComesFromAllowExpect :: No ,
1033- } ) ) ;
1034- if let ControlFlow :: Break ( guar) = symbol_visitor. mark_live_symbols ( ) {
1035- return Err ( guar) ;
1036- }
1037-
1038- items_to_check. extend ( unsolved_items. extract_if ( .., |& mut local_def_id| {
1039- symbol_visitor. check_impl_or_impl_item_live ( local_def_id)
1040- } ) ) ;
1041- }
1042-
1043- Ok ( ( ) )
1044- }
1045-
10461105struct DeadItem {
10471106 def_id : LocalDefId ,
10481107 name : Symbol ,
0 commit comments