Auto merge of #45016 - pnkfelix:mir-borrowck-gather-and-signal-move-errors, r=nikomatsakis

MIR-borrowck: gather and signal any move errors

When building up the `MoveData` structure for a given MIR, also accumulate any erroneous actions, and then report all of those errors when the construction is complete.

This PR adds a host of move-related error constructor methods to `trait BorrowckErrors`. I think I got the notes right; but we should plan to audit all of the notes before turning MIR-borrowck on by default.

Fix #44830
This commit is contained in:
bors 2017-10-08 18:12:26 +00:00
commit 650b1b1f3a
17 changed files with 557 additions and 379 deletions

View File

@ -260,6 +260,19 @@ impl<'tcx> Mir<'tcx> {
debug_assert!(location.statement_index < block.statements.len());
block.statements[location.statement_index].make_nop()
}
/// Returns the source info associated with `location`.
pub fn source_info(&self, location: Location) -> &SourceInfo {
let block = &self[location.block];
let stmts = &block.statements;
let idx = location.statement_index;
if location.statement_index < stmts.len() {
&stmts[idx].source_info
} else {
assert!(location.statement_index == stmts.len());
&block.terminator().source_info
}
}
}
#[derive(Clone, Debug)]

View File

@ -14,6 +14,7 @@ use rustc::middle::mem_categorization::Categorization;
use rustc::middle::mem_categorization::NoteClosureEnv;
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
use rustc::ty;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use syntax::ast;
use syntax_pos;
use errors::DiagnosticBuilder;
@ -134,7 +135,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
}
// (keep in sync with gather_moves::check_and_get_illegal_move_origin )
fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>,
move_from: mc::cmt<'tcx>)
-> DiagnosticBuilder<'a> {
match move_from.cat {
@ -142,43 +143,21 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
Categorization::Deref(_, mc::Implicit(..)) |
Categorization::Deref(_, mc::UnsafePtr(..)) |
Categorization::StaticItem => {
let mut err = struct_span_err!(bccx, move_from.span, E0507,
"cannot move out of {}",
move_from.descriptive_string(bccx.tcx));
err.span_label(
move_from.span,
format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx))
);
err
bccx.cannot_move_out_of(
move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast)
}
Categorization::Interior(ref b, mc::InteriorElement(ik)) => {
let type_name = match (&b.ty.sty, ik) {
(&ty::TyArray(_, _), Kind::Index) => "array",
(&ty::TySlice(_), _) => "slice",
_ => {
span_bug!(move_from.span, "this path should not cause illegal move");
},
};
let mut err = struct_span_err!(bccx, move_from.span, E0508,
"cannot move out of type `{}`, \
a non-copy {}",
b.ty, type_name);
err.span_label(move_from.span, "cannot move out of here");
err
bccx.cannot_move_out_of_interior_noncopy(
move_from.span, b.ty, ik == Kind::Index, Origin::Ast)
}
Categorization::Downcast(ref b, _) |
Categorization::Interior(ref b, mc::InteriorField(_)) => {
match b.ty.sty {
ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => {
let mut err = struct_span_err!(bccx, move_from.span, E0509,
"cannot move out of type `{}`, \
which implements the `Drop` trait",
b.ty);
err.span_label(move_from.span, "cannot move out of here");
err
},
bccx.cannot_move_out_of_interior_of_drop(
move_from.span, b.ty, Origin::Ast)
}
_ => {
span_bug!(move_from.span, "this path should not cause illegal move");
}

View File

@ -317,272 +317,6 @@ fn main() {
```
"##,
E0507: r##"
You tried to move out of a value which was borrowed. Erroneous code example:
```compile_fail,E0507
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // error: cannot move out of borrowed content
}
```
Here, the `nothing_is_true` method takes the ownership of `self`. However,
`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`,
which is a borrow of the content owned by the `RefCell`. To fix this error,
you have three choices:
* Try to avoid moving the variable.
* Somehow reclaim the ownership.
* Implement the `Copy` trait on the type.
Examples:
```
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(&self) {} // First case, we don't take ownership
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // ok!
}
```
Or:
```
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
let x = x.into_inner(); // we get back ownership
x.nothing_is_true(); // ok!
}
```
Or:
```
use std::cell::RefCell;
#[derive(Clone, Copy)] // we implement the Copy trait
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // ok!
}
```
Moving a member out of a mutably borrowed struct will also cause E0507 error:
```compile_fail,E0507
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
struct Batcave {
knight: TheDarkKnight
}
fn main() {
let mut cave = Batcave {
knight: TheDarkKnight
};
let borrowed = &mut cave;
borrowed.knight.nothing_is_true(); // E0507
}
```
It is fine only if you put something back. `mem::replace` can be used for that:
```
# struct TheDarkKnight;
# impl TheDarkKnight { fn nothing_is_true(self) {} }
# struct Batcave { knight: TheDarkKnight }
use std::mem;
let mut cave = Batcave {
knight: TheDarkKnight
};
let borrowed = &mut cave;
mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok!
```
You can find more information about borrowing in the rust-book:
http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
"##,
E0508: r##"
A value was moved out of a non-copy fixed-size array.
Example of erroneous code:
```compile_fail,E0508
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`,
// a non-copy fixed-size array
}
```
The first element was moved out of the array, but this is not
possible because `NonCopy` does not implement the `Copy` trait.
Consider borrowing the element instead of moving it:
```
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
let _value = &array[0]; // Borrowing is allowed, unlike moving.
}
```
Alternatively, if your type implements `Clone` and you need to own the value,
consider borrowing and then cloning:
```
#[derive(Clone)]
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
// Now you can clone the array element.
let _value = array[0].clone();
}
```
"##,
E0509: r##"
This error occurs when an attempt is made to move out of a value whose type
implements the `Drop` trait.
Example of erroneous code:
```compile_fail,E0509
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let fancy_field = drop_struct.fancy; // Error E0509
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
```
Here, we tried to move a field out of a struct of type `DropStruct` which
implements the `Drop` trait. However, a struct cannot be dropped if one or
more of its fields have been moved.
Structs implementing the `Drop` trait have an implicit destructor that gets
called when they go out of scope. This destructor may use the fields of the
struct, so moving out of the struct could make it impossible to run the
destructor. Therefore, we must think of all values whose type implements the
`Drop` trait as single units whose fields cannot be moved.
This error can be fixed by creating a reference to the fields of a struct,
enum, or tuple using the `ref` keyword:
```
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let ref fancy_field = drop_struct.fancy; // No more errors!
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
```
Note that this technique can also be used in the arms of a match expression:
```
struct FancyNum {
num: usize
}
enum DropEnum {
Fancy(FancyNum)
}
impl Drop for DropEnum {
fn drop(&mut self) {
// Destruct DropEnum, possibly using FancyNum
}
}
fn main() {
// Creates and enum of type `DropEnum`, which implements `Drop`
let drop_enum = DropEnum::Fancy(FancyNum{num: 10});
match drop_enum {
// Creates a reference to the inside of `DropEnum::Fancy`
DropEnum::Fancy(ref fancy_field) => // No error!
println!("It was fancy-- {}!", fancy_field.num),
}
// implicit call to `drop_enum.drop()` as drop_enum goes out of scope
}
```
"##,
E0595: r##"
Closures cannot mutate immutable captured variables.

