Skip to content

Commit f83ecd8

Browse files
committed
Refactor the two-phase check for impls and impl items
1 parent 0fc6f16 commit f83ecd8

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)