Skip to content

Commit 27ddb1b

Browse files
committed
Refactor the two-phase check for impls and impl items, makes the logic cleaner
1 parent b6d74b5 commit 27ddb1b

File tree

1 file changed

+75
-108
lines changed

1 file changed

+75
-108
lines changed

compiler/rustc_passes/src/dead.rs

+75-108
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::{self as 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,20 +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.local_trait_impls(item.owner_id.def_id) {
425-
if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind
426-
{
427-
// skip items
428-
// mark dependent traits live
429-
intravisit::walk_generics(self, impl_ref.generics);
430-
// mark dependent parameters live
431-
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
432-
}
433-
}
434-
435-
intravisit::walk_item(self, item)
436-
}
437426
_ => intravisit::walk_item(self, item),
438427
},
439428
Node::TraitItem(trait_item) => {
@@ -442,30 +431,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
442431
if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) {
443432
// mark the trait live
444433
self.check_def_id(trait_id);
445-
446-
for impl_id in self.tcx.all_impls(trait_id) {
447-
if let Some(local_impl_id) = impl_id.as_local()
448-
&& let ItemKind::Impl(impl_ref) =
449-
self.tcx.hir_expect_item(local_impl_id).kind
450-
{
451-
if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
452-
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
453-
{
454-
// skip methods of private ty,
455-
// they would be solved in `solve_rest_impl_items`
456-
continue;
457-
}
458-
459-
// mark self_ty live
460-
intravisit::walk_unambig_ty(self, impl_ref.self_ty);
461-
if let Some(&impl_item_id) =
462-
self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id)
463-
{
464-
self.check_def_id(impl_item_id);
465-
}
466-
}
467-
}
468434
}
435+
469436
intravisit::walk_trait_item(self, trait_item);
470437
}
471438
Node::ImplItem(impl_item) => {
@@ -508,48 +475,55 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
508475
}
509476
}
510477

511-
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
478+
fn solve_rest_items(&mut self, mut unsolved_items: Vec<(hir::ItemId, LocalDefId)>) {
512479
let mut ready;
513-
(ready, unsolved_impl_items) =
514-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
515-
self.impl_item_with_used_self(impl_id, impl_item_id)
480+
(ready, unsolved_items) =
481+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
482+
self.item_should_be_checked(impl_id, local_def_id)
516483
});
517484

518485
while !ready.is_empty() {
519486
self.worklist =
520487
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
521488
self.mark_live_symbols();
522489

523-
(ready, unsolved_impl_items) =
524-
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
525-
self.impl_item_with_used_self(impl_id, impl_item_id)
490+
(ready, unsolved_items) =
491+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
492+
self.item_should_be_checked(impl_id, local_def_id)
526493
});
527494
}
528495
}
529496

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

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

@@ -738,7 +712,7 @@ fn check_item<'tcx>(
738712
tcx: TyCtxt<'tcx>,
739713
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
740714
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
741-
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
715+
unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
742716
id: hir::ItemId,
743717
) {
744718
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -770,35 +744,24 @@ fn check_item<'tcx>(
770744
.iter()
771745
.filter_map(|def_id| def_id.as_local());
772746

773-
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+
}
774754

775755
// And we access the Map here to get HirId from LocalDefId
776756
for local_def_id in local_def_ids {
777-
// check the function may construct Self
778-
let mut may_construct_self = false;
779-
if let Some(fn_sig) =
780-
tcx.hir_fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id))
781-
{
782-
may_construct_self =
783-
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
784-
}
785-
786-
// for trait impl blocks,
787-
// mark the method live if the self_ty is public,
788-
// or the method is public and may construct self
789-
if of_trait
790-
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
791-
|| tcx.visibility(local_def_id).is_public()
792-
&& (ty_is_pub || may_construct_self))
793-
{
757+
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
794758
worklist.push((local_def_id, ComesFromAllowExpect::No));
795759
} else if let Some(comes_from_allow) =
796760
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
797761
{
798762
worklist.push((local_def_id, comes_from_allow));
799763
} else if of_trait {
800-
// private method || public method not constructs self
801-
unsolved_impl_items.push((id, local_def_id));
764+
unsolved_items.push((id, local_def_id));
802765
}
803766
}
804767
}
@@ -864,6 +827,18 @@ fn create_and_seed_worklist(
864827
effective_vis
865828
.is_public_at_level(Level::Reachable)
866829
.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::Impl)
838+
|| assoc_item.trait_item_def_id.is_none()
839+
},
840+
_ => true
841+
})
867842
.map(|id| (id, ComesFromAllowExpect::No))
868843
})
869844
// Seed entry point
@@ -893,7 +868,7 @@ fn live_symbols_and_ignored_derived_traits(
893868
tcx: TyCtxt<'_>,
894869
(): (),
895870
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
896-
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
871+
let (worklist, struct_constructors, unsolved_items) = create_and_seed_worklist(tcx);
897872
let mut symbol_visitor = MarkSymbolVisitor {
898873
worklist,
899874
tcx,
@@ -907,7 +882,7 @@ fn live_symbols_and_ignored_derived_traits(
907882
ignored_derived_traits: Default::default(),
908883
};
909884
symbol_visitor.mark_live_symbols();
910-
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
885+
symbol_visitor.solve_rest_items(unsolved_items);
911886

912887
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
913888
}
@@ -1186,19 +1161,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11861161
let def_kind = tcx.def_kind(item.owner_id);
11871162

11881163
let mut dead_codes = Vec::new();
1189-
// if we have diagnosed the trait, do not diagnose unused methods
1190-
if matches!(def_kind, DefKind::Impl { .. })
1164+
// if we have diagnosed the trait, do not diagnose unused assoc items
1165+
if matches!(def_kind, DefKind::Impl { of_trait: false })
11911166
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11921167
{
11931168
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1194-
// We have diagnosed unused methods in traits
1195-
if matches!(def_kind, DefKind::Impl { of_trait: true })
1196-
&& tcx.def_kind(def_id) == DefKind::AssocFn
1197-
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1198-
{
1199-
continue;
1200-
}
1201-
12021169
if let Some(local_def_id) = def_id.as_local()
12031170
&& !visitor.is_live_code(local_def_id)
12041171
{

0 commit comments

Comments
 (0)