-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
two ref mut
patterns with same name used in arm guard causes ICE on NLL
#51348
Comments
Was this working with a previous nightly? |
It's a little hard to evaluate as we're using a fork of |
This also happens with |
It also seems to be related to two-phased borrows? |
Okay, here's a reproducing failure case. Happens only with NLL as far as I can tell. #![feature(nll)]
pub struct Foo {
pub bar: Option<String>,
pub baz: Option<Box<Baz>>,
}
pub enum Baz {
A(Foo),
B(Foo, String),
}
fn sadness() {
use Baz::*;
let _fail = |mut f: Foo| -> Foo {
f.bar = match f.bar {
None => match f.baz {
Some(ref mut f) => {
match (*f).as_mut() {
| &mut A(ref mut foo) | &mut B(ref mut foo, _) if foo.bar.is_none() => {
foo.bar = None;
}
_ => {}
}
None
}
None => None,
},
Some(x) => Some(x),
};
f
};
} $ cargo check
Checking sadness v0.1.0 (file:///home/jon/d)
thread 'main' panicked at 'assertion failed: old_value.is_none()', librustc_mir/borrow_check/borrow_set.rs:350:9
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at libstd/sys_common/backtrace.rs:71
at libstd/sys_common/backtrace.rs:59
2: std::panicking::default_hook::{{closure}}
at libstd/panicking.rs:211
3: std::panicking::default_hook
at libstd/panicking.rs:227
4: rustc::util::common::panic_hook
5: std::panicking::rust_panic_with_hook
at libstd/panicking.rs:515
6: std::panicking::begin_panic
7: <rustc_mir::borrow_check::borrow_set::GatherBorrows<'a, 'gcx, 'tcx> as rustc::mir::visit::Visitor<'tcx>>::visit_assign
8: rustc_mir::borrow_check::borrow_set::BorrowSet::build
9: rustc_mir::borrow_check::do_mir_borrowck
10: rustc::ty::context::tls::with_related_context
11: rustc::infer::InferCtxtBuilder::enter
12: rustc_mir::borrow_check::mir_borrowck
13: rustc::ty::query::__query_compute::mir_borrowck
14: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors<'tcx> for rustc::ty::query::queries::mir_borrowck<'tcx>>::compute
15: rustc::ty::context::tls::with_context
16: rustc::dep_graph::graph::DepGraph::with_task_impl
17: rustc::ty::context::tls::with_related_context
18: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::force_query_with_job
19: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::get_query
20: rustc::ty::query::<impl rustc::ty::context::TyCtxt<'a, 'tcx, 'lcx>>::mir_borrowck
21: rustc_mir::borrow_check::nll::type_check::TypeChecker::typeck_mir
22: rustc_mir::borrow_check::nll::type_check::type_check_internal
23: rustc_mir::borrow_check::nll::compute_regions
24: rustc_mir::borrow_check::do_mir_borrowck
25: rustc::ty::context::tls::with_related_context
26: rustc::infer::InferCtxtBuilder::enter
27: rustc_mir::borrow_check::mir_borrowck
28: rustc::ty::query::__query_compute::mir_borrowck
29: rustc::ty::query::<impl rustc::ty::query::config::QueryAccessors<'tcx> for rustc::ty::query::queries::mir_borrowck<'tcx>>::compute
30: rustc::ty::context::tls::with_context
31: rustc::dep_graph::graph::DepGraph::with_task_impl
32: rustc::ty::context::tls::with_related_context
33: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::force_query_with_job
34: rustc::ty::query::plumbing::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::get_query
35: rustc::ty::query::<impl rustc::ty::context::TyCtxt<'a, 'tcx, 'lcx>>::mir_borrowck
36: rustc::ty::<impl rustc::ty::context::TyCtxt<'a, 'gcx, 'tcx>>::par_body_owners
37: rustc::util::common::time
38: rustc::ty::context::tls::enter_context
39: <std::thread::local::LocalKey<T>>::with
40: rustc::ty::context::TyCtxt::create_and_enter
41: rustc_driver::driver::compile_input
42: rustc_driver::run_compiler_with_pool
43: <scoped_tls::ScopedKey<T>>::set
44: syntax::with_globals
45: <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
46: __rust_maybe_catch_panic
at libpanic_unwind/lib.rs:105
47: rustc_driver::run
48: rustc_driver::main
49: std::rt::lang_start::{{closure}}
50: std::panicking::try::do_call
at libstd/rt.rs:59
at libstd/panicking.rs:310
51: __rust_maybe_catch_panic
at libpanic_unwind/lib.rs:105
52: std::rt::lang_start_internal
at libstd/panicking.rs:289
at libstd/panic.rs:397
at libstd/rt.rs:58
53: main
54: __libc_start_main
55: <unknown>
query stack during panic:
#0 [mir_borrowck] processing `sadness::{{closure}}`
#1 [mir_borrowck] processing `sadness`
end of query stack
error: internal compiler error: unexpected panic
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.28.0-nightly (2a1c4eec4 2018-06-25) running on x86_64-unknown-linux-gnu
note: compiler flags: -C debuginfo=2 -C incremental -C target-cpu=native --crate-type lib
note: some of the compiler flags provided by cargo are hidden |
OK so the error is arising because of the combination of
For example this is a (more) minimal reproduction: #![feature(nll)]
fn foo(x: &mut Result<u32, u32>) {
match x {
Ok(v) | Err(v) if *v > 0 => { }
_ => { }
}
}
fn main() { } |
The problem seems to be that, during the match desugaring, we re-use the temporaries for the
but we also mark the Probably we could make a more complex def'n of "two-phaseness" -- that is, the two borrows here use the same temporary, but they have disjoint activation periods, and they cannot reach one another, so we could probably engineer something that permits this MIR as is to work. Alternatively, maybe we can lower via distinct temporaries and that would be fine. It is crucial though that the first use of the temporary |
|
Certainly not an Edition Preview blocker though. |
OK, so, I still think this is not an EP blocker, but for a different reason: Basically, the situation is obscure enough. But I would consider it an RC blocker. |
ref mut
patterns with same name used in arm guard causes ICE on NLL
Nominating for reconsideration ... should we try to tackle this for EP2? (cc @rust-lang/wg-compiler-nll) |
I'll take a shot at this today. |
Interestingly, I was vaguely worried about making sure we had a test case that covered this scenario: fn foo(mut x: Result<u32, &mut u32>) {
match x {
Ok(ref mut v) | Err(v) if *v > 0 => { }
_ => { }
}
} but it turns out (of course?) that doing that is illegal: One of the patterns is non-moving and the other moves, and you cannot do that within a single match arm.
(it is also illegal to bind-by-move into a pattern guard, but that is a limitation that we seek to remove long term, so i don't intend to depend on that constraint.) |
I have something plausible. Not sure if we want to actually land it for EP2, but we'll see. |
… *for each* candidate pat. This required a bit of plumbing to keep track of candidates. But I took advantage of the hack session to try to improve the docs for the relevant structs here. (I also tried to simplify some of the related code in passing.)
…ate-in-arm, r=nikomatsakis [NLL] make temp for each candidate in `match` arm In NLL, `ref mut` patterns leverage the two-phase borrow infrastructure to allow the shared borrows within a guard before the "activation" of the mutable borrow when we begin execution of the match arm's body. (There is further discussion of this on PR #50783.) To accommodate the restrictions we impose on two-phase borrows (namely that there is a one-to-one mapping between each activation and the original initialization), this PR is making separate temps for each candidate pattern. So in an arm like this: ```rust PatA(_, ref mut ident) | PatB(ref mut ident) | PatC(_, _, ref mut ident) | PatD(ref mut ident) if guard_stuff(ident) => ... ``` instead of 3 temps (two for the guard and one for the arm body), we now have 4 + 2 temps associated with `ident`: one for each candidate plus the actual temp that the guard uses directly, and then the sixth is the temp used in the arm body. Fix #51348
To reproduce:
Original bug report:
To reproduce:
The text was updated successfully, but these errors were encountered: