Auto merge of #44985 - zilbuz:issue-44974, r=pnkfelix

MIR borrowck: print lvalues in error messages in the same way that the AST borrowck

Fix #44974

- Print fields with `.name` rather than `.<num>`
- Autoderef values if followed by a field or an index
- Output `[..]` when borrowing inside a slice
This commit is contained in:
bors 2017-10-12 03:40:10 +00:00
commit d27411494a
5 changed files with 439 additions and 35 deletions

View file

@ -14,7 +14,7 @@ use rustc::hir::def_id::{DefId};
use rustc::infer::{InferCtxt};
use rustc::ty::{self, TyCtxt, ParamEnv};
use rustc::ty::maps::Providers;
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue};
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local};
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
use rustc::mir::transform::{MirSource};
@ -1080,49 +1080,52 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
// End-user visible description of `lvalue`
fn describe_lvalue(&self, lvalue: &Lvalue) -> String {
let mut buf = String::new();
self.append_lvalue_to_string(lvalue, &mut buf);
self.append_lvalue_to_string(lvalue, &mut buf, None);
buf
}
// Appends end-user visible description of `lvalue` to `buf`.
fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String) {
fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String, autoderef: Option<bool>) {
match *lvalue {
Lvalue::Local(local) => {
let local = &self.mir.local_decls[local];
match local.name {
Some(name) => buf.push_str(&format!("{}", name)),
None => buf.push_str("_"),
}
self.append_local_to_string(local, buf, "_");
}
Lvalue::Static(ref static_) => {
buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
}
Lvalue::Projection(ref proj) => {
let mut autoderef = autoderef.unwrap_or(false);
let (prefix, suffix, index_operand) = match proj.elem {
ProjectionElem::Deref =>
("(*", format!(")"), None),
ProjectionElem::Deref => {
if autoderef {
("", format!(""), None)
} else {
("(*", format!(")"), None)
}
},
ProjectionElem::Downcast(..) =>
("", format!(""), None), // (dont emit downcast info)
ProjectionElem::Field(field, _ty) =>
("", format!(".{}", field.index()), None), // FIXME: report name of field
ProjectionElem::Index(index) =>
("", format!(""), Some(index)),
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
("", format!("[{} of {}]", offset, min_length), None),
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
("", format!("[-{} of {}]", offset, min_length), None),
ProjectionElem::Subslice { from, to: 0 } =>
("", format!("[{}:]", from), None),
ProjectionElem::Subslice { from: 0, to } =>
("", format!("[:-{}]", to), None),
ProjectionElem::Subslice { from, to } =>
("", format!("[{}:-{}]", from, to), None),
ProjectionElem::Field(field, _ty) => {
autoderef = true;
("", format!(".{}", self.describe_field(&proj.base, field.index())), None)
},
ProjectionElem::Index(index) => {
autoderef = true;
("", format!(""), Some(index))
},
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
autoderef = true;
// Since it isn't possible to borrow an element on a particular index and
// then use another while the borrow is held, don't output indices details
// to avoid confusing the end-user
("", format!("[..]"), None)
},
};
buf.push_str(prefix);
self.append_lvalue_to_string(&proj.base, buf);
self.append_lvalue_to_string(&proj.base, buf, Some(autoderef));
if let Some(index) = index_operand {
buf.push_str("[");
self.append_lvalue_to_string(&Lvalue::Local(index), buf);
self.append_local_to_string(index, buf, "..");
buf.push_str("]");
} else {
buf.push_str(&suffix);
@ -1131,6 +1134,77 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
}
}
// Appends end-user visible description of the `local` lvalue to `buf`. If `local` doesn't have
// a name, then `none_string` is appended instead
fn append_local_to_string(&self, local_index: Local, buf: &mut String, none_string: &str) {
let local = &self.mir.local_decls[local_index];
match local.name {
Some(name) => buf.push_str(&format!("{}", name)),
None => buf.push_str(none_string)
}
}
// End-user visible description of the `field_index`nth field of `base`
fn describe_field(&self, base: &Lvalue, field_index: usize) -> String {
match *base {
Lvalue::Local(local) => {
let local = &self.mir.local_decls[local];
self.describe_field_from_ty(&local.ty, field_index)
},
Lvalue::Static(ref static_) => {
self.describe_field_from_ty(&static_.ty, field_index)
},
Lvalue::Projection(ref proj) => {
match proj.elem {
ProjectionElem::Deref =>
self.describe_field(&proj.base, field_index),
ProjectionElem::Downcast(def, variant_index) =>
format!("{}", def.variants[variant_index].fields[field_index].name),
ProjectionElem::Field(_, field_type) =>
self.describe_field_from_ty(&field_type, field_index),
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } =>
format!("{}", self.describe_field(&proj.base, field_index)),
}
}
}
}
// End-user visible description of the `field_index`nth field of `ty`
fn describe_field_from_ty(&self, ty: &ty::Ty, field_index: usize) -> String {
if ty.is_box() {
// If the type is a box, the field is described from the boxed type
self.describe_field_from_ty(&ty.boxed_ty(), field_index)
}
else {
match ty.sty {
ty::TyAdt(def, _) => {
if def.is_enum() {
format!("{}", field_index)
}
else {
format!("{}", def.struct_variant().fields[field_index].name)
}
},
ty::TyTuple(_, _) => {
format!("{}", field_index)
},
ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
self.describe_field_from_ty(&tnm.ty, field_index)
},
ty::TyArray(ty, _) | ty::TySlice(ty) => {
self.describe_field_from_ty(&ty, field_index)
}
_ => {
// Might need a revision when the fields in trait RFC is implemented
// (https://github.com/rust-lang/rfcs/pull/1546)
bug!("End-user description not implemented for field access on `{:?}`", ty.sty);
}
}
}
}
// Retrieve span of given borrow from the current MIR representation
fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
self.mir.source_info(borrow.location).span

