Skip to content

Commit cbc2406

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

2 files changed

Lines changed: 148 additions & 75 deletions

File tree

compiler/rustc_passes/src/dead.rs

Lines changed: 124 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> {
@@ -544,13 +545,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
544545
/// `local_def_id` points to an impl or an impl item,
545546
/// both impl and impl item that may be passed to this function are of a trait,
546547
/// 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 {
548+
fn check_impl_or_impl_item_live(
549+
&self,
550+
local_def_id: LocalDefId,
551+
defer_seeds_come_from_allow: bool,
552+
) -> Option<ComesFromAllowExpect> {
548553
let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) {
549554
// assoc impl items of traits are live if the corresponding trait items are live
550555
DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::AssocFn => {
551-
let trait_item_id =
556+
let trait_def_id =
552557
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)
558+
(self.tcx.local_parent(local_def_id), trait_def_id)
554559
}
555560
// impl items are live if the corresponding traits are live
556561
DefKind::Impl { of_trait: true } => {
@@ -559,10 +564,19 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
559564
_ => bug!(),
560565
};
561566

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

568582
// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
@@ -571,10 +585,70 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
571585
&& let Some(adt_def_id) = adt.did().as_local()
572586
&& !self.live_symbols.contains(&adt_def_id)
573587
{
574-
return false;
588+
if defer_seeds_come_from_allow {
589+
return None;
590+
} else {
591+
return trait_comes_from_allow
592+
.or_else(|| has_allow_dead_code_or_lang_attr(self.tcx, adt_def_id));
593+
}
575594
}
576595

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

@@ -843,21 +917,22 @@ fn maybe_record_as_seed<'tcx>(
843917
if allow_dead_code.is_none() {
844918
let parent = tcx.local_parent(owner_id.def_id);
845919
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()
920+
DefKind::Trait => {}
921+
DefKind::Impl { of_trait: false } => {
922+
if let ty::Adt(adt, _) =
923+
tcx.type_of(parent).instantiate_identity().skip_normalization().kind()
924+
&& let Some(adt_def_id) = adt.did().as_local()
851925
&& let Some(comes_from_allow) =
852-
has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id)
926+
has_allow_dead_code_or_lang_attr(tcx, adt_def_id)
853927
{
854928
push_into_worklist(WorkItem {
855929
id: owner_id.def_id,
856930
propagated: comes_from_allow,
857-
own: comes_from_allow,
931+
own: ComesFromAllowExpect::No,
858932
});
859933
}
860-
934+
}
935+
DefKind::Impl { of_trait: true } => {
861936
// We only care about associated items of traits,
862937
// because they cannot be visited directly,
863938
// so we later mark them as live if their corresponding traits
@@ -869,23 +944,22 @@ fn maybe_record_as_seed<'tcx>(
869944
}
870945
}
871946
}
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);
947+
DefKind::Impl { of_trait: false } if allow_dead_code.is_none() => {
948+
if let ty::Adt(adt, _) =
949+
tcx.type_of(owner_id.def_id).instantiate_identity().skip_normalization().kind()
950+
&& let Some(adt_def_id) = adt.did().as_local()
951+
&& let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, adt_def_id)
952+
{
953+
push_into_worklist(WorkItem {
954+
id: owner_id.def_id,
955+
propagated: comes_from_allow,
956+
own: ComesFromAllowExpect::No,
957+
});
887958
}
888959
}
960+
DefKind::Impl { of_trait: true } if allow_dead_code.is_none() => {
961+
unsolved_items.push(owner_id.def_id);
962+
}
889963
DefKind::GlobalAsm => {
890964
// global_asm! is always live.
891965
push_into_worklist(WorkItem {
@@ -910,15 +984,21 @@ fn maybe_record_as_seed<'tcx>(
910984
}
911985
}
912986

987+
#[derive(Default)]
988+
struct DeferredSeeds {
989+
pub_reachables: Vec<WorkItem>,
990+
come_from_allow: Vec<WorkItem>,
991+
}
992+
913993
struct SeedWorklists {
914994
worklist: Vec<WorkItem>,
915-
deferred_seeds: Vec<WorkItem>,
995+
deferred_seeds: DeferredSeeds,
916996
unsolved_items: Vec<LocalDefId>,
917997
}
918998

919999
fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9201000
let mut unsolved_items = Vec::new();
921-
let mut deferred_seeds = Vec::new();
1001+
let mut deferred_seeds = DeferredSeeds::default();
9221002
let mut worklist = Vec::new();
9231003

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

9491029
for (id, effective_vis) in tcx.effective_visibilities(()).iter() {
9501030
if effective_vis.is_public_at_level(Level::Reachable) {
951-
deferred_seeds.push(WorkItem {
1031+
deferred_seeds.pub_reachables.push(WorkItem {
9521032
id: *id,
9531033
propagated: ComesFromAllowExpect::No,
9541034
own: ComesFromAllowExpect::No,
@@ -957,7 +1037,7 @@ fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists {
9571037
}
9581038

9591039
let mut push_into_worklist = |work_item: WorkItem| match work_item.own {
960-
ComesFromAllowExpect::Yes => deferred_seeds.push(work_item),
1040+
ComesFromAllowExpect::Yes => deferred_seeds.come_from_allow.push(work_item),
9611041
ComesFromAllowExpect::No => worklist.push(work_item),
9621042
};
9631043
let crate_items = tcx.hir_crate_items(());
@@ -972,8 +1052,7 @@ fn live_symbols_and_ignored_derived_traits(
9721052
tcx: TyCtxt<'_>,
9731053
(): (),
9741054
) -> Result<DeadCodeLivenessSummary, ErrorGuaranteed> {
975-
let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } =
976-
create_and_seed_worklist(tcx);
1055+
let SeedWorklists { worklist, deferred_seeds, unsolved_items } = create_and_seed_worklist(tcx);
9771056
let mut symbol_visitor = MarkSymbolVisitor {
9781057
worklist,
9791058
tcx,
@@ -986,15 +1065,19 @@ fn live_symbols_and_ignored_derived_traits(
9861065
ignore_variant_stack: vec![],
9871066
ignored_derived_traits: Default::default(),
9881067
propagated_comes_from_allow_expect: ComesFromAllowExpect::No,
1068+
unsolved_items,
9891069
};
990-
mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?;
1070+
symbol_visitor.mark_live_symbols_and_ignored_derived_traits(true)?;
9911071
let pre_deferred_seeding = DeadCodeLivenessSnapshot {
9921072
live_symbols: symbol_visitor.live_symbols.clone(),
9931073
ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(),
9941074
};
9951075

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

9991082
Ok(DeadCodeLivenessSummary {
10001083
pre_deferred_seeding,
@@ -1005,40 +1088,6 @@ fn live_symbols_and_ignored_derived_traits(
10051088
})
10061089
}
10071090

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-
10421091
struct DeadItem {
10431092
def_id: LocalDefId,
10441093
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)