Skip to content

Commit a22c560

Browse files
committed
Refactor the two-phase check for impls and impl items, makes the logic cleaner
1 parent 7028d93 commit a22c560

File tree

1 file changed

+75
-110
lines changed

1 file changed

+75
-110
lines changed

compiler/rustc_passes/src/dead.rs

Lines changed: 75 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1818
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1919
use rustc_middle::middle::privacy::Level;
2020
use rustc_middle::query::Providers;
21-
use rustc_middle::ty::{self, TyCtxt};
21+
use rustc_middle::ty::{self, AssocItemContainer, Ty, TyCtxt};
2222
use rustc_middle::{bug, span_bug};
2323
use rustc_session::lint;
2424
use rustc_session::lint::builtin::DEAD_CODE;
@@ -45,15 +45,18 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4545
)
4646
}
4747

48-
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
49-
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
50-
&& let Res::Def(def_kind, def_id) = path.res
51-
&& def_id.is_local()
52-
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
53-
{
54-
tcx.visibility(def_id).is_public()
55-
} else {
56-
true
48+
fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
49+
match ty.kind {
50+
TyKind::Path(hir::QPath::Resolved(_, path)) => {
51+
if let Res::Def(def_kind, def_id) = path.res
52+
&& let Some(local_def_id) = def_id.as_local()
53+
{
54+
Some((local_def_id, def_kind))
55+
} else {
56+
None
57+
}
58+
}
59+
_ => None,
5760
}
5861
}
5962

@@ -421,22 +424,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
421424
intravisit::walk_item(self, item)
422425
}
423426
hir::ItemKind::ForeignMod { .. } => {}
424-
hir::ItemKind::Trait(..) => {
425-
for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
426-
if let Some(local_def_id) = impl_def_id.as_local()
427-
&& let ItemKind::Impl(impl_ref) =
428-
self.tcx.hir().expect_item(local_def_id).kind
429-
{
430-
// skip items
431-
// mark dependent traits live
432-
intravisit::walk_generics(self, impl_ref.generics);
433-
// mark dependent parameters live
434-
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
435-
}
436-
}
437-
438-
intravisit::walk_item(self, item)
439-
}
440427
_ => intravisit::walk_item(self, item),
441428
},
442429
Node::TraitItem(trait_item) => {
@@ -445,30 +432,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
445432
if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) {
446433
// mark the trait live
447434
self.check_def_id(trait_id);
448-
449-
for impl_id in self.tcx.all_impls(trait_id) {
450-
if let Some(local_impl_id) = impl_id.as_local()
451-
&& let ItemKind::Impl(impl_ref) =
452-
self.tcx.hir().expect_item(local_impl_id).kind
453-
{
454-
if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
455-
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
456-
{
457-
// skip methods of private ty,
458-
// they would be solved in `solve_rest_impl_items`
459-
continue;
460-
}
461-
462-
// mark self_ty live
463-
intravisit::walk_ty(self, impl_ref.self_ty);
464-
if let Some(&impl_item_id) =
465-
self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id)
466-
{
467-
self.check_def_id(impl_item_id);
468-
}
469-
}
470-
}
471435
}
436+
472437
intravisit::walk_trait_item(self, trait_item);
473438
}
474439
Node::ImplItem(impl_item) => {
@@ -511,48 +476,55 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
511476
}
512477
}
513478

514-
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
479+
fn solve_rest_items(&mut self, mut unsolved_items: Vec<(hir::ItemId, LocalDefId)>) {
515480
let mut ready;
516-
(ready, unsolved_impl_items) =
517-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
518-
self.impl_item_with_used_self(impl_id, impl_item_id)
481+
(ready, unsolved_items) =
482+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
483+
self.item_should_be_checked(impl_id, local_def_id)
519484
});
520485

521486
while !ready.is_empty() {
522487
self.worklist =
523488
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
524489
self.mark_live_symbols();
525490

526-
(ready, unsolved_impl_items) =
527-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
528-
self.impl_item_with_used_self(impl_id, impl_item_id)
491+
(ready, unsolved_items) =
492+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
493+
self.item_should_be_checked(impl_id, local_def_id)
529494
});
530495
}
531496
}
532497

533-
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
534-
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
535-
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
536-
&& let Res::Def(def_kind, def_id) = path.res
537-
&& let Some(local_def_id) = def_id.as_local()
538-
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
539-
{
540-
if self.tcx.visibility(impl_item_id).is_public() {
541-
// for the public method, we don't know the trait item is used or not,
542-
// so we mark the method live if the self is used
543-
return self.live_symbols.contains(&local_def_id);
544-
}
498+
fn item_should_be_checked(&mut self, impl_id: hir::ItemId, local_def_id: LocalDefId) -> bool {
499+
let trait_def_id = match self.tcx.def_kind(local_def_id) {
500+
// for assoc impl items of traits, we concern the corresponding trait items are used or not
501+
DefKind::AssocFn => self
502+
.tcx
503+
.associated_item(local_def_id)
504+
.trait_item_def_id
505+
.and_then(|def_id| def_id.as_local()),
506+
// for impl items, we concern the corresonding traits are used or not
507+
DefKind::Impl { of_trait: true } => self
508+
.tcx
509+
.impl_trait_ref(impl_id.owner_id.def_id)
510+
.and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
511+
_ => None,
512+
};
545513

546-
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
547-
&& let Some(local_id) = trait_item_id.as_local()
548-
{
549-
// for the private method, we can know the trait item is used or not,
550-
// so we mark the method live if the self is used and the trait item is used
551-
return self.live_symbols.contains(&local_id)
552-
&& self.live_symbols.contains(&local_def_id);
553-
}
514+
if !trait_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true) {
515+
return false;
554516
}
555-
false
517+
518+
// we only check the ty is used or not for ADTs defined locally
519+
let ty_def_id = adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty).and_then(
520+
|(local_def_id, def_kind)| {
521+
matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
522+
.then_some(local_def_id)
523+
},
524+
);
525+
526+
// the impl/impl item is used if the trait/trait item is used and the ty is used
527+
ty_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true)
556528
}
557529
}
558530