View file

@ -22,7 +22,7 @@ fn a() {
// immutable. Otherwise the type of &_q.x (&isize) would be wrong.
p.x = 5; //[ast]~ ERROR cannot assign to `p.x`
//[mir]~^ ERROR cannot assign to `p.x` because it is borrowed (Ast)
//[mir]~| ERROR cannot assign to `p.0` because it is borrowed (Mir)
//[mir]~| ERROR cannot assign to `p.x` because it is borrowed (Mir)
q.x;
}
@ -47,7 +47,7 @@ fn d() {
let q = &p.y;
p.y = 5; //[ast]~ ERROR cannot assign to `p.y`
//[mir]~^ ERROR cannot assign to `p.y` because it is borrowed (Ast)
//[mir]~| ERROR cannot assign to `p.1` because it is borrowed (Mir)
//[mir]~| ERROR cannot assign to `p.y` because it is borrowed (Mir)
*q;
}

View file

@ -82,7 +82,7 @@ fn g() {
let c1 = || get(&*x.f);
*x.f = 5; //[ast]~ ERROR cannot assign to `*x.f`
//[mir]~^ ERROR cannot assign to `*x.f` because it is borrowed (Ast)
//[mir]~| ERROR cannot assign to `(*(*x).0)` because it is borrowed (Mir)
//[mir]~| ERROR cannot assign to `(*x.f)` because it is borrowed (Mir)
}
fn h() {

View file

@ -0,0 +1,330 @@
// Copyright 2016 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.
// ignore-tidy-linelength
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
#![feature(slice_patterns)]
#![feature(advanced_slice_patterns)]
pub struct Foo {
x: u32
}
pub struct Bar(u32);
pub enum Baz {
X(u32)
}
union U {
a: u8,
b: u64,
}
impl Foo {
fn x(&mut self) -> &mut u32 { &mut self.x }
}
impl Bar {
fn x(&mut self) -> &mut u32 { &mut self.0 }
}
impl Baz {
fn x(&mut self) -> &mut u32 {
match *self {
Baz::X(ref mut value) => value
}
}
}
static mut sfoo : Foo = Foo{x: 23 };
static mut sbar : Bar = Bar(23);
static mut stuple : (i32, i32) = (24, 25);
static mut senum : Baz = Baz::X(26);
static mut sunion : U = U { a: 0 };
fn main() {
// Local and field from struct
{
let mut f = Foo { x: 22 };
let _x = f.x();
f.x; //[ast]~ ERROR cannot use `f.x` because it was mutably borrowed
//[mir]~^ ERROR cannot use `f.x` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `f.x` because it was mutably borrowed (Mir)
}
// Local and field from tuple-struct
{
let mut g = Bar(22);
let _0 = g.x();
g.0; //[ast]~ ERROR cannot use `g.0` because it was mutably borrowed
//[mir]~^ ERROR cannot use `g.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `g.0` because it was mutably borrowed (Mir)
}
// Local and field from tuple
{
let mut h = (22, 23);
let _0 = &mut h.0;
h.0; //[ast]~ ERROR cannot use `h.0` because it was mutably borrowed
//[mir]~^ ERROR cannot use `h.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `h.0` because it was mutably borrowed (Mir)
}
// Local and field from enum
{
let mut e = Baz::X(2);
let _e0 = e.x();
match e {
Baz::X(value) => value
//[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `e.0` because it was mutably borrowed (Mir)
};
}
// Local and field from union
unsafe {
let mut u = U { b: 0 };
let _ra = &mut u.a;
u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
//[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `u.a` because it was mutably borrowed (Mir)
}
// Static and field from struct
unsafe {
let _x = sfoo.x();
sfoo.x; //[mir]~ ERROR cannot use `sfoo.x` because it was mutably borrowed (Mir)
}
// Static and field from tuple-struct
unsafe {
let _0 = sbar.x();
sbar.0; //[mir]~ ERROR cannot use `sbar.0` because it was mutably borrowed (Mir)
}
// Static and field from tuple
unsafe {
let _0 = &mut stuple.0;
stuple.0; //[mir]~ ERROR cannot use `stuple.0` because it was mutably borrowed (Mir)
}
// Static and field from enum
unsafe {
let _e0 = senum.x();
match senum {
Baz::X(value) => value
//[mir]~^ ERROR cannot use `senum.0` because it was mutably borrowed (Mir)
};
}
// Static and field from union
unsafe {
let _ra = &mut sunion.a;
sunion.a; //[mir]~ ERROR cannot use `sunion.a` because it was mutably borrowed (Mir)
}
// Deref and field from struct
{
let mut f = Box::new(Foo { x: 22 });
let _x = f.x();
f.x; //[ast]~ ERROR cannot use `f.x` because it was mutably borrowed
//[mir]~^ ERROR cannot use `f.x` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `f.x` because it was mutably borrowed (Mir)
}
// Deref and field from tuple-struct
{
let mut g = Box::new(Bar(22));
let _0 = g.x();
g.0; //[ast]~ ERROR cannot use `g.0` because it was mutably borrowed
//[mir]~^ ERROR cannot use `g.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `g.0` because it was mutably borrowed (Mir)
}
// Deref and field from tuple
{
let mut h = Box::new((22, 23));
let _0 = &mut h.0;
h.0; //[ast]~ ERROR cannot use `h.0` because it was mutably borrowed
//[mir]~^ ERROR cannot use `h.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `h.0` because it was mutably borrowed (Mir)
}
// Deref and field from enum
{
let mut e = Box::new(Baz::X(3));
let _e0 = e.x();
match *e {
Baz::X(value) => value
//[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `e.0` because it was mutably borrowed (Mir)
};
}
// Deref and field from union
unsafe {
let mut u = Box::new(U { b: 0 });
let _ra = &mut u.a;
u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
//[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `u.a` because it was mutably borrowed (Mir)
}
// Constant index
{
let mut v = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let _v = &mut v;
match v {
&[x, _, .., _, _] => println!("{}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[_, x, .., _, _] => println!("{}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[_, _, .., x, _] => println!("{}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[_, _, .., _, x] => println!("{}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
}
// Subslices
{
let mut v = &[1, 2, 3, 4, 5];
let _v = &mut v;
match v {
&[x..] => println!("{:?}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[_, x..] => println!("{:?}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[x.., _] => println!("{:?}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
match v {
&[_, x.., _] => println!("{:?}", x),
//[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..]` because it was mutably borrowed (Mir)
_ => panic!("other case"),
}
}
// Downcasted field
{
enum E<X> { A(X), B { x: X } }
let mut e = E::A(3);
let _e = &mut e;
match e {
E::A(ref ax) =>
//[ast]~^ ERROR cannot borrow `e.0` as immutable because `e` is also borrowed as mutable
//[mir]~^^ ERROR cannot borrow `e.0` as immutable because `e` is also borrowed as mutable (Ast)
//[mir]~| ERROR cannot borrow `e.0` as immutable because it is also borrowed as mutable (Mir)
//[mir]~| ERROR cannot use `e` because it was mutably borrowed (Mir)
println!("e.ax: {:?}", ax),
E::B { x: ref bx } =>
//[ast]~^ ERROR cannot borrow `e.x` as immutable because `e` is also borrowed as mutable
//[mir]~^^ ERROR cannot borrow `e.x` as immutable because `e` is also borrowed as mutable (Ast)
//[mir]~| ERROR cannot borrow `e.x` as immutable because it is also borrowed as mutable (Mir)
println!("e.bx: {:?}", bx),
}
}
// Field in field
{
struct F { x: u32, y: u32 };
struct S { x: F, y: (u32, u32), };
let mut s = S { x: F { x: 1, y: 2}, y: (999, 998) };
let _s = &mut s;
match s {
S { y: (ref y0, _), .. } =>
//[ast]~^ ERROR cannot borrow `s.y.0` as immutable because `s` is also borrowed as mutable
//[mir]~^^ ERROR cannot borrow `s.y.0` as immutable because `s` is also borrowed as mutable (Ast)
//[mir]~| ERROR cannot borrow `s.y.0` as immutable because it is also borrowed as mutable (Mir)
println!("y0: {:?}", y0),
_ => panic!("other case"),
}
match s {
S { x: F { y: ref x0, .. }, .. } =>
//[ast]~^ ERROR cannot borrow `s.x.y` as immutable because `s` is also borrowed as mutable
//[mir]~^^ ERROR cannot borrow `s.x.y` as immutable because `s` is also borrowed as mutable (Ast)
//[mir]~| ERROR cannot borrow `s.x.y` as immutable because it is also borrowed as mutable (Mir)
println!("x0: {:?}", x0),
_ => panic!("other case"),
}
}
// Field of ref
{
struct Block<'a> {
current: &'a u8,
unrelated: &'a u8,
};
fn bump<'a>(mut block: &mut Block<'a>) {
let x = &mut block;
let p: &'a u8 = &*block.current;
//[mir]~^ ERROR cannot borrow `(*block.current)` as immutable because it is also borrowed as mutable (Mir)
// No errors in AST because of issue rust#38899
}
}
// Field of ptr
{
struct Block2 {
current: *const u8,
unrelated: *const u8,
}
unsafe fn bump2(mut block: *mut Block2) {
let x = &mut block;
let p : *const u8 = &*(*block).current;
//[mir]~^ ERROR cannot borrow `(*block.current)` as immutable because it is also borrowed as mutable (Mir)
// No errors in AST because of issue rust#38899
}
}
// Field of index
{
struct F {x: u32, y: u32};
let mut v = &[F{x: 1, y: 2}, F{x: 3, y: 4}];
let _v = &mut v;
v[0].y;
//[ast]~^ ERROR cannot use `v[..].y` because it was mutably borrowed
//[mir]~^^ ERROR cannot use `v[..].y` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `v[..].y` because it was mutably borrowed (Mir)
//[mir]~| ERROR cannot use `(*v)` because it was mutably borrowed (Mir)
}
// Field of constant index
{
struct F {x: u32, y: u32};
let mut v = &[F{x: 1, y: 2}, F{x: 3, y: 4}];
let _v = &mut v;
match v {
&[_, F {x: ref xf, ..}] => println!("{}", xf),
//[mir]~^ ERROR cannot borrow `v[..].x` as immutable because it is also borrowed as mutable (Mir)
// No errors in AST
_ => panic!("other case")
}
}
}

View file

@ -34,13 +34,13 @@ fn main() {
let ra = &u.a;
let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
//[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable (Ast)
//[mir]~| ERROR cannot borrow `u.0` as mutable because it is also borrowed as immutable (Mir)
//[mir]~| ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable (Mir)
}
{
let ra = &u.a;
u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
//[mir]~^ ERROR cannot assign to `u.a` because it is borrowed (Ast)
//[mir]~| ERROR cannot assign to `u.0` because it is borrowed (Mir)
//[mir]~| ERROR cannot assign to `u.a` because it is borrowed (Mir)
}
// Imm borrow, other field
{
@ -68,25 +68,25 @@ fn main() {
let rma = &mut u.a;
let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
//[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable (Ast)
//[mir]~| ERROR cannot borrow `u.0` as immutable because it is also borrowed as mutable (Mir)
//[mir]~| ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable (Mir)
}
{
let ra = &mut u.a;
let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
//[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed (Ast)
//[mir]~| ERROR cannot use `u.0` because it was mutably borrowed (Mir)
//[mir]~| ERROR cannot use `u.a` because it was mutably borrowed (Mir)
}
{
let rma = &mut u.a;
let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time
//[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time (Ast)
//[mir]~| ERROR cannot borrow `u.0` as mutable more than once at a time (Mir)
//[mir]~| ERROR cannot borrow `u.a` as mutable more than once at a time (Mir)
}
{
let rma = &mut u.a;
u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
//[mir]~^ ERROR cannot assign to `u.a` because it is borrowed (Ast)
//[mir]~| ERROR cannot assign to `u.0` because it is borrowed (Mir)
//[mir]~| ERROR cannot assign to `u.a` because it is borrowed (Mir)
}
// Mut borrow, other field
{