Skip to content

ICE with NLL #46628

@ia0

Description

@ia0
Contributor

I tried this code (in a file named ice.rs):

use std::io::BufRead;

fn main() {
    let stdin = std::io::stdin();
    let line = stdin.lock().lines().next().unwrap().unwrap();
    println!("{}", line);
}

I expected RUST_BACKTRACE=1 rustc -Z nll ice.rs to succeed. Instead, I got the following error:

error: internal compiler error: /checkout/src/librustc_mir/borrow_check/nll/mod.rs:309: region is not an ReVar: ReStatic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.24.0-nightly (6fa53b00e 2017-12-09) running on x86_64-unknown-linux-gnu

note: run with `RUST_BACKTRACE=1` for a backtrace

thread 'rustc' panicked at 'Box<Any>', /checkout/src/librustc_errors/lib.rs:501:8
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at /checkout/src/libstd/sys_common/backtrace.rs:68
             at /checkout/src/libstd/sys_common/backtrace.rs:57
   2: std::panicking::default_hook::{{closure}}
             at /checkout/src/libstd/panicking.rs:381
   3: std::panicking::default_hook
             at /checkout/src/libstd/panicking.rs:391
   4: std::panicking::rust_panic_with_hook
             at /checkout/src/libstd/panicking.rs:577
   5: std::panicking::begin_panic
   6: rustc_errors::Handler::bug
   7: <std::thread::local::LocalKey<T>>::with
   8: rustc::ty::context::tls::with_opt
   9: rustc::session::opt_span_bug_fmt
  10: rustc::session::bug_fmt
  11: rustc::ty::structural_impls::<impl rustc::ty::fold::TypeFoldable<'tcx> for &'tcx rustc::ty::TyS<'tcx>>::super_visit_with
  12: <rustc_mir::borrow_check::nll::constraint_generation::ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> as rustc::mir::visit::Visitor<'tcx>>::visit_basic_block_data
  13: rustc_mir::borrow_check::do_mir_borrowck
  14: <std::thread::local::LocalKey<T>>::with
  15: rustc::ty::context::GlobalCtxt::enter_local
  16: rustc_mir::borrow_check::mir_borrowck
  17: rustc::ty::maps::<impl rustc::ty::maps::queries::mir_borrowck<'tcx>>::compute_result
  18: rustc::dep_graph::graph::DepGraph::with_task_impl
  19: rustc_errors::Handler::track_diagnostics
  20: rustc::ty::maps::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::cycle_check
  21: rustc::ty::maps::<impl rustc::ty::maps::queries::mir_borrowck<'tcx>>::force
  22: rustc::ty::maps::<impl rustc::ty::maps::queries::mir_borrowck<'tcx>>::try_get
  23: rustc::ty::maps::TyCtxtAt::mir_borrowck
  24: rustc::ty::maps::<impl rustc::ty::context::TyCtxt<'a, 'tcx, 'lcx>>::mir_borrowck
  25: rustc_driver::driver::phase_3_run_analysis_passes::{{closure}}::{{closure}}
  26: <std::thread::local::LocalKey<T>>::with
  27: <std::thread::local::LocalKey<T>>::with
  28: rustc::ty::context::TyCtxt::create_and_enter
  29: rustc_driver::driver::compile_input
  30: rustc_driver::run_compiler

My output of rustc --version --verbose is:

rustc 1.24.0-nightly (6fa53b00e 2017-12-09)
binary: rustc
commit-hash: 6fa53b00e7450060a3af9b1ef63169db37e589c2
commit-date: 2017-12-09
host: x86_64-unknown-linux-gnu
release: 1.24.0-nightly
LLVM version: 4.0

Probably related context

I tried the following example (in a file named weird.rs):

use std::io::BufRead;

fn main() {
    let line = std::io::stdin().lock().lines().next().unwrap().unwrap();
    println!("{}", line);
}

I expected rustc -Z borrowck=compare weird.rs to succeed. Instead, I got the following error:

error[E0597]: borrowed value does not live long enough (Ast)
 --> weird.rs:4:72
  |
4 |     let line = std::io::stdin().lock().lines().next().unwrap().unwrap();
  |                ---------------- temporary value created here           ^ temporary value dropped here while still borrowed
  |
  = note: values in a scope are dropped in the opposite order they are created
  = note: consider using a `let` binding to increase its lifetime

error[E0597]: borrowed value does not live long enough (Mir)
 --> weird.rs:4:73
  |
4 |     let line = std::io::stdin().lock().lines().next().unwrap().unwrap();
  |                ---------------- temporary value created here            ^ temporary value dropped here while still borrowed
5 |     println!("{}", line);
6 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

error: aborting due to 2 previous errors

I tried to understand why the borrow checker does not understand that the lock is dropped before the temporary is dropped. So I wrote the following example (in a file named test.rs):

struct Foo(i32);
struct Lock<'a>(&'a Foo);

impl Foo {
    fn new(value: i32) -> Foo { Foo(value) }
    fn lock(&self) -> Lock { Lock(self) }
}

impl<'a> Lock<'a> {
    fn read(self) -> i32 { (self.0).0 }
}

impl<'a> std::ops::Drop for Lock<'a> {
    fn drop(&mut self) {}
}

fn main() {
    let value = {
        Foo::new(42).lock().read() // fail
        // let foo = Foo::new(42); foo.lock().read() // fail
        // let value = Foo::new(42).lock().read(); value // fail
        // let foo = Foo::new(42); let value = foo.lock().read(); value // pass
        // let foo = Foo::new(42); let lock = foo.lock(); lock.read() // pass
    };
    println!("{}", value);
}

I expected rustc -Z borrowck=compare test.rs to succeed for all versions. Instead, only the last 2 versions succeeded. The first 3 versions failed. Notice how the 3rd and 4th versions just differ by assigning the result value into a variable.

Then, I stepped on #21114 and considered using NLL and discovered that rustc -Z nll -Z borrowck=compare test.rs only fails for AST and not for MIR for the first 3 versions. It still succeeds for the last 2 versions. In particular, for the first 3 versions, rustc -Z nll -Z borrowck=mir test.rs succeeds while rustc -Z nll -Z borrowck=ast test.rs fails.

Activity

added
A-NLLArea: Non-lexical lifetimes (NLL)
C-bugCategory: This is a bug.
I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
on Dec 10, 2017
pnkfelix

pnkfelix commented on Dec 13, 2017

@pnkfelix
Member

Thanks for the report!

pnkfelix

pnkfelix commented on Dec 19, 2017

@pnkfelix
Member

FYI here is a version that doesn't rely on reading from stdin (and thus may serve as a better basis for a unit test when this is fixed):

use std::io::*;

fn main() {
    let in_buf: &[u8] = b"a\nb\nc";
    let mut reader = BufReader::with_capacity(2, in_buf);
    let elem = first_line(&mut reader);
    assert_eq!(elem, "a");
}

fn first_line<R: Read>(reader: &mut BufReader<R>) -> String {
    let elem = reader.lines().next().unwrap().unwrap();
    elem
}

(I factored it into two methods because the error here is arising from fn first_line, so having it in its own function may ease inspecting the MIR etc)

added this to the NLL prototype milestone on Dec 19, 2017
arielb1

arielb1 commented on Dec 19, 2017

@arielb1
Contributor
nikomatsakis

nikomatsakis commented on Dec 20, 2017

@nikomatsakis
Contributor

Works on nll-master (i.e., the code found here #46862).

nikomatsakis

nikomatsakis commented on Dec 20, 2017

@nikomatsakis
Contributor

However, the error message on the original example is not quite what I had hoped for:

use std::io::BufRead;

fn main() {
    let line = std::io::stdin().lock().lines().next().unwrap().unwrap();
    println!("{}", line);
}

gives

lunch-box. rustc ~/tmp/weird.rs -Znll -Zborrowck=mir -Znll-dump-cause
error[E0597]: borrowed value does not live long enough
 --> /home/nmatsakis/tmp/weird.rs:4:16
  |
4 |     let line = std::io::stdin().lock().lines().next().unwrap().unwrap();
  |                ^^^^^^^^^^^^^^^^                                         - temporary value only lives until here
  |                |
  |                temporary value does not live long enough
  |
  = note: borrowed value must be valid for lifetime '_#2r...
ia0

ia0 commented on Dec 24, 2017

@ia0
ContributorAuthor

Thanks for the fix! I confirm that the ICE has disappeared in nightly. Should we close the issue? (Or keep it open for the weird.rs case since I would expect this example to type-check?)

removed this from the NLL prototype milestone on Jan 3, 2018
nikomatsakis

nikomatsakis commented on Jan 4, 2018

@nikomatsakis
Contributor
nikomatsakis

nikomatsakis commented on Jan 4, 2018

@nikomatsakis
Contributor

Oh, that's an ICE when the code executes. The example that @pnkfelix gave compiles successfully.

nikomatsakis

nikomatsakis commented on Jan 4, 2018

@nikomatsakis
Contributor

@ia0 the code now type-checks and executes, so I'm going to close the issue.

ia0

ia0 commented on Jan 4, 2018

@ia0
ContributorAuthor

Yes, in the playground stdin seems to be empty, so the first unwrap is wrong (there are no lines to read).

Thanks again for the work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-NLLArea: Non-lexical lifetimes (NLL)C-bugCategory: This is a bug.I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @kennytm@nikomatsakis@pnkfelix@ia0@arielb1

        Issue actions

          ICE with NLL · Issue #46628 · rust-lang/rust