diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 59a347c9042bf..a2d960352b317 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -129,6 +129,7 @@ struct MarkSymbolVisitor<'tcx> { // macro) ignored_derived_traits: LocalDefIdMap>, propagated_comes_from_allow_expect: ComesFromAllowExpect, + unsolved_items: Vec, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -544,13 +545,16 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// `local_def_id` points to an impl or an impl item, /// both impl and impl item that may be passed to this function are of a trait, /// and added into the unsolved_items during `create_and_seed_worklist` - fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool { + fn check_impl_or_impl_item_live( + &self, + local_def_id: LocalDefId, + ) -> Option { let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live DefKind::AssocConst { .. } | DefKind::AssocTy | DefKind::AssocFn => { - let trait_item_id = + let trait_def_id = self.tcx.trait_item_of(local_def_id).and_then(|def_id| def_id.as_local()); - (self.tcx.local_parent(local_def_id), trait_item_id) + (self.tcx.local_parent(local_def_id), trait_def_id) } // impl items are live if the corresponding traits are live DefKind::Impl { of_trait: true } => { @@ -562,7 +566,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(trait_def_id) = trait_def_id && !self.live_symbols.contains(&trait_def_id) { - return false; + return has_allow_dead_code_or_lang_attr(self.tcx, trait_def_id); } // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. @@ -571,10 +575,50 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let Some(adt_def_id) = adt.did().as_local() && !self.live_symbols.contains(&adt_def_id) { - return false; + return has_allow_dead_code_or_lang_attr(self.tcx, adt_def_id); } - true + Some(ComesFromAllowExpect::No) + } + + fn collect_live_items_from_unsolved_items( + &mut self, + ) -> Vec<(LocalDefId, ComesFromAllowExpect)> { + let mut unsolved_items = std::mem::take(&mut self.unsolved_items); + let mut items_to_check = vec![]; + unsolved_items.retain(|&def_id| { + if let Some(comes_from_allow) = self.check_impl_or_impl_item_live(def_id) { + items_to_check.push((def_id, comes_from_allow)); + false + } else { + true + } + }); + self.unsolved_items = unsolved_items; + items_to_check + } + + fn mark_live_symbols_and_ignored_derived_traits(&mut self) -> Result<(), ErrorGuaranteed> { + if let ControlFlow::Break(guar) = self.mark_live_symbols() { + return Err(guar); + } + + // We have marked the primary seeds as live. We now need to process unsolved items from traits + // and trait impls: add them to the work list if the trait or the implemented type is live. + let mut items_to_check = self.collect_live_items_from_unsolved_items(); + + while !items_to_check.is_empty() { + self.worklist.extend(items_to_check.into_iter().map(|(id, comes_from_allow)| { + WorkItem { id, propagated: comes_from_allow, own: comes_from_allow } + })); + if let ControlFlow::Break(guar) = self.mark_live_symbols() { + return Err(guar); + } + + items_to_check = self.collect_live_items_from_unsolved_items(); + } + + Ok(()) } } @@ -843,21 +887,22 @@ fn maybe_record_as_seed<'tcx>( if allow_dead_code.is_none() { let parent = tcx.local_parent(owner_id.def_id); match tcx.def_kind(parent) { - DefKind::Impl { of_trait: false } | DefKind::Trait => {} - DefKind::Impl { of_trait: true } => { - if let Some(trait_item_def_id) = - tcx.associated_item(owner_id.def_id).trait_item_def_id() - && let Some(trait_item_local_def_id) = trait_item_def_id.as_local() + DefKind::Trait => {} + DefKind::Impl { of_trait: false } => { + if let ty::Adt(adt, _) = + tcx.type_of(parent).instantiate_identity().skip_normalization().kind() + && let Some(adt_def_id) = adt.did().as_local() && let Some(comes_from_allow) = - has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) + has_allow_dead_code_or_lang_attr(tcx, adt_def_id) { push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, - own: comes_from_allow, + own: ComesFromAllowExpect::No, }); } - + } + DefKind::Impl { of_trait: true } => { // We only care about associated items of traits, // because they cannot be visited directly, // so we later mark them as live if their corresponding traits @@ -869,23 +914,22 @@ fn maybe_record_as_seed<'tcx>( } } } - DefKind::Impl { of_trait: true } => { - if allow_dead_code.is_none() { - if let Some(trait_def_id) = - tcx.impl_trait_ref(owner_id.def_id).skip_binder().def_id.as_local() - && let Some(comes_from_allow) = - has_allow_dead_code_or_lang_attr(tcx, trait_def_id) - { - push_into_worklist(WorkItem { - id: owner_id.def_id, - propagated: comes_from_allow, - own: comes_from_allow, - }); - } - - unsolved_items.push(owner_id.def_id); + DefKind::Impl { of_trait: false } if allow_dead_code.is_none() => { + if let ty::Adt(adt, _) = + tcx.type_of(owner_id.def_id).instantiate_identity().skip_normalization().kind() + && let Some(adt_def_id) = adt.did().as_local() + && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, adt_def_id) + { + push_into_worklist(WorkItem { + id: owner_id.def_id, + propagated: comes_from_allow, + own: ComesFromAllowExpect::No, + }); } } + DefKind::Impl { of_trait: true } if allow_dead_code.is_none() => { + unsolved_items.push(owner_id.def_id); + } DefKind::GlobalAsm => { // global_asm! is always live. push_into_worklist(WorkItem { @@ -972,8 +1016,7 @@ fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), ) -> Result { - let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } = - create_and_seed_worklist(tcx); + let SeedWorklists { worklist, deferred_seeds, unsolved_items } = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -986,15 +1029,16 @@ fn live_symbols_and_ignored_derived_traits( ignore_variant_stack: vec![], ignored_derived_traits: Default::default(), propagated_comes_from_allow_expect: ComesFromAllowExpect::No, + unsolved_items, }; - mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + symbol_visitor.mark_live_symbols_and_ignored_derived_traits()?; let pre_deferred_seeding = DeadCodeLivenessSnapshot { live_symbols: symbol_visitor.live_symbols.clone(), ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(), }; symbol_visitor.worklist.extend(deferred_seeds); - mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + symbol_visitor.mark_live_symbols_and_ignored_derived_traits()?; Ok(DeadCodeLivenessSummary { pre_deferred_seeding, @@ -1005,40 +1049,6 @@ fn live_symbols_and_ignored_derived_traits( }) } -fn mark_live_symbols_and_ignored_derived_traits( - symbol_visitor: &mut MarkSymbolVisitor<'_>, - unsolved_items: &mut Vec, -) -> Result<(), ErrorGuaranteed> { - if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() { - return Err(guar); - } - - // We have marked the primary seeds as live. We now need to process unsolved items from traits - // and trait impls: add them to the work list if the trait or the implemented type is live. - let mut items_to_check: Vec<_> = unsolved_items - .extract_if(.., |&mut local_def_id| { - symbol_visitor.check_impl_or_impl_item_live(local_def_id) - }) - .collect(); - - while !items_to_check.is_empty() { - symbol_visitor.worklist.extend(items_to_check.drain(..).map(|id| WorkItem { - id, - propagated: ComesFromAllowExpect::No, - own: ComesFromAllowExpect::No, - })); - if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() { - return Err(guar); - } - - items_to_check.extend(unsolved_items.extract_if(.., |&mut local_def_id| { - symbol_visitor.check_impl_or_impl_item_live(local_def_id) - })); - } - - Ok(()) -} - struct DeadItem { def_id: LocalDefId, name: Symbol, diff --git a/tests/ui/lint/dead-code/unused-impl-for-allow-dead-type.rs b/tests/ui/lint/dead-code/unused-impl-for-allow-dead-type.rs new file mode 100644 index 0000000000000..6167cfaef3e60 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-allow-dead-type.rs @@ -0,0 +1,24 @@ +//@ check-pass + +#![deny(dead_code)] + +#[allow(dead_code)] +struct Foo; + +impl Foo { + fn foo(&self) {} +} + +pub trait Tr { + fn foo(&self); +} + +impl Tr for Foo { + fn foo(&self) { + bar() + } +} + +fn bar() {} + +fn main() {}