Skip to content

Commit c93fac7

Browse files
authored
Rollup merge of rust-lang#142485 - mu001999-contrib:dead-code/adt-pattern, r=petrochenkov
Marks ADT live if it appears in pattern Marks ADT live if it appears in pattern, it implies the construction of the ADT. 1. Then we can detect unused private ADTs impl `Default`, without special logics for `Default` and other std traits. 2. We can also remove `rustc_trivial_field_reads` on `Default`, and the logic in `should_ignore_item` (introduced by rust-lang#126302). Fixes rust-lang#120770 Extracted from rust-lang#128637. r? `@petrochenkov`
2 parents 8751016 + 52167e0 commit c93fac7

24 files changed

+161
-67
lines changed

compiler/rustc_passes/src/dead.rs

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
234234
pats: &[hir::PatField<'_>],
235235
) {
236236
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
237-
ty::Adt(adt, _) => adt.variant_of_res(res),
237+
ty::Adt(adt, _) => {
238+
// Marks the ADT live if its variant appears as the pattern,
239+
// considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`,
240+
// we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`.
241+
// Related issue: https://github.com/rust-lang/rust/issues/120770
242+
self.check_def_id(adt.did());
243+
adt.variant_of_res(res)
244+
}
238245
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
239246
};
240247
for pat in pats {
@@ -254,7 +261,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
254261
dotdot: hir::DotDotPos,
255262
) {
256263
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
257-
ty::Adt(adt, _) => adt.variant_of_res(res),
264+
ty::Adt(adt, _) => {
265+
// Marks the ADT live if its variant appears as the pattern
266+
self.check_def_id(adt.did());
267+
adt.variant_of_res(res)
268+
}
258269
_ => {
259270
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
260271
return;
@@ -359,31 +370,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
359370
return false;
360371
}
361372

362-
// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
363-
// cause external crate may call such methods to construct values of these types
364-
if let Some(local_impl_of) = impl_of.as_local()
365-
&& let Some(local_def_id) = def_id.as_local()
366-
&& let Some(fn_sig) =
367-
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
368-
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
369-
&& let TyKind::Path(QPath::Resolved(_, path)) =
370-
self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind
371-
&& let Res::Def(def_kind, did) = path.res
372-
{
373-
match def_kind {
374-
// for example, #[derive(Default)] pub struct T(i32);
375-
// external crate can call T::default() to construct T,
376-
// so that don't ignore impl Default for pub Enum and Structs
377-
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
378-
return false;
379-
}
380-
// don't ignore impl Default for Enums,
381-
// cause we don't know which variant is constructed
382-
DefKind::Enum => return false,
383-
_ => (),
384-
};
385-
}
386-
387373
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
388374
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
389375
{
@@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
494480
impl_id: hir::ItemId,
495481
local_def_id: LocalDefId,
496482
) -> bool {
497-
if self.should_ignore_item(local_def_id.to_def_id()) {
498-
return false;
499-
}
500-
501483
let trait_def_id = match self.tcx.def_kind(local_def_id) {
502484
// assoc impl items of traits are live if the corresponding trait items are live
503-
DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id,
485+
DefKind::AssocFn => self
486+
.tcx
487+
.associated_item(local_def_id)
488+
.trait_item_def_id
489+
.and_then(|def_id| def_id.as_local()),
504490
// impl items are live if the corresponding traits are live
505491
DefKind::Impl { of_trait: true } => self
506492
.tcx
507493
.impl_trait_ref(impl_id.owner_id.def_id)
508-
.and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)),
494+
.and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
509495
_ => None,
510496
};
511497

