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

ICE with NLL #46628

Closed
ia0 opened this issue Dec 10, 2017 · 10 comments
Closed

ICE with NLL #46628

ia0 opened this issue Dec 10, 2017 · 10 comments
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️

Comments

@ia0
Copy link
Contributor

ia0 commented Dec 10, 2017

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.

@kennytm kennytm added A-NLL Area: Non-lexical lifetimes (NLL) C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ WG-compiler-nll labels Dec 10, 2017
@pnkfelix
Copy link
Member

Thanks for the report!

@pnkfelix
Copy link
Member

pnkfelix commented Dec 19, 2017

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)

@arielb1 arielb1 added this to the NLL prototype milestone Dec 19, 2017
@arielb1
Copy link
Contributor

arielb1 commented Dec 19, 2017

cc @nikomatsakis

@nikomatsakis
Copy link
Contributor

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

@nikomatsakis
Copy link
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
Copy link
Contributor Author

ia0 commented Dec 24, 2017

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?)

@nikomatsakis nikomatsakis removed this from the NLL prototype milestone Jan 3, 2018
@nikomatsakis
Copy link
Contributor

@nikomatsakis
Copy link
Contributor

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

@nikomatsakis
Copy link
Contributor

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

@ia0
Copy link
Contributor Author

ia0 commented Jan 4, 2018

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
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
Projects
None yet
Development

No branches or pull requests

5 participants