Skip to content

Marks ADT live if it appears in pattern #142485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 28 additions & 50 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
pats: &[hir::PatField<'_>],
) {
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
ty::Adt(adt, _) => adt.variant_of_res(res),
ty::Adt(adt, _) => {
// Marks the ADT live if its variant appears as the pattern,
// considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`,
// we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`.
// Related issue: https://github.com/rust-lang/rust/issues/120770
self.check_def_id(adt.did());
adt.variant_of_res(res)
}
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
};
for pat in pats {
Expand All @@ -254,7 +261,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
dotdot: hir::DotDotPos,
) {
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
ty::Adt(adt, _) => adt.variant_of_res(res),
ty::Adt(adt, _) => {
// Marks the ADT live if its variant appears as the pattern
self.check_def_id(adt.did());
adt.variant_of_res(res)
}
_ => {
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
return;
Expand Down Expand Up @@ -359,31 +370,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
return false;
}

// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
// cause external crate may call such methods to construct values of these types
if let Some(local_impl_of) = impl_of.as_local()
&& let Some(local_def_id) = def_id.as_local()
&& let Some(fn_sig) =
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
&& let TyKind::Path(QPath::Resolved(_, path)) =
self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind
&& let Res::Def(def_kind, did) = path.res
{
match def_kind {
// for example, #[derive(Default)] pub struct T(i32);
// external crate can call T::default() to construct T,
// so that don't ignore impl Default for pub Enum and Structs
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
return false;
}
// don't ignore impl Default for Enums,
// cause we don't know which variant is constructed
DefKind::Enum => return false,
_ => (),
};
}

if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
{
Expand Down Expand Up @@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
impl_id: hir::ItemId,
local_def_id: LocalDefId,
) -> bool {
if self.should_ignore_item(local_def_id.to_def_id()) {
return false;
}

let 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::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id,
DefKind::AssocFn => self
.tcx
.associated_item(local_def_id)
.trait_item_def_id
.and_then(|def_id| def_id.as_local()),
// impl items are live if the corresponding traits are live
DefKind::Impl { of_trait: true } => self
.tcx
.impl_trait_ref(impl_id.owner_id.def_id)
.and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)),
.and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
_ => None,
};

