Skip to content
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

dead_code false positive introduced in rustc 1.78.0-nightly (8ace7ea1f 2024-02-07) #120770

Open
tamird opened this issue Feb 8, 2024 · 11 comments · Fixed by #127107 · May be fixed by #128637
Open

dead_code false positive introduced in rustc 1.78.0-nightly (8ace7ea1f 2024-02-07) #120770

tamird opened this issue Feb 8, 2024 · 11 comments · Fixed by #127107 · May be fixed by #128637
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. C-discussion Category: Discussion or questions that doesn't represent real issues. L-dead_code Lint: dead_code P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@tamird
Copy link
Contributor

tamird commented Feb 8, 2024

#![deny(warnings)]

#[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
}

This code compiles on stable (and nightlies before 2024-02-07) but now produces:

error: struct `RecordFieldWrapper` is never constructed
  --> aya-log/src/lib.rs:79:8
   |
79 | struct RecordFieldWrapper(RecordField);
   |        ^^^^^^^^^^^^^^^^^^
   |
   = note: `RecordFieldWrapper` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis
   = note: `-D dead-code` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(dead_code)]`

NOTE: playground's nightly is not new enough
playground

@tamird tamird added C-bug Category: This is a bug. regression-untriaged Untriaged performance or correctness regression. labels Feb 8, 2024
@rustbot rustbot added I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Feb 8, 2024
tamird added a commit to tamird/aya that referenced this issue Feb 8, 2024
Some of these are legit, others are false positives. I've filed
rust-lang/rust#120770 for the latter.
@apiraino
Copy link
Contributor

apiraino commented Feb 8, 2024

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-high

@rustbot rustbot added P-high High priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Feb 8, 2024
@apiraino
Copy link
Contributor

apiraino commented Feb 8, 2024

searched nightlies: from nightly-2024-02-01 to nightly-2024-02-08
regressed nightly: nightly-2024-02-08
searched commit range: 256b6fb...8ace7ea
regressed commit: d4f6f9e

bisected with cargo-bisect-rustc v0.6.7

Host triple: x86_64-unknown-linux-gnu
Reproduce with:

cargo bisect-rustc --start=2024-02-01 --regress error --script script.sh 

cc @mu001999 @cjgillot

vadorovsky pushed a commit to aya-rs/aya that referenced this issue Feb 8, 2024
Some of these are legit, others are false positives. I've filed
rust-lang/rust#120770 for the latter.
@Noratrieb
Copy link
Member

How would you expect the compiler to figure out that it is constructed? Just because there's a value of that type somewhere doesn't mean it is, that would make the lint basically useless.

@fmease fmease added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-discussion Category: Discussion or questions that doesn't represent real issues. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Feb 8, 2024
@mu001999
Copy link
Contributor

mu001999 commented Feb 8, 2024

By the way, the following code does not compile on stable (play):

#![deny(warnings)]

pub struct Bar(u32);

#[repr(transparent)]
struct Wrapper(Bar);

fn try_read<T>(p: &u32) -> T {
    unsafe { std::ptr::read_unaligned(p as *const u32 as *const T) }
}

pub fn foo(p: &u32) -> Bar {
    let Wrapper(bar) = try_read(p);
    bar
}

@Noratrieb
Copy link
Member

Yeah, this isn't a regression, but a long standing issue (which seems hard to impossible to solve) that's now being exposed a bit more.
Would be nice if we could solve it, but I have doubts about that.

@oli-obk oli-obk added P-low Low priority and removed P-high High priority regression-untriaged Untriaged performance or correctness regression. labels Feb 10, 2024
@oli-obk
Copy link
Contributor

oli-obk commented Feb 10, 2024

I don't see a way to solve this without looking through generic function calls (recursively, by looking at the function bodies). That also means it can only be done after monomorphization.

And even then it won't catch e.g. a transmute that produces a None. Instead that would be treated as producing a value of the Some type hypothetically.

Such deeper analyses maybe better suited to external tools that even do partial function instantiation, cross crate analysis...

@ryanavella
Copy link

At risk of mentioning the obvious, the example provided is not entirely minimal. It will work for any "constructor" of the form fn foo<T>() -> T, no trait bounds required.

I'm seeing this in some ffi-heavy code where it didn't trigger previously. In my case the "constructor" looks like this:

unsafe fn sysctl<T>(mib: &mut [c_int]) -> Option<T> {
    todo!()
}

@shepmaster
Copy link
Member

It will work for any "constructor" of the form […] no trait bounds required.

Is that not what is shown in #120770 (comment) ?

@mu001999
Copy link
Contributor

@rustbot claim

I suddenly realized that this probably didn't need to be done after monomorphization.

We won't meet this warning if specific the type:

#![deny(warnings)]

pub struct Bar(u32);

#[repr(transparent)]
struct Wrapper(Bar);

fn try_read<T>(p: &u32) -> T {
    unsafe { std::ptr::read_unaligned(p as *const u32 as *const T) }
}

pub fn foo(p: &u32) -> Bar {
    let Wrapper(bar) = try_read::<Wrapper>(p);
    bar
}

So I think maybe we can just mark the type live when visiting the corresponding pattern.

I will try it.

workingjubilee added a commit to workingjubilee/rustc that referenced this issue Jul 5, 2024
…=pnkfelix

Improve dead code analysis

Fixes rust-lang#120770

1. check impl items later if self ty is private although the trait method is public, cause we must use the ty firstly if it's private
2. mark the adt live if it appears in pattern, like generic argument, this implies the use of the adt
3. based on the above, we can handle the case that private adts impl Default, so that we don't need adding rustc_trivial_field_reads on Default, and the logic in should_ignore_item

r? `@pnkfelix`
@bors bors closed this as completed in 31fe962 Jul 6, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jul 6, 2024
Rollup merge of rust-lang#127107 - mu001999-contrib:dead/enhance-2, r=pnkfelix

Improve dead code analysis

Fixes rust-lang#120770

1. check impl items later if self ty is private although the trait method is public, cause we must use the ty firstly if it's private
2. mark the adt live if it appears in pattern, like generic argument, this implies the use of the adt
3. based on the above, we can handle the case that private adts impl Default, so that we don't need adding rustc_trivial_field_reads on Default, and the logic in should_ignore_item

r? ``@pnkfelix``
@compiler-errors
Copy link
Member

I believe this is uncovered again since we reverted recent changes to dead code analysis

@hvenev-insait
Copy link

It can also trigger when the constructor is used in reachable code:

trait Constructible: Sized {
    fn new() -> Self;
}

struct My(i32);

impl Constructible for My {
    fn new() -> Self {
        My(42)
    }
}

fn make<M: Constructible>() -> M {
    M::new()
}

fn main() {
    let My(x) = make();
    eprintln!("ok {}", x);
}

Perhaps without monomorphization we could reason that:

  1. The impl uses the constructor.
  2. The impl itself isn't dead.
  3. Therefore the constructor isn't dead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. C-discussion Category: Discussion or questions that doesn't represent real issues. L-dead_code Lint: dead_code P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet