Skip to content

Commit ee9bcfc

Browse files
authored
Rollup merge of rust-lang#141407 - mu001999-contrib:dead-code/refactor, r=petrochenkov
Refactor the two-phase check for impls and impl items Refactor the two-phase dead code check to make the logic clearer and simpler: 1. adding assoc fn and impl into `unsolved_items` directly during the initial construction of the worklist 2. converge the logic of checking whether assoc fn and impl are used to `item_should_be_checked`, and the item is considered used only when its corresponding trait and Self adt are used This PR only refactors as much as possible to avoid affecting the original functions. However, due to the adjustment of the order of checks, the test results are slightly different, but overall, there is no regression problem Extracted from rust-lang#128637. r? petrochenkov
2 parents c17284e + f83ecd8 commit ee9bcfc

File tree

6 files changed

+128
-133
lines changed

6 files changed

+128
-133
lines changed

compiler/rustc_middle/src/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ rustc_queries! {
11371137
/// their respective impl (i.e., part of the derive macro)
11381138
query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx (
11391139
LocalDefIdSet,
1140-
LocalDefIdMap<Vec<(DefId, DefId)>>
1140+
LocalDefIdMap<FxIndexSet<(DefId, DefId)>>
11411141
) {
11421142
arena_cache
11431143
desc { "finding live symbols in crate" }

compiler/rustc_passes/src/dead.rs

Lines changed: 120 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ use std::mem;
88
use hir::ItemKind;
99
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
1010
use rustc_abi::FieldIdx;
11+
use rustc_data_structures::fx::FxIndexSet;
1112
use rustc_data_structures::unord::UnordSet;
1213
use rustc_errors::MultiSpan;
1314
use rustc_hir::def::{CtorOf, DefKind, Res};
1415
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1516
use rustc_hir::intravisit::{self, Visitor};
16-
use rustc_hir::{self as hir, Node, PatKind, TyKind};
17+
use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind};
1718
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1819
use rustc_middle::middle::privacy::Level;
1920
use rustc_middle::query::Providers;
@@ -44,15 +45,20 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4445
)
4546
}
4647

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
48+
/// Returns the local def id of the ADT if the given ty refers to a local one.
49+
fn local_adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option<LocalDefId> {
50+
match ty.kind {
51+
TyKind::Path(QPath::Resolved(_, path)) => {
52+
if let Res::Def(def_kind, def_id) = path.res
53+
&& let Some(local_def_id) = def_id.as_local()
54+
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
55+
{
56+
Some(local_def_id)
57+
} else {
58+
None
59+
}
60+
}
61+
_ => None,
5662
}
5763
}
5864

@@ -78,7 +84,7 @@ struct MarkSymbolVisitor<'tcx> {
7884
// maps from ADTs to ignored derived traits (e.g. Debug and Clone)
7985
// and the span of their respective impl (i.e., part of the derive
8086
// macro)
81-
ignored_derived_traits: LocalDefIdMap<Vec<(DefId, DefId)>>,
87+
ignored_derived_traits: LocalDefIdMap<FxIndexSet<(DefId, DefId)>>,
8288
}
8389

8490
impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -360,7 +366,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
360366
&& let Some(fn_sig) =
361367
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
362368
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
363-
&& let TyKind::Path(hir::QPath::Resolved(_, path)) =
369+
&& let TyKind::Path(QPath::Resolved(_, path)) =
364370
self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind
365371
&& let Res::Def(def_kind, did) = path.res
366372
{
@@ -388,7 +394,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
388394
self.ignored_derived_traits
389395
.entry(adt_def_id)
390396
.or_default()
391-
.push((trait_of, impl_of));
397+
.insert((trait_of, impl_of));
392398
}
393399
return true;
394400
}
@@ -420,51 +426,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
420426
intravisit::walk_item(self, item)
421427
}
422428
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);
429+
hir::ItemKind::Trait(.., trait_item_refs) => {
430+
// mark assoc ty live if the trait is live
431+
for trait_item in trait_item_refs {
432+
if matches!(trait_item.kind, hir::AssocItemKind::Type) {
433+
self.check_def_id(trait_item.id.owner_id.to_def_id());
432434
}
433435
}
434-
435436
intravisit::walk_item(self, item)
436437
}
437438
_ => intravisit::walk_item(self, item),
438439
},
439440
Node::TraitItem(trait_item) => {
440-
// mark corresponding ImplTerm live
441+
// mark the trait live
441442
let trait_item_id = trait_item.owner_id.to_def_id();
442443
if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) {
443-
// mark the trait live
444444
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-
}
468445
}
469446
intravisit::walk_trait_item(self, trait_item);
470447
}
@@ -508,48 +485,58 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
508485
}
509486
}
510487

