Skip to content

Commit 2b7b7a0

Browse files
committed
Refactor the two-phase check for impls and impl items, makes the logic cleaner
1 parent 80eb5a8 commit 2b7b7a0

File tree

1 file changed

+75
-110
lines changed

1 file changed

+75
-110
lines changed

compiler/rustc_passes/src/dead.rs

+75-110
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1717
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1818
use rustc_middle::middle::privacy::Level;
1919
use rustc_middle::query::Providers;
20-
use rustc_middle::ty::{self, TyCtxt};
20+
use rustc_middle::ty::{self, AssocItemContainer, Ty, TyCtxt};
2121
use rustc_middle::{bug, span_bug};
2222
use rustc_session::lint;
2323
use rustc_session::lint::builtin::DEAD_CODE;
@@ -44,15 +44,18 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4444
)
4545
}
4646

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

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

512-
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
477+
fn solve_rest_items(&mut self, mut unsolved_items: Vec<(hir::ItemId, LocalDefId)>) {
513478
let mut ready;
514-
(ready, unsolved_impl_items) =
515-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
516-
self.impl_item_with_used_self(impl_id, impl_item_id)
479+
(ready, unsolved_items) =
480+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
481+
self.item_should_be_checked(impl_id, local_def_id)
517482
});
518483

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

524-
(ready, unsolved_impl_items) =
525-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
526-
self.impl_item_with_used_self(impl_id, impl_item_id)
489+
(ready, unsolved_items) =
490+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
491+
self.item_should_be_checked(impl_id, local_def_id)
527492
});
528493
}
529494
}
530495

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

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

@@ -740,7 +712,7 @@ fn check_item<'tcx>(
740712
tcx: TyCtxt<'tcx>,
741713
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
742714
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
743-
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
715+
unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
744716
id: hir::ItemId,
745717
) {
746718
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -772,35 +744,24 @@ fn check_item<'tcx>(
772744
.iter()
773745
.filter_map(|def_id| def_id.as_local());
774746

775-
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
747+
if let Some(comes_from_allow) =
748+
has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
749+
{
750+
worklist.push((id.owner_id.def_id, comes_from_allow));
751+
} else if of_trait {
752+
unsolved_items.push((id, id.owner_id.def_id));
753+
}
776754

777755
// And we access the Map here to get HirId from LocalDefId
778756
for local_def_id in local_def_ids {
779-
// check the function may construct Self
780-
let mut may_construct_self = false;
781-
if let Some(fn_sig) =
782-
tcx.hir().fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id))
783-
{
784-
may_construct_self =
785-
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
786-
}
787-
788-
// for trait impl blocks,
789-
// mark the method live if the self_ty is public,
790-
// or the method is public and may construct self
791-
if of_trait
792-
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
793-
|| tcx.visibility(local_def_id).is_public()
794-
&& (ty_is_pub || may_construct_self))
795-
{
757+
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
796758
worklist.push((local_def_id, ComesFromAllowExpect::No));
797759
} else if let Some(comes_from_allow) =
798760
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
799761
{
800762
worklist.push((local_def_id, comes_from_allow));
801763
} else if of_trait {
802-
// private method || public method not constructs self
803-
unsolved_impl_items.push((id, local_def_id));
764+
unsolved_items.push((id, local_def_id));
804765
}
805766
}
806767
}
@@ -866,6 +827,18 @@ fn create_and_seed_worklist(
866827
effective_vis
867828
.is_public_at_level(Level::Reachable)
868829
.then_some(id)
830+
.filter(|&id|
831+
// checks impls and impl-items later
832+
match tcx.def_kind(id) {
833+
DefKind::Impl { of_trait } => !of_trait,
834+
DefKind::AssocFn => {
835+
// still check public trait items, and impl items not of trait
836+
let assoc_item = tcx.associated_item(id);
837+
!matches!(assoc_item.container, AssocItemContainer::ImplContainer)
838+
|| assoc_item.trait_item_def_id.is_none()
839+
},
840+
_ => true
841+
})
869842
.map(|id| (id, ComesFromAllowExpect::No))
870843
})
871844
// Seed entry point
@@ -895,7 +868,7 @@ fn live_symbols_and_ignored_derived_traits(
895868
tcx: TyCtxt<'_>,
896869
(): (),
897870
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
898-
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
871+
let (worklist, struct_constructors, unsolved_items) = create_and_seed_worklist(tcx);
899872
let mut symbol_visitor = MarkSymbolVisitor {
900873
worklist,
901874
tcx,
@@ -909,7 +882,7 @@ fn live_symbols_and_ignored_derived_traits(
909882
ignored_derived_traits: Default::default(),
910883
};
911884
symbol_visitor.mark_live_symbols();
912-
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
885+
symbol_visitor.solve_rest_items(unsolved_items);
913886

914887
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
915888
}
@@ -1185,19 +1158,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11851158
let def_kind = tcx.def_kind(item.owner_id);
11861159

11871160
let mut dead_codes = Vec::new();
1188-
// if we have diagnosed the trait, do not diagnose unused methods
1189-
if matches!(def_kind, DefKind::Impl { .. })
1161+
// if we have diagnosed the trait, do not diagnose unused assoc items
1162+
if matches!(def_kind, DefKind::Impl { of_trait: false })
11901163
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11911164
{
11921165
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1193-
// We have diagnosed unused methods in traits
1194-
if matches!(def_kind, DefKind::Impl { of_trait: true })
1195-
&& tcx.def_kind(def_id) == DefKind::AssocFn
1196-
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1197-
{
1198-
continue;
1199-
}
1200-
12011166
if let Some(local_def_id) = def_id.as_local()
12021167
&& !visitor.is_live_code(local_def_id)
12031168
{

0 commit comments

Comments
 (0)