@@ -17,7 +17,7 @@ use rustc_hir::{self as hir, Node, PatKind, TyKind};
17
17
use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
18
18
use rustc_middle:: middle:: privacy:: Level ;
19
19
use rustc_middle:: query:: Providers ;
20
- use rustc_middle:: ty:: { self , AssocItemContainer , Ty , TyCtxt } ;
20
+ use rustc_middle:: ty:: { self , AssocItemContainer , TyCtxt } ;
21
21
use rustc_middle:: { bug, span_bug} ;
22
22
use rustc_session:: lint;
23
23
use rustc_session:: lint:: builtin:: DEAD_CODE ;
@@ -44,7 +44,9 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
44
44
)
45
45
}
46
46
47
- fn adt_of < ' tcx > ( ty : & hir:: Ty < ' tcx > ) -> Option < ( LocalDefId , DefKind ) > {
47
+ /// Returns the local def id and def kind of the adt,
48
+ /// if the given ty refers to one local adt definition
49
+ fn adt_def_of_ty < ' tcx > ( ty : & hir:: Ty < ' tcx > ) -> Option < ( LocalDefId , DefKind ) > {
48
50
match ty. kind {
49
51
TyKind :: Path ( hir:: QPath :: Resolved ( _, path) ) => {
50
52
if let Res :: Def ( def_kind, def_id) = path. res
@@ -55,8 +57,8 @@ fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
55
57
None
56
58
}
57
59
}
58
- TyKind :: Slice ( ty) | TyKind :: Array ( ty, _) => adt_of ( ty) ,
59
- TyKind :: Ptr ( ty) | TyKind :: Ref ( _, ty) => adt_of ( ty. ty ) ,
60
+ TyKind :: Slice ( ty) | TyKind :: Array ( ty, _) => adt_def_of_ty ( ty) ,
61
+ TyKind :: Ptr ( ty) | TyKind :: Ref ( _, ty) => adt_def_of_ty ( ty. ty ) ,
60
62
_ => None ,
61
63
}
62
64
}
@@ -114,7 +116,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
114
116
115
117
fn handle_res ( & mut self , res : Res ) {
116
118
match res {
117
- Res :: Def ( DefKind :: Const | DefKind :: AssocConst | DefKind :: TyAlias , def_id) => {
119
+ Res :: Def (
120
+ DefKind :: AssocConst | DefKind :: AssocTy | DefKind :: Const | DefKind :: TyAlias ,
121
+ def_id,
122
+ ) => {
118
123
self . check_def_id ( def_id) ;
119
124
}
120
125
_ if self . in_pat => { }
@@ -234,6 +239,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
234
239
) {
235
240
let variant = match self . typeck_results ( ) . node_type ( lhs. hir_id ) . kind ( ) {
236
241
ty:: Adt ( adt, _) => {
242
+ // mark the adt live if its variant appears as the pattern
243
+ // considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`
244
+ // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`
245
+ // related issue: https://github.com/rust-lang/rust/issues/120770
237
246
self . check_def_id ( adt. did ( ) ) ;
238
247
adt. variant_of_res ( res)
239
248
}
@@ -257,6 +266,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
257
266
) {
258
267
let variant = match self . typeck_results ( ) . node_type ( lhs. hir_id ) . kind ( ) {
259
268
ty:: Adt ( adt, _) => {
269
+ // mark the adt live if its variant appears as the pattern
270
+ // considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`
271
+ // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`
272
+ // related issue: https://github.com/rust-lang/rust/issues/120770
260
273
self . check_def_id ( adt. did ( ) ) ;
261
274
adt. variant_of_res ( res)
262
275
}
@@ -406,13 +419,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
406
419
intravisit:: walk_item ( self , item)
407
420
}
408
421
hir:: ItemKind :: ForeignMod { .. } => { }
422
+ hir:: ItemKind :: Trait ( _, _, _, _, trait_item_refs) => {
423
+ // mark assoc ty live if the trait is live
424
+ for trait_item in trait_item_refs {
425
+ if matches ! ( trait_item. kind, hir:: AssocItemKind :: Type ) {
426
+ self . check_def_id ( trait_item. id . owner_id . to_def_id ( ) ) ;
427
+ }
428
+ }
429
+ intravisit:: walk_item ( self , item)
430
+ }
409
431
_ => intravisit:: walk_item ( self , item) ,
410
432
} ,
411
433
Node :: TraitItem ( trait_item) => {
412
- // mark corresponding ImplTerm live
434
+ // mark the trait live
413
435
let trait_item_id = trait_item. owner_id . to_def_id ( ) ;
414
436
if let Some ( trait_id) = self . tcx . trait_of_item ( trait_item_id) {
415
- // mark the trait live
416
437
self . check_def_id ( trait_id) ;
417
438
}
418
439
@@ -437,6 +458,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
437
458
_ => { }
438
459
}
439
460
}
461
+
440
462
intravisit:: walk_impl_item ( self , impl_item) ;
441
463
}
442
464
Node :: ForeignItem ( foreign_item) => {
@@ -458,55 +480,44 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
458
480
}
459
481
}
460
482
461
- fn solve_rest_items ( & mut self , mut unsolved_items : Vec < ( hir:: ItemId , LocalDefId ) > ) {
462
- let mut ready;
463
- ( ready, unsolved_items) =
464
- unsolved_items. into_iter ( ) . partition ( |& ( impl_id, local_def_id) | {
465
- self . item_should_be_checked ( impl_id, local_def_id)
466
- } ) ;
467
-
468
- while !ready. is_empty ( ) {
469
- self . worklist =
470
- ready. into_iter ( ) . map ( |( _, id) | ( id, ComesFromAllowExpect :: No ) ) . collect ( ) ;
471
- self . mark_live_symbols ( ) ;
472
-
473
- ( ready, unsolved_items) =
474
- unsolved_items. into_iter ( ) . partition ( |& ( impl_id, local_def_id) | {
475
- self . item_should_be_checked ( impl_id, local_def_id)
476
- } ) ;
477
- }
478
- }
479
-
483
+ /// Returns an impl or impl item should be checked or not
484
+ /// `impl_id` is the `ItemId` of the impl or the impl item belongs to,
485
+ /// `local_def_id` may point to the impl or the impl item,
486
+ /// both impl and impl item that may pass to this function are of trait,
487
+ /// and added into the unsolved_items during `create_and_seed_worklist`
480
488
fn item_should_be_checked ( & mut self , impl_id : hir:: ItemId , local_def_id : LocalDefId ) -> bool {
481
489
let trait_def_id = match self . tcx . def_kind ( local_def_id) {
482
- // for assoc impl items of traits, we concern the corresponding trait items are used or not
483
- DefKind :: AssocFn => self
490
+ // assoc impl items of traits are live if the corresponding trait items are live
491
+ DefKind :: AssocConst | DefKind :: AssocTy | DefKind :: AssocFn => self
484
492
. tcx
485
493
. associated_item ( local_def_id)
486
494
. trait_item_def_id
487
495
. and_then ( |def_id| def_id. as_local ( ) ) ,
488
- // for impl items, we concern the corresonding traits are used or not
496
+ // impl items are live if the corresponding traits are live
489
497
DefKind :: Impl { of_trait : true } => self
490
498
. tcx
491
499
. impl_trait_ref ( impl_id. owner_id . def_id )
492
500
. and_then ( |trait_ref| trait_ref. skip_binder ( ) . def_id . as_local ( ) ) ,
493
501
_ => None ,
494
502
} ;
495
503
496
- if !trait_def_id. map ( |def_id| self . live_symbols . contains ( & def_id) ) . unwrap_or ( true ) {
504
+ if let Some ( def_id) = trait_def_id
505
+ && !self . live_symbols . contains ( & def_id)
506
+ {
497
507
return false ;
498
508
}
499
509
500
510
// we only check the ty is used or not for ADTs defined locally
501
- let ty_def_id = adt_of ( self . tcx . hir_item ( impl_id) . expect_impl ( ) . self_ty ) . and_then (
502
- |( local_def_id, def_kind) | {
503
- matches ! ( def_kind, DefKind :: Struct | DefKind :: Enum | DefKind :: Union )
504
- . then_some ( local_def_id)
505
- } ,
506
- ) ;
507
-
508
511
// the impl/impl item is used if the trait/trait item is used and the ty is used
509
- ty_def_id. map ( |def_id| self . live_symbols . contains ( & def_id) ) . unwrap_or ( true )
512
+ if let Some ( ( local_def_id, def_kind) ) =
513
+ adt_def_of_ty ( self . tcx . hir_item ( impl_id) . expect_impl ( ) . self_ty )
514
+ && matches ! ( def_kind, DefKind :: Struct | DefKind :: Enum | DefKind :: Union )
515
+ && !self . live_symbols . contains ( & local_def_id)
516
+ {
517
+ return false ;
518
+ }
519
+
520
+ true
510
521
}
511
522
}
512
523
@@ -643,6 +654,29 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
643
654
644
655
self . in_pat = in_pat;
645
656
}
657
+
658
+ fn visit_poly_trait_ref ( & mut self , t : & ' tcx hir:: PolyTraitRef < ' tcx > ) {
659
+ // mark the assoc const appears in poly-trait-ref live
660
+ if let Some ( pathsegment) = t. trait_ref . path . segments . last ( )
661
+ && let Some ( args) = pathsegment. args
662
+ {
663
+ for constraint in args. constraints {
664
+ if let Some ( item) = self
665
+ . tcx
666
+ . associated_items ( pathsegment. res . def_id ( ) )
667
+ . filter_by_name_unhygienic ( constraint. ident . name )
668
+ . find ( |i| {
669
+ matches ! ( i. kind, ty:: AssocKind :: Const )
670
+ && i. ident ( self . tcx ) . normalize_to_macros_2_0 ( ) == constraint. ident
671
+ } )
672
+ && let Some ( local_def_id) = item. def_id . as_local ( )
673
+ {
674
+ self . worklist . push ( ( local_def_id, ComesFromAllowExpect :: No ) ) ;
675
+ }
676
+ }
677
+ }
678
+ intravisit:: walk_poly_trait_ref ( self , t) ;
679
+ }
646
680
}
647
681
648
682
fn has_allow_dead_code_or_lang_attr (
@@ -726,7 +760,7 @@ fn check_item<'tcx>(
726
760
}
727
761
DefKind :: Impl { of_trait } => {
728
762
// get DefIds from another query
729
- let local_def_ids = tcx
763
+ let associated_item_def_ids = tcx
730
764
. associated_item_def_ids ( id. owner_id )
731
765
. iter ( )
732
766
. filter_map ( |def_id| def_id. as_local ( ) ) ;
@@ -740,14 +774,16 @@ fn check_item<'tcx>(
740
774
}
741
775
742
776
// And we access the Map here to get HirId from LocalDefId
743
- for local_def_id in local_def_ids {
744
- if !matches ! ( tcx. def_kind( local_def_id) , DefKind :: AssocFn ) {
745
- worklist. push ( ( local_def_id, ComesFromAllowExpect :: No ) ) ;
746
- } else if let Some ( comes_from_allow) =
747
- has_allow_dead_code_or_lang_attr ( tcx, local_def_id)
777
+ for local_def_id in associated_item_def_ids {
778
+ if let Some ( comes_from_allow) = has_allow_dead_code_or_lang_attr ( tcx, local_def_id)
748
779
{
749
780
worklist. push ( ( local_def_id, comes_from_allow) ) ;
750
781
} else if of_trait {
782
+ // we only care about assoc items of trait,
783
+ // because they cannot be visited directly
784
+ // so we mark them based on the trait/trait item
785
+ // and self ty are checked first and both live,
786
+ // but inherent assoc items can be visited and marked directly
751
787
unsolved_items. push ( ( id, local_def_id) ) ;
752
788
}
753
789
}
@@ -773,10 +809,13 @@ fn check_trait_item(
773
809
worklist : & mut Vec < ( LocalDefId , ComesFromAllowExpect ) > ,
774
810
id : hir:: TraitItemId ,
775
811
) {
776
- use hir:: TraitItemKind :: { Const , Fn } ;
777
- if matches ! ( tcx. def_kind( id. owner_id) , DefKind :: AssocConst | DefKind :: AssocFn ) {
812
+ use hir:: TraitItemKind :: { Const , Fn , Type } ;
813
+ if matches ! (
814
+ tcx. def_kind( id. owner_id) ,
815
+ DefKind :: AssocConst | DefKind :: AssocTy | DefKind :: AssocFn
816
+ ) {
778
817
let trait_item = tcx. hir_trait_item ( id) ;
779
- if matches ! ( trait_item. kind, Const ( _, Some ( _) ) | Fn ( ..) )
818
+ if matches ! ( trait_item. kind, Const ( _, Some ( _) ) | Type ( .. ) | Fn ( ..) )
780
819
&& let Some ( comes_from_allow) =
781
820
has_allow_dead_code_or_lang_attr ( tcx, trait_item. owner_id . def_id )
782
821
{
@@ -818,7 +857,7 @@ fn create_and_seed_worklist(
818
857
// checks impls and impl-items later
819
858
match tcx. def_kind ( id) {
820
859
DefKind :: Impl { of_trait } => !of_trait,
821
- DefKind :: AssocFn => {
860
+ DefKind :: AssocConst | DefKind :: AssocTy | DefKind :: AssocFn => {
822
861
// still check public trait items, and impl items not of trait
823
862
let assoc_item = tcx. associated_item ( id) ;
824
863
!matches ! ( assoc_item. container, AssocItemContainer :: Impl )
@@ -855,7 +894,7 @@ fn live_symbols_and_ignored_derived_traits(
855
894
tcx : TyCtxt < ' _ > ,
856
895
( ) : ( ) ,
857
896
) -> ( LocalDefIdSet , LocalDefIdMap < Vec < ( DefId , DefId ) > > ) {
858
- let ( worklist, struct_constructors, unsolved_items) = create_and_seed_worklist ( tcx) ;
897
+ let ( worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist ( tcx) ;
859
898
let mut symbol_visitor = MarkSymbolVisitor {
860
899
worklist,
861
900
tcx,
@@ -868,8 +907,26 @@ fn live_symbols_and_ignored_derived_traits(
868
907
struct_constructors,
869
908
ignored_derived_traits : Default :: default ( ) ,
870
909
} ;
910
+
871
911
symbol_visitor. mark_live_symbols ( ) ;
872
- symbol_visitor. solve_rest_items ( unsolved_items) ;
912
+ let mut rest_items_should_be_checked;
913
+ ( rest_items_should_be_checked, unsolved_items) =
914
+ unsolved_items. into_iter ( ) . partition ( |& ( impl_id, local_def_id) | {
915
+ symbol_visitor. item_should_be_checked ( impl_id, local_def_id)
916
+ } ) ;
917
+
918
+ while !rest_items_should_be_checked. is_empty ( ) {
919
+ symbol_visitor. worklist = rest_items_should_be_checked
920
+ . into_iter ( )
921
+ . map ( |( _, id) | ( id, ComesFromAllowExpect :: No ) )
922
+ . collect ( ) ;
923
+ symbol_visitor. mark_live_symbols ( ) ;
924
+
925
+ ( rest_items_should_be_checked, unsolved_items) =
926
+ unsolved_items. into_iter ( ) . partition ( |& ( impl_id, local_def_id) | {
927
+ symbol_visitor. item_should_be_checked ( impl_id, local_def_id)
928
+ } ) ;
929
+ }
873
930
874
931
( symbol_visitor. live_symbols , symbol_visitor. ignored_derived_traits )
875
932
}
@@ -1112,6 +1169,7 @@ impl<'tcx> DeadVisitor<'tcx> {
1112
1169
}
1113
1170
match self . tcx . def_kind ( def_id) {
1114
1171
DefKind :: AssocConst
1172
+ | DefKind :: AssocTy
1115
1173
| DefKind :: AssocFn
1116
1174
| DefKind :: Fn
1117
1175
| DefKind :: Static { .. }
@@ -1148,7 +1206,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
1148
1206
let def_kind = tcx. def_kind ( item. owner_id ) ;
1149
1207
1150
1208
let mut dead_codes = Vec :: new ( ) ;
1151
- // if we have diagnosed the trait, do not diagnose unused assoc items
1209
+ // only diagnose unused assoc items for inherient impl and used trait
1210
+ // for unused assoc items in impls of trait,
1211
+ // we have diagnosed them in the trait if they are unused,
1212
+ // for unused assoc items in unused trait,
1213
+ // we have diagnosed the unused trait
1152
1214
if matches ! ( def_kind, DefKind :: Impl { of_trait: false } )
1153
1215
|| ( def_kind == DefKind :: Trait && live_symbols. contains ( & item. owner_id . def_id ) )
1154
1216
{
0 commit comments