View File

@ -30,6 +30,7 @@ use dataflow::{MoveDataParamEnv};
use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
use dataflow::{Borrows, BorrowData, BorrowIndex};
use dataflow::move_paths::{MoveError, IllegalMoveOriginKind};
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
use util::borrowck_errors::{BorrowckErrors, Origin};
@ -59,7 +60,33 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
let param_env = tcx.param_env(def_id);
tcx.infer_ctxt().enter(|_infcx| {
let move_data = MoveData::gather_moves(mir, tcx, param_env);
let move_data = match MoveData::gather_moves(mir, tcx, param_env) {
Ok(move_data) => move_data,
Err((move_data, move_errors)) => {
for move_error in move_errors {
let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
MoveError::UnionMove { .. } =>
unimplemented!("dont know how to report union move errors yet."),
MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
};
let origin = Origin::Mir;
let mut err = match kind {
IllegalMoveOriginKind::Static =>
tcx.cannot_move_out_of(span, "static item", origin),
IllegalMoveOriginKind::BorrowedContent =>
tcx.cannot_move_out_of(span, "borrowed_content", origin),
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
};
err.emit();
}
move_data
}
};
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
@ -1106,9 +1133,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
// Retrieve span of given borrow from the current MIR representation
fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
self.mir.basic_blocks()[borrow.location.block]
.statements[borrow.location.statement_index]
.source_info.span
self.mir.source_info(borrow.location).span
}
}

