Auto merge of #46537 - pnkfelix:two-phase-borrows, r=arielb1
[MIR-borrowck] Two phase borrows This adds limited support for two-phase borrows as described in http://smallcultfollowing.com/babysteps/blog/2017/03/01/nested-method-calls-via-two-phase-borrowing/ The support is off by default; you opt into it via the flag `-Z two-phase-borrows` I have written "*limited* support" above because there are simple variants of the simple `v.push(v.len())` example that one would think should work but currently do not, such as the one documented in the test compile-fail/borrowck/two-phase-reservation-sharing-interference-2.rs (To be clear, that test is not describing something that is unsound. It is just providing an explicit example of a limitation in the implementation given in this PR. I have ideas on how to fix, but I want to land the work that is in this PR first, so that I can stop repeatedly rebasing this branch.)
This commit is contained in:
commit
84feab34e4
25 changed files with 1183 additions and 256 deletions
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is an important corner case pointed out by Niko: one is
|
||||
// allowed to initiate a shared borrow during a reservation, but it
|
||||
// *must end* before the activation occurs.
|
||||
//
|
||||
// FIXME: for clarity, diagnostics for these cases might be better off
|
||||
// if they specifically said "cannot activate mutable borrow of `x`"
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
fn read(_: &i32) { }
|
||||
|
||||
fn ok() {
|
||||
let mut x = 3;
|
||||
let y = &mut x;
|
||||
{ let z = &x; read(z); }
|
||||
*y += 1;
|
||||
}
|
||||
|
||||
fn not_ok() {
|
||||
let mut x = 3;
|
||||
let y = &mut x;
|
||||
let z = &x;
|
||||
*y += 1;
|
||||
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
|
||||
//[nll]~^^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
|
||||
read(z);
|
||||
}
|
||||
|
||||
fn should_be_ok_with_nll() {
|
||||
let mut x = 3;
|
||||
let y = &mut x;
|
||||
let z = &x;
|
||||
read(z);
|
||||
*y += 1;
|
||||
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
|
||||
// (okay with nll today)
|
||||
}
|
||||
|
||||
fn should_also_eventually_be_ok_with_nll() {
|
||||
let mut x = 3;
|
||||
let y = &mut x;
|
||||
let _z = &x;
|
||||
*y += 1;
|
||||
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
|
||||
//[nll]~^^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is the second counter-example from Niko's blog post
|
||||
// smallcultfollowing.com/babysteps/blog/2017/03/01/nested-method-calls-via-two-phase-borrowing/
|
||||
//
|
||||
// It is "artificial". It is meant to illustrate directly that we
|
||||
// should allow an aliasing access during reservation, but *not* while
|
||||
// the mutable borrow is active.
|
||||
|
||||
fn main() {
|
||||
/*0*/ let mut i = 0;
|
||||
|
||||
/*1*/ let p = &mut i; // (reservation of `i` starts here)
|
||||
|
||||
/*2*/ let j = i; // OK: `i` is only reserved here
|
||||
|
||||
/*3*/ *p += 1; // (mutable borrow of `i` starts here, since `p` is used)
|
||||
|
||||
/*4*/ let k = i; //[lxl]~ ERROR cannot use `i` because it was mutably borrowed [E0503]
|
||||
//[nll]~^ ERROR cannot use `i` because it was mutably borrowed [E0503]
|
||||
|
||||
/*5*/ *p += 1;
|
||||
|
||||
let _ = (j, k, p);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is the third counter-example from Niko's blog post
|
||||
// smallcultfollowing.com/babysteps/blog/2017/03/01/nested-method-calls-via-two-phase-borrowing/
|
||||
//
|
||||
// It shows that not all nested method calls on `self` are magically
|
||||
// allowed by this change. In particular, a nested `&mut` borrow is
|
||||
// still disallowed.
|
||||
|
||||
fn main() {
|
||||
|
||||
|
||||
let mut vec = vec![0, 1];
|
||||
vec.get({
|
||||
|
||||
vec.push(2);
|
||||
//[lxl]~^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
|
||||
//[nll]~^^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
|
||||
|
||||
0
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is similar to two-phase-reservation-sharing-interference.rs
|
||||
// in that it shows a reservation that overlaps with a shared borrow.
|
||||
//
|
||||
// Currently, this test fails with lexical lifetimes, but succeeds
|
||||
// with non-lexical lifetimes. (The reason is because the activation
|
||||
// of the mutable borrow ends up overlapping with a lexically-scoped
|
||||
// shared borrow; but a non-lexical shared borrow can end before the
|
||||
// activation occurs.)
|
||||
//
|
||||
// So this test is just making a note of the current behavior.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { //[nll]~ ERROR compilation successful
|
||||
let mut v = vec![0, 1, 2];
|
||||
let shared = &v;
|
||||
|
||||
v.push(shared.len());
|
||||
//[lxl]~^ ERROR cannot borrow `v` as mutable because it is also borrowed as immutable [E0502]
|
||||
|
||||
assert_eq!(v, [0, 1, 2, 3]);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is a corner case that the current implementation is (probably)
|
||||
// treating more conservatively than is necessary. But it also does
|
||||
// not seem like a terribly important use case to cover.
|
||||
//
|
||||
// So this test is just making a note of the current behavior, with
|
||||
// the caveat that in the future, the rules may be loosened, at which
|
||||
// point this test might be thrown out.
|
||||
|
||||
fn main() {
|
||||
let mut vec = vec![0, 1];
|
||||
let delay: &mut Vec<_>;
|
||||
{
|
||||
let shared = &vec;
|
||||
|
||||
// we reserve here, which could (on its own) be compatible
|
||||
// with the shared borrow. But in the current implementation,
|
||||
// its an error.
|
||||
delay = &mut vec;
|
||||
//[lxl]~^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
|
||||
//[nll]~^^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
|
||||
|
||||
shared[0];
|
||||
}
|
||||
|
||||
// the &mut-borrow only becomes active way down here.
|
||||
//
|
||||
// (At least in theory; part of the reason this test fails is that
|
||||
// the constructed MIR throws in extra &mut reborrows which
|
||||
// flummoxes our attmpt to delay the activation point here.)
|
||||
delay.push(2);
|
||||
}
|
||||
|
||||
30
src/test/compile-fail/borrowck/two-phase-sneaky.rs
Normal file
30
src/test/compile-fail/borrowck/two-phase-sneaky.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is the first counter-example from Niko's blog post
|
||||
// smallcultfollowing.com/babysteps/blog/2017/03/01/nested-method-calls-via-two-phase-borrowing/
|
||||
// of a danger for code to crash if we just turned off the check for whether
|
||||
// a mutable-borrow aliases another borrow.
|
||||
|
||||
fn main() {
|
||||
let mut v: Vec<String> = vec![format!("Hello, ")];
|
||||
v[0].push_str({
|
||||
|
||||
v.push(format!("foo"));
|
||||
//[lxl]~^ ERROR cannot borrow `v` as mutable more than once at a time [E0499]
|
||||
//[nll]~^^ ERROR cannot borrow `v` as mutable more than once at a time [E0499]
|
||||
|
||||
"World!"
|
||||
});
|
||||
}
|
||||
21
src/test/run-pass/borrowck/two-phase-baseline.rs
Normal file
21
src/test/run-pass/borrowck/two-phase-baseline.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
// This is the "goto example" for why we want two phase borrows.
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![0, 1, 2];
|
||||
v.push(v.len());
|
||||
assert_eq!(v, [0, 1, 2, 3]);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: lxl nll
|
||||
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
|
||||
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
|
||||
|
||||
fn main() {
|
||||
let mut a = 0;
|
||||
let mut b = 0;
|
||||
let p = if maybe() {
|
||||
&mut a
|
||||
} else {
|
||||
&mut b
|
||||
};
|
||||
use_(p);
|
||||
}
|
||||
|
||||
fn maybe() -> bool { false }
|
||||
fn use_<T>(_: T) { }
|
||||
Loading…
Add table
Add a link
Reference in a new issue