Restrict two-phase borrows to solely borrows introduced via autoref.

Added `-Z two-phase-beyond-autoref` to bring back old behavior (mainly
to allow demonstration of desugared examples).

Updated tests to use aforementioned flag when necessary. (But in each
case where I added the flag, I made sure to also include a revision
without the flag so that one can readily see what the actual behavior
we expect is for the initial deployment of NLL.)
This commit is contained in:
Felix S. Klock II 2018-01-16 10:52:52 +01:00
parent c00266b7ac
commit 1855ab7424
6 changed files with 89 additions and 20 deletions

View file

@ -8,9 +8,13 @@
// 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
// ignore-tidy-linelength
// revisions: lxl_beyond nll_beyond nll_target
//[lxl_beyond] compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two-phase-beyond-autoref
//[nll_beyond] compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two-phase-beyond-autoref -Z nll
//[nll_target] 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
@ -18,6 +22,14 @@
//
// FIXME: for clarity, diagnostics for these cases might be better off
// if they specifically said "cannot activate mutable borrow of `x`"
//
// The convention for the listed revisions: "lxl" means lexical
// lifetimes (which can be easier to reason about). "nll" means
// non-lexical lifetimes. "nll_target" means the initial conservative
// two-phase borrows that only applies to autoref-introduced borrows.
// "nll_beyond" means the generalization of two-phase borrows to all
// `&mut`-borrows (doing so makes it easier to write code for specific
// corner cases).
#![allow(dead_code)]
@ -27,6 +39,7 @@ fn ok() {
let mut x = 3;
let y = &mut x;
{ let z = &x; read(z); }
//[nll_target]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable
*y += 1;
}
@ -34,9 +47,11 @@ fn not_ok() {
let mut x = 3;
let y = &mut x;
let z = &x;
//[nll_target]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable
*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
//[lxl_beyond]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
//[nll_beyond]~^^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
//[nll_target]~^^^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
read(z);
}
@ -44,18 +59,21 @@ fn should_be_ok_with_nll() {
let mut x = 3;
let y = &mut x;
let z = &x;
//[nll_target]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable
read(z);
*y += 1;
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
// (okay with nll today)
//[lxl_beyond]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
// (okay with (generalized) nll today)
}
fn should_also_eventually_be_ok_with_nll() {
let mut x = 3;
let y = &mut x;
let _z = &x;
//[nll_target]~^ ERROR cannot borrow `x` as immutable because it is also borrowed as mutable
*y += 1;
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
//[lxl_beyond]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
// (okay with (generalized) nll today)
}
fn main() { }

View file

@ -8,9 +8,12 @@
// 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
// ignore-tidy-linelength
// revisions: lxl_beyond nll_beyond nll_target
//[lxl_beyond] compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two_phase_beyond_autoref
//[nll_beyond] compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two_phase_beyond_autoref -Z nll
//[nll_target] 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/
@ -18,6 +21,14 @@
// 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.
//
// The convention for the listed revisions: "lxl" means lexical
// lifetimes (which can be easier to reason about). "nll" means
// non-lexical lifetimes. "nll_target" means the initial conservative
// two-phase borrows that only applies to autoref-introduced borrows.
// "nll_beyond" means the generalization of two-phase borrows to all
// `&mut`-borrows (doing so makes it easier to write code for specific
// corner cases).
fn main() {
/*0*/ let mut i = 0;
@ -25,11 +36,13 @@ fn main() {
/*1*/ let p = &mut i; // (reservation of `i` starts here)
/*2*/ let j = i; // OK: `i` is only reserved here
//[nll_target]~^ ERROR cannot use `i` because it was mutably borrowed [E0503]
/*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]
/*4*/ let k = i; //[lxl_beyond]~ ERROR cannot use `i` because it was mutably borrowed [E0503]
//[nll_beyond]~^ ERROR cannot use `i` because it was mutably borrowed [E0503]
//[nll_target]~^^ ERROR cannot use `i` because it was mutably borrowed [E0503]
/*5*/ *p += 1;

View file

@ -8,9 +8,13 @@
// 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
// ignore-tidy-linelength
// revisions: lxl_beyond nll_beyond nll_target
//[lxl_beyond]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two-phase-beyond-autoref
//[nll_beyond]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z two-phase-beyond-autoref -Z nll
//[nll_target]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
@ -19,6 +23,18 @@
// 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.
//
// The convention for the listed revisions: "lxl" means lexical
// lifetimes (which can be easier to reason about). "nll" means
// non-lexical lifetimes. "nll_target" means the initial conservative
// two-phase borrows that only applies to autoref-introduced borrows.
// "nll_beyond" means the generalization of two-phase borrows to all
// `&mut`-borrows (doing so makes it easier to write code for specific
// corner cases).
//
// FIXME: in "nll_target", we currently see the same error reported
// twice. This is injected by `-Z two-phase-borrows`; not sure why as
// of yet.
fn main() {
let mut vec = vec![0, 1];
@ -30,8 +46,10 @@ fn main() {
// 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
//[lxl_beyond]~^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
//[nll_beyond]~^^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
//[nll_target]~^^^ ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
//[nll_target]~| ERROR cannot borrow `vec` as mutable because it is also borrowed as immutable
shared[0];
}