View File

@ -22,17 +22,15 @@ use std::mem;
use super::abs_domain::Lift;
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
use super::{MoveError};
use super::IllegalMoveOriginKind::*;
pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
struct MoveDataBuilder<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
data: MoveData<'tcx>,
}
pub enum MovePathError {
IllegalMove,
UnionMove { path: MovePathIndex },
errors: Vec<MoveError<'tcx>>,
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
@ -47,6 +45,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
mir,
tcx,
param_env,
errors: Vec::new(),
data: MoveData {
moves: IndexVec::new(),
loc_map: LocationMap::new(mir),
@ -85,7 +84,9 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
assert_eq!(path_map_ent, move_path);
move_path
}
}
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
/// if that lvalue can't be moved from.
///
@ -94,13 +95,15 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
///
/// Maybe we should have separate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
-> Result<MovePathIndex, MovePathError>
-> Result<MovePathIndex, MoveError<'tcx>>
{
debug!("lookup({:?})", lval);
match *lval {
Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
// error: can't move out of a static
Lvalue::Static(..) => Err(MovePathError::IllegalMove),
Lvalue::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]),
Lvalue::Static(..) => {
let span = self.builder.mir.source_info(self.loc).span;
Err(MoveError::cannot_move_out_of(span, Static))
}
Lvalue::Projection(ref proj) => {
self.move_path_for_projection(lval, proj)
}
@ -116,37 +119,52 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn move_path_for_projection(&mut self,
lval: &Lvalue<'tcx>,
proj: &LvalueProjection<'tcx>)
-> Result<MovePathIndex, MovePathError>
-> Result<MovePathIndex, MoveError<'tcx>>
{
let base = try!(self.move_path_for(&proj.base));
let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
let mir = self.builder.mir;
let tcx = self.builder.tcx;
let lv_ty = proj.base.ty(mir, tcx).to_ty(tcx);
match lv_ty.sty {
// error: can't move out of borrowed content
ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
// error: can't move out of struct with destructor
ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
return Err(MovePathError::IllegalMove),
ty::TyRef(..) | ty::TyRawPtr(..) =>
return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span,
BorrowedContent)),
ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() =>
return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span,
InteriorOfTypeWithDestructor {
container_ty: lv_ty
})),
// move out of union - always move the entire union
ty::TyAdt(adt, _) if adt.is_union() =>
return Err(MovePathError::UnionMove { path: base }),
// error: can't move out of a slice
ty::TySlice(..) =>
return Err(MovePathError::IllegalMove),
ty::TyArray(..) => match proj.elem {
// error: can't move out of an array
ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
return Err(MoveError::UnionMove { path: base }),
ty::TySlice(elem_ty) =>
return Err(MoveError::cannot_move_out_of(
mir.source_info(self.loc).span,
InteriorOfSlice {
elem_ty, is_index: match proj.elem {
ProjectionElem::Index(..) => true,
_ => false
},
})),
ty::TyArray(elem_ty, _num_elems) => match proj.elem {
ProjectionElem::Index(..) =>
return Err(MoveError::cannot_move_out_of(
mir.source_info(self.loc).span,
InteriorOfArray {
elem_ty, is_index: true
})),
_ => {
// FIXME: still badly broken
}
},
_ => {}
};
match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
match self.builder.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
Entry::Occupied(ent) => Ok(*ent.get()),
Entry::Vacant(ent) => {
let path = Self::new_move_path(
&mut self.data.move_paths,
&mut self.data.path_map,
let path = MoveDataBuilder::new_move_path(
&mut self.builder.data.move_paths,
&mut self.builder.data.path_map,
Some(base),
lval.clone()
);
@ -155,8 +173,10 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
}
}
fn finalize(self) -> MoveData<'tcx> {
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn finalize(self) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
debug!("{}", {
debug!("moves for {:?}:", self.mir.span);
for (j, mo) in self.data.moves.iter_enumerated() {
@ -168,14 +188,20 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
"done dumping moves"
});
self.data
if self.errors.len() > 0 {
Err((self.data, self.errors))
} else {
Ok(self.data)
}
}
}
pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> MoveData<'tcx> {
-> Result<MoveData<'tcx>,
(MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
for (bb, block) in mir.basic_blocks().iter_enumerated() {
@ -197,6 +223,22 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
debug!("gather_statement({:?}, {:?})", loc, stmt);
(Gatherer { builder: self, loc }).gather_statement(stmt);
}
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
debug!("gather_terminator({:?}, {:?})", loc, term);
(Gatherer { builder: self, loc }).gather_terminator(term);
}
}
struct Gatherer<'b, 'a: 'b, 'tcx: 'a> {
builder: &'b mut MoveDataBuilder<'a, 'tcx>,
loc: Location,
}
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
match stmt.kind {
StatementKind::Assign(ref lval, ref rval) => {
self.create_move_path(lval);
@ -206,7 +248,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
// the exterior.
self.create_move_path(&lval.clone().deref());
}
self.gather_rvalue(loc, rval);
self.gather_rvalue(rval);
}
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) => {}
@ -221,22 +263,22 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
match *rvalue {
Rvalue::Use(ref operand) |
Rvalue::Repeat(ref operand, _) |
Rvalue::Cast(_, ref operand, _) |
Rvalue::UnaryOp(_, ref operand) => {
self.gather_operand(loc, operand)
self.gather_operand(operand)
}
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
self.gather_operand(loc, lhs);
self.gather_operand(loc, rhs);
self.gather_operand(lhs);
self.gather_operand(rhs);
}
Rvalue::Aggregate(ref _kind, ref operands) => {
for operand in operands {
self.gather_operand(loc, operand);
self.gather_operand(operand);
}
}
Rvalue::Ref(..) |
@ -258,8 +300,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
debug!("gather_terminator({:?}, {:?})", loc, term);
fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
@ -267,7 +308,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
self.gather_move(&Lvalue::Local(RETURN_POINTER));
}
TerminatorKind::Assert { .. } |
@ -276,20 +317,20 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
TerminatorKind::Yield { ref value, .. } => {
self.gather_operand(loc, value);
self.gather_operand(value);
}
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
self.gather_move(loc, location);
self.gather_move(location);
}
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
self.create_move_path(location);
self.gather_operand(loc, value);
self.gather_operand(value);
}
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
self.gather_operand(loc, func);
self.gather_operand(func);
for arg in args {
self.gather_operand(loc, arg);
self.gather_operand(arg);
}
if let Some((ref destination, _bb)) = *destination {
self.create_move_path(destination);
@ -298,40 +339,38 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
fn gather_operand(&mut self, operand: &Operand<'tcx>) {
match *operand {
Operand::Constant(..) => {} // not-a-move
Operand::Consume(ref lval) => { // a move
self.gather_move(loc, lval);
self.gather_move(lval);
}
}
}
fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
debug!("gather_move({:?}, {:?})", loc, lval);
fn gather_move(&mut self, lval: &Lvalue<'tcx>) {
debug!("gather_move({:?}, {:?})", self.loc, lval);
let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
let tcx = self.builder.tcx;
let lv_ty = lval.ty(self.builder.mir, tcx).to_ty(tcx);
if !lv_ty.moves_by_default(tcx, self.builder.param_env, DUMMY_SP) {
debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", self.loc, lval, lv_ty);
return
}
let path = match self.move_path_for(lval) {
Ok(path) | Err(MovePathError::UnionMove { path }) => path,
Err(MovePathError::IllegalMove) => {
// Moving out of a bad path. Eventually, this should be a MIR
// borrowck error instead of a bug.
span_bug!(self.mir.span,
"Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
lval, lv_ty, loc);
Ok(path) | Err(MoveError::UnionMove { path }) => path,
Err(error @ MoveError::IllegalMove { .. }) => {
self.builder.errors.push(error);
return;
}
};
let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc });
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
loc, lval, move_out, path);
self.loc, lval, move_out, path);
self.data.path_map[path].push(move_out);
self.data.loc_map[loc].push(move_out);
self.builder.data.path_map[path].push(move_out);
self.builder.data.loc_map[self.loc].push(move_out);
}
}