if let Some(trait_def_id) = trait_def_id {
if let Some(trait_def_id) = trait_def_id.as_local()
&& !self.live_symbols.contains(&trait_def_id)
{
return false;
}

// FIXME: legacy logic to check whether the function may construct `Self`,
// this can be removed after supporting marking ADTs appearing in patterns
// as live, then we can check private impls of public traits directly
if let Some(fn_sig) =
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
&& self.tcx.visibility(trait_def_id).is_public()
{
return true;
}
if let Some(trait_def_id) = trait_def_id
&& !self.live_symbols.contains(&trait_def_id)
{
return false;
}

// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
Expand Down Expand Up @@ -635,6 +608,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
match &expr.kind {
rustc_hir::PatExprKind::Path(qpath) => {
// mark the type of variant live when meeting E::V in expr
if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() {
self.check_def_id(adt.did());
}

let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
self.handle_res(res);
}
Expand Down
1 change: 0 additions & 1 deletion library/core/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ use crate::ascii::Char as AsciiChar;
/// ```
#[rustc_diagnostic_item = "Default"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_trivial_field_reads]
pub trait Default: Sized {
/// Returns the "default value" for a type.
///
Expand Down
2 changes: 2 additions & 0 deletions tests/incremental/track-deps-in-new-solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//@ compile-flags: -Znext-solver
//@ check-pass

#![allow(dead_code)]

pub trait Future {
type Error;
fn poll() -> Self::Error;
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/const-generics/issues/issue-86535-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub trait Foo {
[(); Self::ASSOC_C]:;
}

struct Bar<const N: &'static ()>;
struct Bar<const N: &'static ()>; //~ WARN struct `Bar` is never constructed
impl<const N: &'static ()> Foo for Bar<N> {
const ASSOC_C: usize = 3;

Expand Down
10 changes: 10 additions & 0 deletions tests/ui/const-generics/issues/issue-86535-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
warning: struct `Bar` is never constructed
--> $DIR/issue-86535-2.rs:12:8
|
LL | struct Bar<const N: &'static ()>;
| ^^^
|
= note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

2 changes: 1 addition & 1 deletion tests/ui/const-generics/issues/issue-86535.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![feature(adt_const_params, unsized_const_params, generic_const_exprs)]
#![allow(incomplete_features, unused_variables)]

struct F<const S: &'static str>;
struct F<const S: &'static str>; //~ WARN struct `F` is never constructed
impl<const S: &'static str> X for F<{ S }> {
const W: usize = 3;

Expand Down
10 changes: 10 additions & 0 deletions tests/ui/const-generics/issues/issue-86535.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
warning: struct `F` is never constructed
--> $DIR/issue-86535.rs:5:8
|
LL | struct F<const S: &'static str>;
| ^
|
= note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

2 changes: 1 addition & 1 deletion tests/ui/derives/clone-debug-dead-code.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ LL | struct D { f: () }
| |
| field in this struct
|
= note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
= note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis

error: field `f` is never read
--> $DIR/clone-debug-dead-code.rs:21:12
Expand Down
1 change: 1 addition & 0 deletions tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@ run-rustfix

#![allow(dead_code)]
struct S<T>(T);
struct S2;

Expand Down
1 change: 1 addition & 0 deletions tests/ui/impl-trait/extra-impl-in-trait-impl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@ run-rustfix

#![allow(dead_code)]
struct S<T>(T);
struct S2;

Expand Down
8 changes: 4 additions & 4 deletions tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error: unexpected `impl` keyword
--> $DIR/extra-impl-in-trait-impl.rs:6:18
--> $DIR/extra-impl-in-trait-impl.rs:7:18
|
LL | impl<T: Default> impl Default for S<T> {
| ^^^^^ help: remove the extra `impl`
|
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
--> $DIR/extra-impl-in-trait-impl.rs:6:18
--> $DIR/extra-impl-in-trait-impl.rs:7:18
|
LL | impl<T: Default> impl Default for S<T> {
| ^^^^^^^^^^^^

error: unexpected `impl` keyword
--> $DIR/extra-impl-in-trait-impl.rs:12:6
--> $DIR/extra-impl-in-trait-impl.rs:13:6
|
LL | impl impl Default for S2 {
| ^^^^^ help: remove the extra `impl`
|
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
--> $DIR/extra-impl-in-trait-impl.rs:12:6
--> $DIR/extra-impl-in-trait-impl.rs:13:6
|
LL | impl impl Default for S2 {
| ^^^^^^^^^^^^
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/lint/dead-code/issue-41883.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed
|
LL | struct UnusedStruct;
| ^^^^^^^^^^^^
|
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis

error: aborting due to 4 previous errors

2 changes: 1 addition & 1 deletion tests/ui/lint/dead-code/issue-59003.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#![deny(dead_code)]

#[allow(dead_code)]
struct Foo {
#[allow(dead_code)]
inner: u32,
}

Expand Down
37 changes: 37 additions & 0 deletions tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#![deny(dead_code)]

struct Foo(u8); //~ ERROR struct `Foo` is never constructed

enum Bar { //~ ERROR enum `Bar` is never used
Var1(u8),
Var2(u8),
}

pub trait Tr1 {
fn f1() -> Self;
}

impl Tr1 for Foo {
fn f1() -> Foo {
let f = Foo(0);
let Foo(tag) = f;
Foo(tag)
}
}

impl Tr1 for Bar {
fn f1() -> Bar {
let b = Bar::Var1(0);
let b = if let Bar::Var1(_) = b {
Bar::Var1(0)
} else {
Bar::Var2(0)
};
match b {
Bar::Var1(_) => Bar::Var2(0),
Bar::Var2(_) => Bar::Var1(0),
}
}
}

fn main() {}
20 changes: 20 additions & 0 deletions tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error: struct `Foo` is never constructed
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8
|
LL | struct Foo(u8);
| ^^^
|
note: the lint level is defined here
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^

error: enum `Bar` is never used
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6
|
LL | enum Bar {
| ^^^

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed
|
LL | struct Foo(usize, #[allow(unused)] usize);
| ^^^
|
= note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis

error: aborting due to 2 previous errors; 2 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//@ check-pass

#![deny(dead_code)]

#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum RecordField {
Target = 1,
Level,
Module,
File,
Line,
NumArgs,
}

unsafe trait Pod {}

#[repr(transparent)]
struct RecordFieldWrapper(RecordField);

unsafe impl Pod for RecordFieldWrapper {}

fn try_read<T: Pod>(buf: &[u8]) -> T {
unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }
}

pub fn foo(buf: &[u8]) -> RecordField {
let RecordFieldWrapper(tag) = try_read(buf);
tag
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

struct T1; //~ ERROR struct `T1` is never constructed
pub struct T2(i32); //~ ERROR field `0` is never read
struct T3;
struct T3; //~ ERROR struct `T3` is never constructed

trait Trait1 { //~ ERROR trait `Trait1` is never used
const UNUSED: i32;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ LL | pub struct T2(i32);
|
= help: consider removing this field

error: struct `T3` is never constructed
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:8
|
LL | struct T3;
| ^^

error: trait `Trait1` is never used
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
|
LL | trait Trait1 {
| ^^^^^^

error: aborting due to 3 previous errors
error: aborting due to 4 previous errors

1 change: 1 addition & 0 deletions tests/ui/lint/dead-code/unused-struct-derive-default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ pub struct T2 {

fn main() {
let _x: Used = Default::default();
let _e: E = Default::default();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ error: struct `T` is never constructed
LL | struct T;
| ^
|
= note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
note: the lint level is defined here
--> $DIR/unused-struct-derive-default.rs:1:9
|
Expand Down
1 change: 1 addition & 0 deletions tests/ui/parser/issues/issue-105366.fixed
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@ run-rustfix

#[allow(dead_code)]
struct Foo;

impl From<i32> for Foo {
Expand Down
Loading
Loading