511-
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
512-
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)
516-
});
517-
518-
while !ready.is_empty() {
519-
self.worklist =
520-
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
521-
self.mark_live_symbols();
522-
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)
526-
});
488+
/// Returns whether `local_def_id` is potentially alive or not.
489+
/// `local_def_id` points to an impl or an impl item,
490+
/// both impl and impl item that may be passed to this function are of a trait,
491+
/// and added into the unsolved_items during `create_and_seed_worklist`
492+
fn check_impl_or_impl_item_live(
493+
&mut self,
494+
impl_id: hir::ItemId,
495+
local_def_id: LocalDefId,
496+
) -> bool {
497+
if self.should_ignore_item(local_def_id.to_def_id()) {
498+
return false;
527499
}
528-
}
529500

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);
501+
let trait_def_id = match self.tcx.def_kind(local_def_id) {
502+
// assoc impl items of traits are live if the corresponding trait items are live
503+
DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id,
504+
// impl items are live if the corresponding traits are live
505+
DefKind::Impl { of_trait: true } => self
506+
.tcx
507+
.impl_trait_ref(impl_id.owner_id.def_id)
508+
.and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)),
509+
_ => None,
510+
};
511+
512+
if let Some(trait_def_id) = trait_def_id {
513+
if let Some(trait_def_id) = trait_def_id.as_local()
514+
&& !self.live_symbols.contains(&trait_def_id)
515+
{
516+
return false;
541517
}
542518

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()
519+
// FIXME: legacy logic to check whether the function may construct `Self`,
520+
// this can be removed after supporting marking ADTs appearing in patterns
521+
// as live, then we can check private impls of public traits directly
522+
if let Some(fn_sig) =
523+
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
524+
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
525+
&& self.tcx.visibility(trait_def_id).is_public()
545526
{
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);
527+
return true;
550528
}
551529
}
552-
false
530+
531+
// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
532+
if let Some(local_def_id) =
533+
local_adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty)
534+
&& !self.live_symbols.contains(&local_def_id)
535+
{
536+
return false;
537+
}
538+
539+
true
553540
}
554541
}
555542

@@ -584,7 +571,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
584571

585572
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
586573
match expr.kind {
587-
hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
574+
hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => {
588575
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
589576
self.handle_res(res);
590577
}
@@ -738,7 +725,7 @@ fn check_item<'tcx>(
738725
tcx: TyCtxt<'tcx>,
739726
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
740727
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
741-
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
728+
unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
742729
id: hir::ItemId,
743730
) {
744731
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -764,41 +751,33 @@ fn check_item<'tcx>(
764751
}
765752
}
766753
DefKind::Impl { of_trait } => {
767-
// get DefIds from another query
768-
let local_def_ids = tcx
769-
.associated_item_def_ids(id.owner_id)
770-
.iter()
771-
.filter_map(|def_id| def_id.as_local());
772-
773-
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir_item(id).expect_impl().self_ty);
774-
775-
// And we access the Map here to get HirId from LocalDefId
776-
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-
}
754+
if let Some(comes_from_allow) =
755+
has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
756+
{
757+
worklist.push((id.owner_id.def_id, comes_from_allow));
758+
} else if of_trait {
759+
unsolved_items.push((id, id.owner_id.def_id));
760+
}
785761

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-
{
794-
worklist.push((local_def_id, ComesFromAllowExpect::No));
795-
} else if let Some(comes_from_allow) =
796-
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
762+
for def_id in tcx.associated_item_def_ids(id.owner_id) {
763+
let local_def_id = def_id.expect_local();
764+
765+
if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id)
797766
{
798767
worklist.push((local_def_id, comes_from_allow));
799768
} else if of_trait {
800-
// private method || public method not constructs self
801-
unsolved_impl_items.push((id, local_def_id));
769+
// FIXME: This condition can be removed
770+
// if we support dead check for assoc consts and tys.
771+
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
772+
worklist.push((local_def_id, ComesFromAllowExpect::No));
773+
} else {
774+
// We only care about associated items of traits,
775+
// because they cannot be visited directly,
776+
// so we later mark them as live if their corresponding traits
777+
// or trait items and self types are both live,
778+
// but inherent associated items can be visited and marked directly.
779+
unsolved_items.push((id, local_def_id));
780+
}
802781
}
803782
}
804783
}
@@ -892,8 +871,8 @@ fn create_and_seed_worklist(
892871
fn live_symbols_and_ignored_derived_traits(
893872
tcx: TyCtxt<'_>,
894873
(): (),
895-
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
896-
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
874+
) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<(DefId, DefId)>>) {
875+
let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx);
897876
let mut symbol_visitor = MarkSymbolVisitor {
898877
worklist,
899878
tcx,
@@ -907,7 +886,22 @@ fn live_symbols_and_ignored_derived_traits(
907886
ignored_derived_traits: Default::default(),
908887
};
909888
symbol_visitor.mark_live_symbols();
910-
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
889+
let mut items_to_check;
890+
(items_to_check, unsolved_items) =
891+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
892+
symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id)
893+
});
894+
895+
while !items_to_check.is_empty() {
896+
symbol_visitor.worklist =
897+
items_to_check.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
898+
symbol_visitor.mark_live_symbols();
899+
900+
(items_to_check, unsolved_items) =
901+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
902+
symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id)
903+
});
904+
}
911905

912906
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
913907
}
@@ -921,7 +915,7 @@ struct DeadItem {
921915
struct DeadVisitor<'tcx> {
922916
tcx: TyCtxt<'tcx>,
923917
live_symbols: &'tcx LocalDefIdSet,
924-
ignored_derived_traits: &'tcx LocalDefIdMap<Vec<(DefId, DefId)>>,
918+
ignored_derived_traits: &'tcx LocalDefIdMap<FxIndexSet<(DefId, DefId)>>,
925919
}
926920

927921
enum ShouldWarnAboutField {
@@ -1188,19 +1182,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11881182
let def_kind = tcx.def_kind(item.owner_id);
11891183

11901184
let mut dead_codes = Vec::new();
1191-
// if we have diagnosed the trait, do not diagnose unused methods
1192-
if matches!(def_kind, DefKind::Impl { .. })
1185+
// Only diagnose unused assoc items in inherient impl and used trait,
1186+
// for unused assoc items in impls of trait,
1187+
// we have diagnosed them in the trait if they are unused,
1188+
// for unused assoc items in unused trait,
1189+
// we have diagnosed the unused trait.
1190+
if matches!(def_kind, DefKind::Impl { of_trait: false })
11931191
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11941192
{
11951193
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1196-
// We have diagnosed unused methods in traits
1197-
if matches!(def_kind, DefKind::Impl { of_trait: true })
1198-
&& tcx.def_kind(def_id) == DefKind::AssocFn
1199-
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1200-
{
1201-
continue;
1202-
}
1203-
12041194
if let Some(local_def_id) = def_id.as_local()
12051195
&& !visitor.is_live_code(local_def_id)
12061196
{

tests/ui/derives/clone-debug-dead-code.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ LL | struct D { f: () }
4040
| |
4141
| field in this struct
4242
|
43-
= note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
43+
= note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
4444

4545
error: field `f` is never read
4646
--> $DIR/clone-debug-dead-code.rs:21:12

0 commit comments

Comments
 (0)