Skip to content

Commit c33f54d

Browse files
committed
Refactor the two-phase check for impls and impl items
1 parent 95a2212 commit c33f54d

File tree

7 files changed

+138
-122
lines changed

7 files changed

+138
-122
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: 112 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ 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};
@@ -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 and def kind of the adt,
49+
/// if the given ty refers to one local adt definition
50+
fn adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
51+
match ty.kind {
52+
TyKind::Path(hir::QPath::Resolved(_, path)) => {
53+
if let Res::Def(def_kind, def_id) = path.res
54+
&& let Some(local_def_id) = def_id.as_local()
55+
{
56+
Some((local_def_id, def_kind))
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> {
@@ -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,57 @@ 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 an impl or impl item should be checked or not
489+
/// `impl_id` is the `ItemId` of the impl or the impl item belongs to,
490+
/// `local_def_id` may point to the impl or the impl item,
491+
/// both impl and impl item that may pass to this function are of trait,
492+
/// and added into the unsolved_items during `create_and_seed_worklist`
493+
fn item_should_be_checked(&mut self, impl_id: hir::ItemId, local_def_id: LocalDefId) -> bool {
494+
if self.should_ignore_item(local_def_id.to_def_id()) {
495+
return false;
527496
}
528-
}
529497

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

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()
516+
// FIXME: legacy logic to check the fn may construct self,
517+
// this can be removed after supporting marking adt appears
518+
// in patterns as live, then we can check private impls of
519+
// public traits directly
520+
if let Some(fn_sig) =
521+
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
522+
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
523+
&& self.tcx.visibility(trait_def_id).is_public()
545524
{
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);
525+
return true;
550526
}
551527
}
552-
false
528+
529+
// the impl/impl item is used if the trait/trait item is used and the ty is used
530+
if let Some((local_def_id, def_kind)) =
531+
adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty)
532+
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
533+
&& !self.live_symbols.contains(&local_def_id)
534+
{
535+
return false;
536+
}
537+
538+
true
553539
}
554540
}
555541

@@ -738,7 +724,7 @@ fn check_item<'tcx>(
738724
tcx: TyCtxt<'tcx>,
739725
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
740726
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
741-
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
727+
unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>,
742728
id: hir::ItemId,
743729
) {
744730
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -765,40 +751,36 @@ fn check_item<'tcx>(
765751
}
766752
DefKind::Impl { of_trait } => {
767753
// get DefIds from another query
768-
let local_def_ids = tcx
754+
let associated_item_def_ids = tcx
769755
.associated_item_def_ids(id.owner_id)
770756
.iter()
771757
.filter_map(|def_id| def_id.as_local());
772758

773-
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir_item(id).expect_impl().self_ty);
759+
if let Some(comes_from_allow) =
760+
has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
761+
{
762+
worklist.push((id.owner_id.def_id, comes_from_allow));
763+
} else if of_trait {
764+
unsolved_items.push((id, id.owner_id.def_id));
765+
}
774766

775767
// 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-
}
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-
{
768+
for local_def_id in associated_item_def_ids {
769+
// FIXME: this condition can be removed
770+
// if we support dead check for assoc consts and tys
771+
if of_trait && !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
794772
worklist.push((local_def_id, ComesFromAllowExpect::No));
795773
} else if let Some(comes_from_allow) =
796774
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
797775
{
798776
worklist.push((local_def_id, comes_from_allow));
799777
} else if of_trait {
800-
// private method || public method not constructs self
801-
unsolved_impl_items.push((id, local_def_id));
778+
// we only care about assoc items of trait,
779+
// because they cannot be visited directly
780+
// so we mark them based on the trait/trait item
781+
// and self ty are checked first and both live,
782+
// but inherent assoc items can be visited and marked directly
783+
unsolved_items.push((id, local_def_id));
802784
}
803785
}
804786
}
@@ -892,8 +874,8 @@ fn create_and_seed_worklist(
892874
fn live_symbols_and_ignored_derived_traits(
893875
tcx: TyCtxt<'_>,
894876
(): (),
895-
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
896-
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
877+
) -> (LocalDefIdSet, LocalDefIdMap<FxIndexSet<(DefId, DefId)>>) {
878+
let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx);
897879
let mut symbol_visitor = MarkSymbolVisitor {
898880
worklist,
899881
tcx,
@@ -907,7 +889,24 @@ fn live_symbols_and_ignored_derived_traits(
907889
ignored_derived_traits: Default::default(),
908890
};
909891
symbol_visitor.mark_live_symbols();
910-
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
892+
let mut rest_items_should_be_checked;
893+
(rest_items_should_be_checked, unsolved_items) =
894+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
895+
symbol_visitor.item_should_be_checked(impl_id, local_def_id)
896+
});
897+
898+
while !rest_items_should_be_checked.is_empty() {
899+
symbol_visitor.worklist = rest_items_should_be_checked
900+
.into_iter()
901+
.map(|(_, id)| (id, ComesFromAllowExpect::No))
902+
.collect();
903+
symbol_visitor.mark_live_symbols();
904+
905+
(rest_items_should_be_checked, unsolved_items) =
906+
unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| {
907+
symbol_visitor.item_should_be_checked(impl_id, local_def_id)
908+
});
909+
}
911910

912911
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
913912
}
@@ -921,7 +920,7 @@ struct DeadItem {
921920
struct DeadVisitor<'tcx> {
922921
tcx: TyCtxt<'tcx>,
923922
live_symbols: &'tcx LocalDefIdSet,
924-
ignored_derived_traits: &'tcx LocalDefIdMap<Vec<(DefId, DefId)>>,
923+
ignored_derived_traits: &'tcx LocalDefIdMap<FxIndexSet<(DefId, DefId)>>,
925924
}
926925

927926
enum ShouldWarnAboutField {
@@ -1188,19 +1187,14 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11881187
let def_kind = tcx.def_kind(item.owner_id);
11891188

11901189
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 { .. })
1190+
// only diagnose unused assoc items for inherient impl and used trait
1191+
// for unused assoc items in impls of trait, we have diagnosed them in
1192+
// the trait if they are unused, for unused assoc items in unused trait,
1193+
// we have diagnosed the unused trait
1194+
if matches!(def_kind, DefKind::Impl { of_trait: false })
11931195
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11941196
{
11951197
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-
12041198
if let Some(local_def_id) = def_id.as_local()
12051199
&& !visitor.is_live_code(local_def_id)
12061200
{

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

tests/ui/deriving/deriving-in-macro.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
//@ run-pass
21
#![allow(non_camel_case_types)]
2+
#![deny(dead_code)]
33

44
macro_rules! define_vec {
55
() => (
66
mod foo {
77
#[derive(PartialEq)]
8-
pub struct bar;
8+
pub struct bar; //~ ERROR struct `bar` is never constructed
99
}
1010
)
1111
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: struct `bar` is never constructed
2+
--> $DIR/deriving-in-macro.rs:8:24
3+
|
4+
LL | pub struct bar;
5+
| ^^^
6+
...
7+
LL | define_vec![];
8+
| ------------- in this macro invocation
9+
|
10+
note: the lint level is defined here
11+
--> $DIR/deriving-in-macro.rs:2:9
12+
|
13+
LL | #![deny(dead_code)]
14+
| ^^^^^^^^^
15+
= note: this error originates in the macro `define_vec` (in Nightly builds, run with -Z macro-backtrace for more info)
16+
17+
error: aborting due to 1 previous error
18+

0 commit comments

Comments
 (0)