rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.08k stars 12.41k forks source link

ice: broken mir with `let_chains` #122871

Open Naserume opened 4 months ago

Naserume commented 4 months ago

Code

#![feature(let_chains)]

fn owned2<T, U>() -> U
where
    T: Default,
    U: Default,
{
    let foo: Result<T, ()> = Ok(T::default());
    let baz: U = U::default();

    if let Ok(foo) = foo && let Ok(bar) = transform(foo) {
        bar
    } else {
        baz
    }
}

fn transform<T, U>(input: T) -> Result<U, ()> {
    todo!()
}

fn main() {}

Meta

rustc --version --verbose:

rustc 1.79.0-nightly (0ad927c0c 2024-03-21)
binary: rustc
commit-hash: 0ad927c0c07b65fc0dae37105e09c877c87c296a
commit-date: 2024-03-21
host: x86_64-apple-darwin
release: 1.79.0-nightly
LLVM version: 18.1.2

Error output

warning: unused variable: `input`
  --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:20:20
   |
20 | fn transform<T, U>(input: T) -> Result<U, ()> {
   |                    ^^^^^ help: if this is intentional, prefix it with an underscore: `_input`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: function `owned2` is never used
 --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:5:4
  |
5 | fn owned2<T, U>() -> U
  |    ^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: function `transform` is never used
  --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:20:4
   |
20 | fn transform<T, U>(input: T) -> Result<U, ()> {
   |    ^^^^^^^^^

warning: 3 warnings emitted

note: no errors encountered even though delayed bugs were created

note: those delayed bugs will now be shown as internal compiler errors

command

rustc EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs '-Zlint-mir'

full backtrace is very long (about 3000 lines) you can see it in here

Backtrace

``` error: internal compiler error: broken MIR in Item(DefId(0:3 ~ EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538[ff8a]::owned2)) (after pass CheckPackedRef) at bb29[0]: use of local _6, which has no storage here --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:18:1 | 18 | } | ^ | note: delayed at compiler/rustc_mir_transform/src/lint.rs:75:22 0: std::backtrace::Backtrace::create 1: std::backtrace::Backtrace::capture 2: ::emit_diagnostic 3: ::emit_diagnostic 4: ::emit_producing_guarantee 5: ::span_delayed_bug:: 6: ::fail:: 7: ::visit_local 8: ::visit_terminator 9: rustc_mir_transform::lint::lint_body 10: rustc_mir_transform::pass_manager::run_passes_inner 11: rustc_mir_transform::mir_const 12: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 13: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 14: rustc_query_impl::query_impl::mir_const::get_query_non_incr::__rust_end_short_backtrace 15: rustc_middle::query::plumbing::query_get_at::>> 16: rustc_mir_transform::mir_promoted 17: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 18: >::call_once 19: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 20: rustc_query_impl::query_impl::mir_promoted::get_query_non_incr::__rust_end_short_backtrace 21: rustc_middle::query::plumbing::query_get_at::>> 22: rustc_borrowck::mir_borrowck 23: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 24: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 25: rustc_query_impl::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace 26: ::par_body_owners::::{closure#0} 27: rustc_interface::passes::analysis 28: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 29: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 30: rustc_query_impl::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace 31: >::enter::, rustc_driver_impl::run_compiler::{closure#0}::{closure#1}::{closure#3}> 32: rustc_interface::interface::run_compiler::, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0} 33: std::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>> 34: <::spawn_unchecked_, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#1} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} 35: std::sys::pal::unix::thread::Thread::new::thread_start 36: __pthread_start --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:18:1 | 18 | } | ^ error: internal compiler error: broken MIR in Item(DefId(0:3 ~ EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538[ff8a]::owned2)) (after pass CheckPackedRef) at bb24[1]: use of local _6, which has no storage here --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:18:1 | 18 | } | ^ | note: delayed at compiler/rustc_mir_transform/src/lint.rs:75:22 0: std::backtrace::Backtrace::create 1: std::backtrace::Backtrace::capture 2: ::emit_diagnostic 3: ::emit_diagnostic 4: ::emit_producing_guarantee 5: ::span_delayed_bug:: 6: ::fail:: 7: ::visit_local 8: ::visit_terminator 9: rustc_mir_transform::lint::lint_body 10: rustc_mir_transform::pass_manager::run_passes_inner 11: rustc_mir_transform::mir_const 12: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 13: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 14: rustc_query_impl::query_impl::mir_const::get_query_non_incr::__rust_end_short_backtrace 15: rustc_middle::query::plumbing::query_get_at::>> 16: rustc_mir_transform::mir_promoted 17: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 18: >::call_once 19: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 20: rustc_query_impl::query_impl::mir_promoted::get_query_non_incr::__rust_end_short_backtrace 21: rustc_middle::query::plumbing::query_get_at::>> 22: rustc_borrowck::mir_borrowck 23: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 24: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 25: rustc_query_impl::query_impl::mir_borrowck::get_query_non_incr::__rust_end_short_backtrace 26: ::par_body_owners::::{closure#0} 27: rustc_interface::passes::analysis 28: rustc_query_impl::plumbing::__rust_begin_short_backtrace::> 29: rustc_query_system::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false> 30: rustc_query_impl::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace 31: >::enter::, rustc_driver_impl::run_compiler::{closure#0}::{closure#1}::{closure#3}> 32: rustc_interface::interface::run_compiler::, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0} 33: std::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>> 34: <::spawn_unchecked_, rustc_driver_impl::run_compiler::{closure#0}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#1} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} 35: std::sys::pal::unix::thread::Thread::new::thread_start 36: __pthread_start --> EDF91430837E67F42912F85F184235C09E30358C3D226F98C2B0098353056538.rs:18:1 | 18 | } | ^ ```

seems like bug occured at visit_local and it's different one from #120421 visit_statement and visit_terminator

matthiaskrgr commented 4 months ago

known issue https://github.com/rust-lang/rust/issues?q=is%3Aissue+let_chains+broken+mir+is%3Aclosed

Nadrieril commented 4 months ago

Indeed looks very similar to https://github.com/rust-lang/rust/issues/99852 which was fixed in https://github.com/rust-lang/rust/pull/102394.

Minimized:

#![feature(let_chains)]
struct Struct;

impl Drop for Struct {
    fn drop(&mut self) {}
}

fn main() {
    if true
        && let _ = Struct
    {}
}

This is the first pass that errors:

error: internal compiler error: broken MIR in Item(DefId(0:7 ~ foo[b740]::main)) (after pass CheckPackedRef) at bb8[1]:
                                use of local _2, which has no storage here

Here's a MIR that errors, taken in a later phase for convenience:

error: internal compiler error: broken MIR in Item(DefId(0:7 ~ foo[b740]::main)) (after pass SimplifyCfg-pre-optimizations) at bb6[0]:
                                use of local _2, which has no storage here
// MIR for `main` after SimplifyCfg-pre-optimizations

fn main() -> () {
    let mut _0: ();
    let mut _1: bool;
    let mut _2: Struct;
    let mut _3: bool;

    bb0: {
        _3 = const false;
        StorageLive(_1);
        _1 = const true;
        switchInt(move _1) -> [0: bb2, otherwise: bb1];
    }

    bb1: {
        StorageLive(_2);
        _3 = const true;
        _2 = Struct;
        _0 = const ();
        goto -> bb3;
    }

    bb2: {
        _0 = const ();
        goto -> bb3;
    }

    bb3: {
        StorageDead(_1);
        switchInt(_3) -> [0: bb4, otherwise: bb6];
    }

    bb4: {
        _3 = const false;
        StorageDead(_2);
        return;
    }

    bb5 (cleanup): {
        resume;
    }

    bb6: {
        drop(_2) -> [return: bb4, unwind: bb5];
    }
}

version: 1.79.0-nightly (805813650 2024-03-31)

Nadrieril commented 4 months ago

The error is pretty clear: when testing the boolean, if found false we shortcut to bb2 which doesn't initialize the Struct, but the Struct is unconditionally dropped at the end of the if block (in bb6).

Indeed this no longer errors:

#![feature(let_chains)]
struct Struct;

impl Drop for Struct {
    fn drop(&mut self) {}
}

fn main() {
    let s = Struct;
    if true
        && let _ = s
    {}
}
Nadrieril commented 4 months ago

Ah, this is actually an issue with -Zlint-mir more than anything, since variations on this store a drop flag to ensure we only drop the value if it was initialized. There's no unsoundness here, just a validation phase that doesn't take into account drop flags.