Skip to content

Commit f1229af

Browse files
committed
Propagate #[allow(dead_code)] on adts to their inherent impls
1 parent 1f08727 commit f1229af

2 files changed

Lines changed: 150 additions & 75 deletions

File tree

compiler/rustc_passes/src/dead.rs

Lines changed: 126 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct MarkSymbolVisitor<'tcx> {
129129
// macro)
130130
ignored_derived_traits: LocalDefIdMap<FxIndexSet<DefId>>,
131131
propagated_comes_from_allow_expect: ComesFromAllowExpect,
132+
unsolved_items: Vec<LocalDefId>,
132133
}
133134

134135
impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -419,6 +420,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
419420

420421
if !self.scanned.insert((id, propagated)) {
421422
continue;
423+
} else if propagated == ComesFromAllowExpect::No {
424+
self.scanned.insert((id, ComesFromAllowExpect::Yes));
422425
}
423426

424427
// Avoid accessing the HIR for the synthesized associated type generated for RPITITs.
@@ -544,13 +547,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
544547
/// `local_def_id` points to an impl or an impl item,
545548
/// both impl and impl item that may be passed to this function are of a trait,
546549
/// and added into the unsolved_items during `create_and_seed_worklist`
547-
fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool {
550+
fn check_impl_or_impl_item_live(
551+
&self,
552+
local_def_id: LocalDefId,
553+
defer_seeds_come_from_allow: bool,
554+
) -> Option<ComesFromAllowExpect> {
548555
let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) {
549556
// assoc impl items of traits are live if the corresponding trait items are live
550557
DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::AssocFn => {
551-
let trait_item_id =
558+
let trait_def_id =
552559
self.tcx.trait_item_of(local_def_id).and_then(|def_id| def_id.as_local());
553-
(self.tcx.local_parent(local_def_id), trait_item_id)
560+
(self.tcx.local_parent(local_def_id), trait_def_id)
554561
}
555562
// impl items are live if the corresponding traits are live
556563
DefKind::Impl { of_trait: true } => {
@@ -559,10 +566,19 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
559566
_ => bug!(),
560567
};
561568

562-
if let Some(trait_def_id) = trait_def_id
563-
&& !self.live_symbols.contains(&trait_def_id)
564-
{
565-
return false;
569+
let mut trait_comes_from_allow = None;
570+
if let Some(trait_def_id) = trait_def_id {
571+
if defer_seeds_come_from_allow {
572+
if !self.live_symbols.contains(&trait_def_id) {
573+
return None;
574+
}
575+
} else {
576+
if !self.live_symbols.contains(&trait_def_id) {
577+
return has_allow_dead_code_or_lang_attr(self.tcx, trait_def_id);
578+
}
579+
580+
trait_comes_from_allow = has_allow_dead_code_or_lang_attr(self.tcx, trait_def_id);
581+
}
566582
}
567583

568584
// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
@@ -571,10 +587,70 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
571587
&& let Some(adt_def_id) = adt.did().as_local()
572588
&& !self.live_symbols.contains(&adt_def_id)
573589
{
574-
return false;
590+
if defer_seeds_come_from_allow {
591+
return None;
592+
} else {
593+
return trait_comes_from_allow
594+
.or_else(|| has_allow_dead_code_or_lang_attr(self.tcx, adt_def_id));
595+
}
575596
}
576597

577-
true
598+
Some(ComesFromAllowExpect::No)
599+
}
600+
601+
fn collect_live_items_from_unsolved_items(
602+
&mut self,
603+
defer_seeds_come_from_allow: bool,
604+
) -> Vec<(LocalDefId, ComesFromAllowExpect)> {
605+
let mut unsolved_items = std::mem::take(&mut self.unsolved_items);
606+
let mut items_to_check = vec![];
607+
unsolved_items.retain(|&def_id| {
608+
let Some(comes_from_allow) =
609+
self.check_impl_or_impl_item_live(def_id, defer_seeds_come_from_allow)
610+
else {
611+
return true;
612+
};
613+
614+
items_to_check.push((def_id, comes_from_allow));
615+
false
616+
});
617+
self.unsolved_items = unsolved_items;
618+
items_to_check
619+
}
620+
621+
fn mark_live_symbols_and_ignored_derived_traits(
622+
&mut self,
623+
defer_seeds_come_from_allow: bool,
624+
) -> Result<(), ErrorGuaranteed> {
625+
if let ControlFlow::Break(guar) = self.mark_live_symbols() {
626+
return Err(guar);
627+
}
628+
629+
// We have marked the primary seeds as live. We now need to process unsolved items from traits
630+
// and trait impls: add them to the work list if the trait or the implemented type is live.
631+
let mut items_to_check =
632+
self.collect_live_items_from_unsolved_items(defer_seeds_come_from_allow);
633+
634+
while !items_to_check.is_empty() {
635+
self.worklist.extend(items_to_check.into_iter().map(|(id, comes_from_allow)| {
636+
let own = if defer_seeds_come_from_allow {
637+
ComesFromAllowExpect::No
638+
} else {
639+
has_allow_dead_code_or_lang_attr(self.tcx, id)
640+
.unwrap_or(ComesFromAllowExpect::No)
641+
};
642+
643+
WorkItem { id, propagated: comes_from_allow, own }
644+
}));
645+
if let ControlFlow::Break(guar) = self.mark_live_symbols() {
646+
return Err(guar);
647+
}
648+
649+
items_to_check =
650+
self.collect_live_items_from_unsolved_items(defer_seeds_come_from_allow);
651+
}
652+
653+
Ok(())
578654
}
579655
}
580656

@@ -843,21 +919,22 @@ fn maybe_record_as_seed<'tcx>(
843919
if allow_dead_code.is_none() {
844920
let parent = tcx.local_parent(owner_id.def_id);
845921
match tcx.def_kind(parent) {
846-
DefKind::Impl { of_trait: false } | DefKind::Trait => {}
847-
DefKind::Impl { of_trait: true } => {
848-
if let Some(trait_item_def_id) =
849-
tcx.associated_item(owner_id.def_id).trait_item_def_id()
850-
&& let Some(trait_item_local_def_id) = trait_item_def_id.as_local()
922+
DefKind::Trait => {}
923+
DefKind::Impl { of_trait: false } => {
924+
if let ty::Adt(adt, _) =
925+
tcx.type_of(parent).instantiate_identity().skip_normalization().kind()
926+
&& let Some(adt_def_id) = adt.did().as_local()
851927
&& let Some(comes_from_allow) =
852-
has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id)
928+
has_allow_dead_code_or_lang_attr(tcx, adt_def_id)
853929
{
854930
push_into_worklist(WorkItem {
855931
id: owner_id.def_id,
856932
propagated: comes_from_allow,
857-
own: comes_from_allow,
933+
own: ComesFromAllowExpect::No,
858934
});
859935
}
860-
936+
}
937+
DefKind::Impl { of_trait: true } => {
861938
// We only care about associated items of traits,
862939
// because they cannot be visited directly,
863940
// so we later mark them as live if their corresponding traits
@@ -869,23 +946,22 @@ fn maybe_record_as_seed<'tcx>(
869946
}
870947
}
871948
}
872-
DefKind::Impl { of_trait: true } => {
873-
if allow_dead_code.is_none() {
874-
if let Some(trait_def_id) =
875-
tcx.impl_trait_ref(owner_id.def_id).skip_binder().def_id.as_local()
876-
&& let Some(comes_from_allow) =
877-
has_allow_dead_code_or_lang_attr(tcx, trait_def_id)
878-
{
879-
push_into_worklist(WorkItem {
880-
id: owner_id.def_id,
881-
propagated: comes_from_allow,
882-
own: comes_from_allow,
883-
});
884-
}
885-
886-
unsolved_items.push(owner_id.def_id);
949+
DefKind::Impl { of_trait: false } if allow_dead_code.is_none() => {
950+
if let ty::Adt(adt, _) =
951+
tcx.type_of(owner_id.def_id).instantiate_identity().skip_normalization().kind()
952+
&& let Some(adt_def_id) = adt.did().as_local()
953+
&& let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, adt_def_id)
954+
{
955+
push_into_worklist(WorkItem {
956+
id: owner_id.def_id,
957+
propagated: comes_from_allow,
958+
own: ComesFromAllowExpect::No,
959+
});
887960
}
888961
}
962+
DefKind::Impl { of_trait: true } if allow_dead_code.is_none() => {
963+
unsolved_items.push(owner_id.def_id);
964+
}
889965
DefKind::GlobalAsm => {
890966
// global_asm! is always live.
891967
push_into_worklist(WorkItem {
@@ -910,15 +986,21 @@ fn maybe_record_as_seed<'tcx>(
910986
}
911987
}
912988

989+
#[derive(Default)]
990+
struct DeferredSeeds {
991+
pub_reachables: Vec<WorkItem>,
992+
come_from_allow: Vec<WorkItem>,
993+
}
994+
913995
struct SeedWorklists {
914996
worklist: Vec<WorkItem>,
915-
deferred_seeds: Vec<WorkItem>,
997+
deferred_seeds: DeferredSeeds,
916998
unsolved_items: Vec<LocalDefId>,
917999
}
9181000

9191001
fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9201002
let mut unsolved_items = Vec::new();
921-
let mut deferred_seeds = Vec::new();
1003+
let mut deferred_seeds = DeferredSeeds::default();
9221004
let mut worklist = Vec::new();
9231005

9241006
if let Some((def_id, _)) = tcx.entry_fn(())
@@ -948,7 +1030,7 @@ fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9481030

9491031
for (id, effective_vis) in tcx.effective_visibilities(()).iter() {
9501032
if effective_vis.is_public_at_level(Level::Reachable) {
951-
deferred_seeds.push(WorkItem {
1033+
deferred_seeds.pub_reachables.push(WorkItem {
9521034
id: *id,
9531035
propagated: ComesFromAllowExpect::No,
9541036
own: ComesFromAllowExpect::No,
@@ -957,7 +1039,7 @@ fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9571039
}
9581040

9591041
let mut push_into_worklist = |work_item: WorkItem| match work_item.own {
960-
ComesFromAllowExpect::Yes => deferred_seeds.push(work_item),
1042+
ComesFromAllowExpect::Yes => deferred_seeds.come_from_allow.push(work_item),
9611043
ComesFromAllowExpect::No => worklist.push(work_item),
9621044
};
9631045
let crate_items = tcx.hir_crate_items(());
@@ -972,8 +1054,7 @@ fn live_symbols_and_ignored_derived_traits(
9721054
tcx: TyCtxt<'_>,
9731055
(): (),
9741056
) -> Result<DeadCodeLivenessSummary, ErrorGuaranteed> {
975-
let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } =
976-
create_and_seed_worklist(tcx);
1057+
let SeedWorklists { worklist, deferred_seeds, unsolved_items } = create_and_seed_worklist(tcx);
9771058
let mut symbol_visitor = MarkSymbolVisitor {
9781059
worklist,
9791060
tcx,
@@ -986,15 +1067,19 @@ fn live_symbols_and_ignored_derived_traits(
9861067
ignore_variant_stack: vec![],
9871068
ignored_derived_traits: Default::default(),
9881069
propagated_comes_from_allow_expect: ComesFromAllowExpect::No,
1070+
unsolved_items,
9891071
};
990-
mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?;
1072+
symbol_visitor.mark_live_symbols_and_ignored_derived_traits(true)?;
9911073
let pre_deferred_seeding = DeadCodeLivenessSnapshot {
9921074
live_symbols: symbol_visitor.live_symbols.clone(),
9931075
ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(),
9941076
};
9951077

996-
symbol_visitor.worklist.extend(deferred_seeds);
997-
mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?;
1078+
symbol_visitor.worklist.extend(deferred_seeds.pub_reachables);
1079+
symbol_visitor.mark_live_symbols_and_ignored_derived_traits(true)?;
1080+
1081+
symbol_visitor.worklist.extend(deferred_seeds.come_from_allow);
1082+
symbol_visitor.mark_live_symbols_and_ignored_derived_traits(false)?;
9981083

9991084
Ok(DeadCodeLivenessSummary {
10001085
pre_deferred_seeding,
@@ -1005,40 +1090,6 @@ fn live_symbols_and_ignored_derived_traits(
10051090
})
10061091
}
10071092

1008-
fn mark_live_symbols_and_ignored_derived_traits(
1009-
symbol_visitor: &mut MarkSymbolVisitor<'_>,
1010-
unsolved_items: &mut Vec<LocalDefId>,
1011-
) -> Result<(), ErrorGuaranteed> {
1012-
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
1013-
return Err(guar);
1014-
}
1015-
1016-
// We have marked the primary seeds as live. We now need to process unsolved items from traits
1017-
// and trait impls: add them to the work list if the trait or the implemented type is live.
1018-
let mut items_to_check: Vec<_> = unsolved_items
1019-
.extract_if(.., |&mut local_def_id| {
1020-
symbol_visitor.check_impl_or_impl_item_live(local_def_id)
1021-
})
1022-
.collect();
1023-
1024-
while !items_to_check.is_empty() {
1025-
symbol_visitor.worklist.extend(items_to_check.drain(..).map(|id| WorkItem {
1026-
id,
1027-
propagated: ComesFromAllowExpect::No,
1028-
own: ComesFromAllowExpect::No,
1029-
}));
1030-
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
1031-
return Err(guar);
1032-
}
1033-
1034-
items_to_check.extend(unsolved_items.extract_if(.., |&mut local_def_id| {
1035-
symbol_visitor.check_impl_or_impl_item_live(local_def_id)
1036-
}));
1037-
}
1038-
1039-
Ok(())
1040-
}
1041-
10421093
struct DeadItem {
10431094
def_id: LocalDefId,
10441095
name: Symbol,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ check-pass
2+
3+
#![deny(dead_code)]
4+
5+
#[allow(dead_code)]
6+
struct Foo;
7+
8+
impl Foo {
9+
fn foo(&self) {}
10+
}
11+
12+
pub trait Tr {
13+
fn foo(&self);
14+
}
15+
16+
impl Tr for Foo {
17+
fn foo(&self) {
18+
bar()
19+
}
20+
}
21+
22+
fn bar() {}
23+
24+
fn main() {}

0 commit comments

Comments
 (0)