512-
if let Some(trait_def_id) = trait_def_id {
513-
if let Some(trait_def_id) = trait_def_id.as_local()
514-
&& !self.live_symbols.contains(&trait_def_id)
515-
{
516-
return false;
517-
}
518-
519-
// FIXME: legacy logic to check whether the function may construct `Self`,
520-
// this can be removed after supporting marking ADTs appearing in patterns
521-
// as live, then we can check private impls of public traits directly
522-
if let Some(fn_sig) =
523-
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
524-
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
525-
&& self.tcx.visibility(trait_def_id).is_public()
526-
{
527-
return true;
528-
}
498+
if let Some(trait_def_id) = trait_def_id
499+
&& !self.live_symbols.contains(&trait_def_id)
500+
{
501+
return false;
529502
}
530503

531504
// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
@@ -635,6 +608,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
635608
fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
636609
match &expr.kind {
637610
rustc_hir::PatExprKind::Path(qpath) => {
611+
// mark the type of variant live when meeting E::V in expr
612+
if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() {
613+
self.check_def_id(adt.did());
614+
}
615+
638616
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
639617
self.handle_res(res);
640618
}

library/core/src/default.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ use crate::ascii::Char as AsciiChar;
103103
/// ```
104104
#[rustc_diagnostic_item = "Default"]
105105
#[stable(feature = "rust1", since = "1.0.0")]
106-
#[rustc_trivial_field_reads]
107106
pub trait Default: Sized {
108107
/// Returns the "default value" for a type.
109108
///

tests/incremental/track-deps-in-new-solver.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//@ compile-flags: -Znext-solver
44
//@ check-pass
55

6+
#![allow(dead_code)]
7+
68
pub trait Future {
79
type Error;
810
fn poll() -> Self::Error;

tests/ui/const-generics/issues/issue-86535-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub trait Foo {
99
[(); Self::ASSOC_C]:;
1010
}
1111

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: struct `Bar` is never constructed
2+
--> $DIR/issue-86535-2.rs:12:8
3+
|
4+
LL | struct Bar<const N: &'static ()>;
5+
| ^^^
6+
|
7+
= note: `#[warn(dead_code)]` on by default
8+
9+
warning: 1 warning emitted
10+

tests/ui/const-generics/issues/issue-86535.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![feature(adt_const_params, unsized_const_params, generic_const_exprs)]
33
#![allow(incomplete_features, unused_variables)]
44

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: struct `F` is never constructed
2+
--> $DIR/issue-86535.rs:5:8
3+
|
4+
LL | struct F<const S: &'static str>;
5+
| ^
6+
|
7+
= note: `#[warn(dead_code)]` on by default
8+
9+
warning: 1 warning emitted
10+

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 `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
43+
= note: `D` has derived impls for the traits `Clone` and `Debug`, 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/impl-trait/extra-impl-in-trait-impl.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ run-rustfix
22

3+
#![allow(dead_code)]
34
struct S<T>(T);
45
struct S2;
56

tests/ui/impl-trait/extra-impl-in-trait-impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ run-rustfix
22

3+
#![allow(dead_code)]
34
struct S<T>(T);
45
struct S2;
56

tests/ui/impl-trait/extra-impl-in-trait-impl.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error: unexpected `impl` keyword
2-
--> $DIR/extra-impl-in-trait-impl.rs:6:18
2+
--> $DIR/extra-impl-in-trait-impl.rs:7:18
33
|
44
LL | impl<T: Default> impl Default for S<T> {
55
| ^^^^^ help: remove the extra `impl`
66
|
77
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
8-
--> $DIR/extra-impl-in-trait-impl.rs:6:18
8+
--> $DIR/extra-impl-in-trait-impl.rs:7:18
99
|
1010
LL | impl<T: Default> impl Default for S<T> {
1111
| ^^^^^^^^^^^^
1212

1313
error: unexpected `impl` keyword
14-
--> $DIR/extra-impl-in-trait-impl.rs:12:6
14+
--> $DIR/extra-impl-in-trait-impl.rs:13:6
1515
|
1616
LL | impl impl Default for S2 {
1717
| ^^^^^ help: remove the extra `impl`
1818
|
1919
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
20-
--> $DIR/extra-impl-in-trait-impl.rs:12:6
20+
--> $DIR/extra-impl-in-trait-impl.rs:13:6
2121
|
2222
LL | impl impl Default for S2 {
2323
| ^^^^^^^^^^^^

tests/ui/lint/dead-code/issue-41883.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed
2929
|
3030
LL | struct UnusedStruct;
3131
| ^^^^^^^^^^^^
32-
|
33-
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
3432

3533
error: aborting due to 4 previous errors
3634

tests/ui/lint/dead-code/issue-59003.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
#![deny(dead_code)]
66

7+
#[allow(dead_code)]
78
struct Foo {
8-
#[allow(dead_code)]
99
inner: u32,
1010
}
1111

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![deny(dead_code)]
2+
3+
struct Foo(u8); //~ ERROR struct `Foo` is never constructed
4+
5+
enum Bar { //~ ERROR enum `Bar` is never used
6+
Var1(u8),
7+
Var2(u8),
8+
}
9+
10+
pub trait Tr1 {
11+
fn f1() -> Self;
12+
}
13+
14+
impl Tr1 for Foo {
15+
fn f1() -> Foo {
16+
let f = Foo(0);
17+
let Foo(tag) = f;
18+
Foo(tag)
19+
}
20+
}
21+
22+
impl Tr1 for Bar {
23+
fn f1() -> Bar {
24+
let b = Bar::Var1(0);
25+
let b = if let Bar::Var1(_) = b {
26+
Bar::Var1(0)
27+
} else {
28+
Bar::Var2(0)
29+
};
30+
match b {
31+
Bar::Var1(_) => Bar::Var2(0),
32+
Bar::Var2(_) => Bar::Var1(0),
33+
}
34+
}
35+
}
36+
37+
fn main() {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: struct `Foo` is never constructed
2+
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8
3+
|
4+
LL | struct Foo(u8);
5+
| ^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9
9+
|
10+
LL | #![deny(dead_code)]
11+
| ^^^^^^^^^
12+
13+
error: enum `Bar` is never used
14+
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6
15+
|
16+
LL | enum Bar {
17+
| ^^^
18+
19+
error: aborting due to 2 previous errors
20+

tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed
5656
|
5757
LL | struct Foo(usize, #[allow(unused)] usize);
5858
| ^^^
59-
|
60-
= note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
6159

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ check-pass
2+
3+
#![deny(dead_code)]
4+
5+
#[repr(u8)]
6+
#[derive(Copy, Clone, Debug)]
7+
pub enum RecordField {
8+
Target = 1,
9+
Level,
10+
Module,
11+
File,
12+
Line,
13+
NumArgs,
14+
}
15+
16+
unsafe trait Pod {}
17+
18+
#[repr(transparent)]
19+
struct RecordFieldWrapper(RecordField);
20+
21+
unsafe impl Pod for RecordFieldWrapper {}
22+
23+
fn try_read<T: Pod>(buf: &[u8]) -> T {
24+
unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }
25+
}
26+
27+
pub fn foo(buf: &[u8]) -> RecordField {
28+
let RecordFieldWrapper(tag) = try_read(buf);
29+
tag
30+
}
31+
32+
fn main() {}

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
struct T1; //~ ERROR struct `T1` is never constructed
44
pub struct T2(i32); //~ ERROR field `0` is never read
5-
struct T3;
5+
struct T3; //~ ERROR struct `T3` is never constructed
66

77
trait Trait1 { //~ ERROR trait `Trait1` is never used
88
const UNUSED: i32;

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ LL | pub struct T2(i32);
2020
|
2121
= help: consider removing this field
2222

23+
error: struct `T3` is never constructed
24+
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:8
25+
|
26+
LL | struct T3;
27+
| ^^
28+
2329
error: trait `Trait1` is never used
2430
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
2531
|
2632
LL | trait Trait1 {
2733
| ^^^^^^
2834

29-
error: aborting due to 3 previous errors
35+
error: aborting due to 4 previous errors
3036

tests/ui/lint/dead-code/unused-struct-derive-default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ pub struct T2 {
2222

2323
fn main() {
2424
let _x: Used = Default::default();
25+
let _e: E = Default::default();
2526
}

tests/ui/lint/dead-code/unused-struct-derive-default.stderr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ error: struct `T` is never constructed
44
LL | struct T;
55
| ^
66
|
7-
= note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
87
note: the lint level is defined here
98
--> $DIR/unused-struct-derive-default.rs:1:9
109
|

tests/ui/parser/issues/issue-105366.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ run-rustfix
22

3+
#[allow(dead_code)]
34
struct Foo;
45

56
impl From<i32> for Foo {

0 commit comments

Comments
 (0)