rust icon indicating copy to clipboard operation
rust copied to clipboard

False-positive with the unreachable code lint

Open bjorn3 opened this issue 4 years ago • 5 comments

Given the following code: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=67b068c7dded060fd71f7e5a5ca91278

#![allow(deprecated, invalid_value)]

enum Void {}

fn main() {
    if false {
        unsafe { std::mem::uninitialized::<Void>(); }
    }
    
    println!();
}

The current output is:

warning: unreachable expression
  --> src/main.rs:10:5
   |
7  |         unsafe { std::mem::uninitialized::<Void>(); }
   |                  --------------------------------- any code following this expression is unreachable
...
10 |     println!();
   |     ^^^^^^^^^^^ unreachable expression
   |
   = note: `#[warn(unreachable_code)]` on by default
note: this expression has type `Void`, which is uninhabited
  --> src/main.rs:7:18
   |
7  |         unsafe { std::mem::uninitialized::<Void>(); }
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: this warning originates in the macro `$crate::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)

warning: `playground` (bin "playground") generated 1 warning

I expected no lint to trigger as the println!() is actually reachable. This problem doesn't occur for panic!().

bjorn3 avatar Aug 27 '21 13:08 bjorn3

@rustbot claim

pierwill avatar Oct 12 '21 21:10 pierwill

Hi @bjorn3! Can you elaborate a little on what makes println!() reachable here?

pierwill avatar Oct 18 '21 14:10 pierwill

println!() is reachable as the std::mem::uninitialized::<Void>(); is never executed due to if false {}.

bjorn3 avatar Oct 18 '21 16:10 bjorn3

@rustbot release-assignment

pierwill avatar Oct 24 '21 20:10 pierwill

I've located the issue to be in the liveness check: https://github.com/rust-lang/rust/blob/56694b04532cc0dec6e2f577135da3513e856923/compiler/rustc_passes/src/liveness.rs#L875-L896

Here a diverge point sits inside a conditional block, but this information is lost when calling propagate_through_expr, which then calls warn_about_unreachable.

A simple fix for this would be skipping the check for conditional neighbors. But that will actually bring out another false negative case:

#![allow(deprecated, invalid_value)]

enum Void {}

fn main() {
    let b = false;
    // should warn:
    // any code following this `match` expression is unreachable, as all arms diverge
    match b {
        false => unsafe { std::mem::uninitialized::<Void>(); }
        _ => unreachable!(),
    }
    println!();
}

I'm quite new to the compiler code, but this case seems difficult to fix because the exhaustiveness check takes place in a earlier(?) pass which doesn't have the complete type information.

tabokie avatar Oct 25 '21 09:10 tabokie