@@ -734,7 +706,7 @@ fn check_item<'tcx>(
734706
tcx: TyCtxt<'tcx>,
735707
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
736708
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
737-
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
709+
unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
738710
id: hir::ItemId,
739711
) {
740712
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -766,35 +738,24 @@ fn check_item<'tcx>(
766738
.iter()
767739
.filter_map(|def_id| def_id.as_local());
768740

769-
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
741+
if let Some(comes_from_allow) =
742+
has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
743+
{
744+
worklist.push((id.owner_id.def_id, comes_from_allow));
745+
} else if of_trait {
746+
unsolved_items.push((id, id.owner_id.def_id));
747+
}
770748

771749
// And we access the Map here to get HirId from LocalDefId
772750
for local_def_id in local_def_ids {
773-
// check the function may construct Self
774-
let mut may_construct_self = false;
775-
if let Some(fn_sig) =
776-
tcx.hir().fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id))
777-
{
778-
may_construct_self =
779-
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
780-
}
781-
782-
// for trait impl blocks,
783-
// mark the method live if the self_ty is public,
784-
// or the method is public and may construct self
785-
if of_trait
786-
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
787-
|| tcx.visibility(local_def_id).is_public()
788-
&& (ty_is_pub || may_construct_self))
789-
{
751+
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
790752
worklist.push((local_def_id, ComesFromAllowExpect::No));
791753
} else if let Some(comes_from_allow) =
792754
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
793755
{
794756
worklist.push((local_def_id, comes_from_allow));
795757
} else if of_trait {
796-
// private method || public method not constructs self
797-
unsolved_impl_items.push((id, local_def_id));
758+
unsolved_items.push((id, local_def_id));
798759
}
799760
}
800761
}
@@ -860,6 +821,18 @@ fn create_and_seed_worklist(
860821
effective_vis
861822
.is_public_at_level(Level::Reachable)
862823
.then_some(id)
824+
.filter(|&id|
825+
// checks impls and impl-items later
826+
match tcx.def_kind(id) {
827+
DefKind::Impl { of_trait } => !of_trait,
828+
DefKind::AssocFn => {
829+
// still check public trait items, and impl items not of trait
830+
let assoc_item = tcx.associated_item(id);
831+
!matches!(assoc_item.container, AssocItemContainer::ImplContainer)
832+
|| assoc_item.trait_item_def_id.is_none()
833+
},
834+
_ => true
835+
})
863836
.map(|id| (id, ComesFromAllowExpect::No))
864837
})
865838
// Seed entry point
@@ -889,7 +862,7 @@ fn live_symbols_and_ignored_derived_traits(
889862
tcx: TyCtxt<'_>,
890863
(): (),
891864
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
892-
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
865+
let (worklist, struct_constructors, unsolved_items) = create_and_seed_worklist(tcx);
893866
let mut symbol_visitor = MarkSymbolVisitor {
894867
worklist,
895868
tcx,
@@ -903,7 +876,7 @@ fn live_symbols_and_ignored_derived_traits(
903876
ignored_derived_traits: Default::default(),
904877
};
905878
symbol_visitor.mark_live_symbols();
906-
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
879+
symbol_visitor.solve_rest_items(unsolved_items);
907880

908881
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
909882
}
@@ -1179,19 +1152,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11791152
let def_kind = tcx.def_kind(item.owner_id);
11801153

11811154
let mut dead_codes = Vec::new();
1182-
// if we have diagnosed the trait, do not diagnose unused methods
1183-
if matches!(def_kind, DefKind::Impl { .. })
1155+
// if we have diagnosed the trait, do not diagnose unused assoc items
1156+
if matches!(def_kind, DefKind::Impl { of_trait: false })
11841157
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11851158
{
11861159
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1187-
// We have diagnosed unused methods in traits
1188-
if matches!(def_kind, DefKind::Impl { of_trait: true })
1189-
&& tcx.def_kind(def_id) == DefKind::AssocFn
1190-
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1191-
{
1192-
continue;
1193-
}
1194-
11951160
if let Some(local_def_id) = def_id.as_local()
11961161
&& !visitor.is_live_code(local_def_id)
11971162
{

0 commit comments

Comments
 (0)