Skip to content

MIR-borrowck: gather and signal any move errors #45016

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

Merged
13 changes: 13 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -284,6 +284,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)]
39 changes: 9 additions & 30 deletions src/librustc_borrowck/borrowck/gather_loans/move_error.rs
Original file line number Diff line number Diff line change
@@ -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,51 +135,29 @@ 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 {
Categorization::Deref(_, mc::BorrowedPtr(..)) |
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");
}
266 changes: 0 additions & 266 deletions src/librustc_borrowck/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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.

Loading