View File

@ -13,6 +13,7 @@ use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{IndexVec};
use syntax_pos::{Span};
use std::fmt;
use std::ops::{Index, IndexMut};
@ -227,11 +228,39 @@ impl<'tcx> MovePathLookup<'tcx> {
}
}
#[derive(Debug)]
pub struct IllegalMoveOrigin<'tcx> {
pub(crate) span: Span,
pub(crate) kind: IllegalMoveOriginKind<'tcx>,
}
#[derive(Debug)]
pub(crate) enum IllegalMoveOriginKind<'tcx> {
Static,
BorrowedContent,
InteriorOfTypeWithDestructor { container_ty: ty::Ty<'tcx> },
InteriorOfSlice { elem_ty: ty::Ty<'tcx>, is_index: bool, },
InteriorOfArray { elem_ty: ty::Ty<'tcx>, is_index: bool, },
}
#[derive(Debug)]
pub enum MoveError<'tcx> {
IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
UnionMove { path: MovePathIndex },
}
impl<'tcx> MoveError<'tcx> {
fn cannot_move_out_of(span: Span, kind: IllegalMoveOriginKind<'tcx>) -> Self {
let origin = IllegalMoveOrigin { span, kind };
MoveError::IllegalMove { cannot_move_out_of: origin }
}
}
impl<'a, 'tcx> MoveData<'tcx> {
pub fn gather_moves(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> Self {
-> Result<Self, (Self, Vec<MoveError<'tcx>>)> {
builder::gather_moves(mir, tcx, param_env)
}
}

View File

@ -999,6 +999,272 @@ fn print_fancy_ref(fancy_ref: &FancyNum){
```
"##,
E0507: r##"
You tried to move out of a value which was borrowed. Erroneous code example:
```compile_fail,E0507
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // error: cannot move out of borrowed content
}
```
Here, the `nothing_is_true` method takes the ownership of `self`. However,
`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`,
which is a borrow of the content owned by the `RefCell`. To fix this error,
you have three choices:
* Try to avoid moving the variable.
* Somehow reclaim the ownership.
* Implement the `Copy` trait on the type.
Examples:
```
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(&self) {} // First case, we don't take ownership
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // ok!
}
```
Or:
```
use std::cell::RefCell;
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
let x = x.into_inner(); // we get back ownership
x.nothing_is_true(); // ok!
}
```
Or:
```
use std::cell::RefCell;
#[derive(Clone, Copy)] // we implement the Copy trait
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
fn main() {
let x = RefCell::new(TheDarkKnight);
x.borrow().nothing_is_true(); // ok!
}
```
Moving a member out of a mutably borrowed struct will also cause E0507 error:
```compile_fail,E0507
struct TheDarkKnight;
impl TheDarkKnight {
fn nothing_is_true(self) {}
}
struct Batcave {
knight: TheDarkKnight
}
fn main() {
let mut cave = Batcave {
knight: TheDarkKnight
};
let borrowed = &mut cave;
borrowed.knight.nothing_is_true(); // E0507
}
```
It is fine only if you put something back. `mem::replace` can be used for that:
```
# struct TheDarkKnight;
# impl TheDarkKnight { fn nothing_is_true(self) {} }
# struct Batcave { knight: TheDarkKnight }
use std::mem;
let mut cave = Batcave {
knight: TheDarkKnight
};
let borrowed = &mut cave;
mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok!
```
You can find more information about borrowing in the rust-book:
http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html
"##,
E0508: r##"
A value was moved out of a non-copy fixed-size array.
Example of erroneous code:
```compile_fail,E0508
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`,
// a non-copy fixed-size array
}
```
The first element was moved out of the array, but this is not
possible because `NonCopy` does not implement the `Copy` trait.
Consider borrowing the element instead of moving it:
```
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
let _value = &array[0]; // Borrowing is allowed, unlike moving.
}
```
Alternatively, if your type implements `Clone` and you need to own the value,
consider borrowing and then cloning:
```
#[derive(Clone)]
struct NonCopy;
fn main() {
let array = [NonCopy; 1];
// Now you can clone the array element.
let _value = array[0].clone();
}
```
"##,
E0509: r##"
This error occurs when an attempt is made to move out of a value whose type
implements the `Drop` trait.
Example of erroneous code:
```compile_fail,E0509
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let fancy_field = drop_struct.fancy; // Error E0509
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
```
Here, we tried to move a field out of a struct of type `DropStruct` which
implements the `Drop` trait. However, a struct cannot be dropped if one or
more of its fields have been moved.
Structs implementing the `Drop` trait have an implicit destructor that gets
called when they go out of scope. This destructor may use the fields of the
struct, so moving out of the struct could make it impossible to run the
destructor. Therefore, we must think of all values whose type implements the
`Drop` trait as single units whose fields cannot be moved.
This error can be fixed by creating a reference to the fields of a struct,
enum, or tuple using the `ref` keyword:
```
struct FancyNum {
num: usize
}
struct DropStruct {
fancy: FancyNum
}
impl Drop for DropStruct {
fn drop(&mut self) {
// Destruct DropStruct, possibly using FancyNum
}
}
fn main() {
let drop_struct = DropStruct{fancy: FancyNum{num: 5}};
let ref fancy_field = drop_struct.fancy; // No more errors!
println!("Fancy: {}", fancy_field.num);
// implicit call to `drop_struct.drop()` as drop_struct goes out of scope
}
```
Note that this technique can also be used in the arms of a match expression:
```
struct FancyNum {
num: usize
}
enum DropEnum {
Fancy(FancyNum)
}
impl Drop for DropEnum {
fn drop(&mut self) {
// Destruct DropEnum, possibly using FancyNum
}
}
fn main() {
// Creates and enum of type `DropEnum`, which implements `Drop`
let drop_enum = DropEnum::Fancy(FancyNum{num: 10});
match drop_enum {
// Creates a reference to the inside of `DropEnum::Fancy`
DropEnum::Fancy(ref fancy_field) => // No error!
println!("It was fancy-- {}!", fancy_field.num),
}
// implicit call to `drop_enum.drop()` as drop_enum goes out of scope
}
```
"##,
}
register_diagnostics! {

View File

@ -45,7 +45,7 @@ impl MirPass for ElaborateDrops {
}
let id = src.item_id();
let param_env = tcx.param_env(tcx.hir.local_def_id(id));
let move_data = MoveData::gather_moves(mir, tcx, param_env);
let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap();
let elaborate_patch = {
let mir = &*mir;
let env = MoveDataParamEnv {

View File

@ -45,7 +45,7 @@ impl MirPass for SanityCheck {
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
let move_data = MoveData::gather_moves(mir, tcx, param_env);
let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap();
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_inits =

View File

@ -248,6 +248,52 @@ pub trait BorrowckErrors {
{
self.cannot_assign(span, &format!("immutable static item `{}`", desc), o)
}
fn cannot_move_out_of(&self, move_from_span: Span, move_from_desc: &str, o: Origin)
-> DiagnosticBuilder
{
let mut err = struct_span_err!(self, move_from_span, E0507,
"cannot move out of {}{OGN}",
move_from_desc, OGN=o);
err.span_label(
move_from_span,
format!("cannot move out of {}", move_from_desc));
err
}
fn cannot_move_out_of_interior_noncopy(&self,
move_from_span: Span,
ty: ty::Ty,
is_index: bool,
o: Origin)
-> DiagnosticBuilder
{
let type_name = match (&ty.sty, is_index) {
(&ty::TyArray(_, _), true) => "array",
(&ty::TySlice(_), _) => "slice",
_ => span_bug!(move_from_span, "this path should not cause illegal move"),
};
let mut err = struct_span_err!(self, move_from_span, E0508,
"cannot move out of type `{}`, \
a non-copy {}{OGN}",
ty, type_name, OGN=o);
err.span_label(move_from_span, "cannot move out of here");
err
}
fn cannot_move_out_of_interior_of_drop(&self,
move_from_span: Span,
container_ty: ty::Ty,
o: Origin)
-> DiagnosticBuilder
{
let mut err = struct_span_err!(self, move_from_span, E0509,
"cannot move out of type `{}`, \
which implements the `Drop` trait{OGN}",
container_ty, OGN=o);
err.span_label(move_from_span, "cannot move out of here");
err
}
}
impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {

View File

@ -8,12 +8,17 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
// Check that we check fns appearing in constant declarations.
// Issue #22382.
const MOVE: fn(&String) -> String = {
fn broken(x: &String) -> String {
return *x //~ ERROR cannot move
return *x //[ast]~ ERROR cannot move out of borrowed content [E0507]
//[mir]~^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
}
broken
};

View File

@ -8,19 +8,28 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
fn with<F>(f: F) where F: FnOnce(&String) {}
fn arg_item(&_x: &String) {}
//~^ ERROR cannot move out of borrowed content
//[ast]~^ ERROR cannot move out of borrowed content [E0507]
//[mir]~^^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
fn arg_closure() {
with(|&_x| ())
//~^ ERROR cannot move out of borrowed content
//[ast]~^ ERROR cannot move out of borrowed content [E0507]
//[mir]~^^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
}
fn let_pat() {
let &_x = &"hi".to_string();
//~^ ERROR cannot move out of borrowed content
//[ast]~^ ERROR cannot move out of borrowed content [E0507]
//[mir]~^^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
}
pub fn main() {}

View File

@ -8,9 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
use std::rc::Rc;
pub fn main() {
let _x = Rc::new(vec![1, 2]).into_iter();
//~^ ERROR cannot move out of borrowed content
//[ast]~^ ERROR cannot move out of borrowed content [E0507]
//[mir]~^^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
}

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
// Ensure that moves out of static items is forbidden
struct Foo {
@ -22,5 +25,7 @@ fn test(f: Foo) {
}
fn main() {
test(BAR); //~ ERROR cannot move out of static item
test(BAR); //[ast]~ ERROR cannot move out of static item [E0507]
//[mir]~^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
}

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
struct S {f:String}
impl Drop for S {
fn drop(&mut self) { println!("{}", self.f); }
@ -16,17 +19,23 @@ impl Drop for S {
fn move_in_match() {
match (S {f:"foo".to_string()}) {
S {f:_s} => {}
//~^ ERROR cannot move out of type `S`, which implements the `Drop` trait
//[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509]
//[mir]~^^ ERROR (Ast) [E0509]
//[mir]~| ERROR (Mir) [E0509]
}
}
fn move_in_let() {
let S {f:_s} = S {f:"foo".to_string()};
//~^ ERROR cannot move out of type `S`, which implements the `Drop` trait
//[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509]
//[mir]~^^ ERROR (Ast) [E0509]
//[mir]~| ERROR (Mir) [E0509]
}
fn move_in_fn_arg(S {f:_s}: S) {
//~^ ERROR cannot move out of type `S`, which implements the `Drop` trait
//[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509]
//[mir]~^^ ERROR (Ast) [E0509]
//[mir]~| ERROR (Mir) [E0509]
}
fn main() {}

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
// Issue 4691: Ensure that functional-struct-update can only copy, not
// move, when the struct implements Drop.
@ -20,12 +23,16 @@ impl Drop for T { fn drop(&mut self) { } }
fn f(s0:S) {
let _s2 = S{a: 2, ..s0};
//~^ error: cannot move out of type `S`, which implements the `Drop` trait
//[ast]~^ error: cannot move out of type `S`, which implements the `Drop` trait
//[mir]~^^ ERROR (Ast) [E0509]
//[mir]~| ERROR (Mir) [E0509]
}
fn g(s0:T) {
let _s2 = T{a: 2, ..s0};
//~^ error: cannot move out of type `T`, which implements the `Drop` trait
//[ast]~^ error: cannot move out of type `T`, which implements the `Drop` trait
//[mir]~^^ ERROR (Ast) [E0509]
//[mir]~| ERROR (Mir) [E0509]
}
fn main() { }

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
// Regression test for #38520. Check that moves of `Foo` are not
// permitted as `Foo` is not copy (even in a static/const
// initializer).
@ -21,8 +24,12 @@ const fn get(x: Foo) -> usize {
}
const X: Foo = Foo(22);
static Y: usize = get(*&X); //~ ERROR E0507
const Z: usize = get(*&X); //~ ERROR E0507
static Y: usize = get(*&X); //[ast]~ ERROR E0507
//[mir]~^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
const Z: usize = get(*&X); //[ast]~ ERROR E0507
//[mir]~^ ERROR (Ast) [E0507]
//[mir]~| ERROR (Mir) [E0507]
fn main() {
}