Skip to content

Commit

Permalink
Change the use of moved value error to be more accurate
Browse files Browse the repository at this point in the history
Previously it output `partially moved` to eagerly. This updates it to be more
accurate and output `collaterally moved` for use of values that were invalidated
by moves out of different fields in the same struct.

Closes rust-lang#15630.
  • Loading branch information
ftxqxd committed Oct 2, 2014
1 parent 35ff2de commit 02c6ebd
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 22 deletions.
59 changes: 48 additions & 11 deletions src/librustc/middle/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,24 +461,58 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
MovedInCapture => "capture",
};

match the_move.kind {
let (ol, moved_lp_msg) = match the_move.kind {
move_data::Declared => {
self.tcx.sess.span_err(
use_span,
format!("{} of possibly uninitialized variable: `{}`",
verb,
self.loan_path_to_string(lp)).as_slice());
(self.loan_path_to_string(moved_lp),
String::new())
}
_ => {
let partially = if lp == moved_lp {""} else {"partially "};
// If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
// normally generate a rather confusing message:
//
// error: use of moved value: `x.b`
// note: `x.a` moved here...
//
// What we want to do instead is get the 'common ancestor' of the two moves and
// use that for most of the message instead, giving is something like this:
//
// error: use of moved value: `x`
// note: `x` moved here (through moving `x.a`)...

let common = moved_lp.common(lp);
let has_common = common.is_some();
let has_fork = moved_lp.has_fork(lp);
let (nl, ol, moved_lp_msg) =
if has_fork && has_common {
let nl = self.loan_path_to_string(&common.unwrap());
let ol = nl.clone();
let moved_lp_msg = format!(" (through moving `{}`)",
self.loan_path_to_string(moved_lp));
(nl, ol, moved_lp_msg)
} else {
(self.loan_path_to_string(lp),
self.loan_path_to_string(moved_lp),
String::new())
};

let partial = moved_lp.depth() > lp.depth();
let msg = if !has_fork && partial { "partially " }
else if has_fork && !has_common { "collaterally "}
else { "" };
self.tcx.sess.span_err(
use_span,
format!("{} of {}moved value: `{}`",
verb,
partially,
self.loan_path_to_string(lp)).as_slice());
msg,
nl).as_slice());
(ol, moved_lp_msg)
}
}
};

match the_move.kind {
move_data::Declared => {}
Expand All @@ -501,19 +535,21 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
"moved by default (use `copy` to override)");
self.tcx.sess.span_note(
expr_span,
format!("`{}` moved here because it has type `{}`, which is {}",
self.loan_path_to_string(moved_lp),
format!("`{}` moved here{} because it has type `{}`, which is {}",
ol,
moved_lp_msg,
expr_ty.user_string(self.tcx),
suggestion).as_slice());
}

move_data::MovePat => {
let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
self.tcx.sess.span_note(self.tcx.map.span(the_move.id),
format!("`{}` moved here because it has type `{}`, \
format!("`{}` moved here{} because it has type `{}`, \
which is moved by default (use `ref` to \
override)",
self.loan_path_to_string(moved_lp),
ol,
moved_lp_msg,
pat_ty.user_string(self.tcx)).as_slice());
}

Expand All @@ -536,9 +572,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
capture that instead to override)");
self.tcx.sess.span_note(
expr_span,
format!("`{}` moved into closure environment here because it \
format!("`{}` moved into closure environment here{} because it \
has type `{}`, which is {}",
self.loan_path_to_string(moved_lp),
ol,
moved_lp_msg,
expr_ty.user_string(self.tcx),
suggestion).as_slice());
}
Expand Down
15 changes: 9 additions & 6 deletions src/test/compile-fail/borrowck-box-insensitivity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,22 @@ struct D {
fn copy_after_move() {
let a = box A { x: box 0, y: 1 };
let _x = a.x;
let _y = a.y; //~ ERROR use of partially moved
let _y = a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
}

fn move_after_move() {
let a = box B { x: box 0, y: box 1 };
let _x = a.x;
let _y = a.y; //~ ERROR use of partially moved
let _y = a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
}

fn borrow_after_move() {
let a = box A { x: box 0, y: 1 };
let _x = a.x;
let _y = &a.y; //~ ERROR use of partially moved
let _y = &a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
}

fn move_after_borrow() {
Expand Down Expand Up @@ -79,19 +82,19 @@ fn mut_borrow_after_borrow() {
fn copy_after_move_nested() {
let a = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = a.x.x;
let _y = a.y; //~ ERROR use of partially moved
let _y = a.y; //~ ERROR use of collaterally moved
}

fn move_after_move_nested() {
let a = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
let _x = a.x.x;
let _y = a.y; //~ ERROR use of partially moved
let _y = a.y; //~ ERROR use of collaterally moved
}

fn borrow_after_move_nested() {
let a = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = a.x.x;
let _y = &a.y; //~ ERROR use of partially moved
let _y = &a.y; //~ ERROR use of collaterally moved
}

fn move_after_borrow_nested() {
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/borrowck-field-sensitivity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ struct A { a: int, b: Box<int> }
fn deref_after_move() {
let x = A { a: 1, b: box 2 };
drop(x.b);
drop(*x.b); //~ ERROR use of partially moved value: `*x.b`
drop(*x.b); //~ ERROR use of moved value: `*x.b`
}

fn deref_after_fu_move() {
let x = A { a: 1, b: box 2 };
let y = A { a: 3, .. x };
drop(*x.b); //~ ERROR use of partially moved value: `*x.b`
drop(*x.b); //~ ERROR use of moved value: `*x.b`
}

fn borrow_after_move() {
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/liveness-use-after-move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ extern crate debug;
fn main() {
let x = box 5i;
let y = x;
println!("{:?}", *x); //~ ERROR use of partially moved value: `*x`
println!("{:?}", *x); //~ ERROR use of moved value: `*x`
y.clone();
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/use-after-move-self-based-on-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl Drop for S {
impl S {
pub fn foo(self) -> int {
self.bar();
return self.x; //~ ERROR use of partially moved value: `self.x`
return self.x; //~ ERROR use of moved value: `self.x`
}

pub fn bar(self) {}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/use-after-move-self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct S {
impl S {
pub fn foo(self) -> int {
self.bar();
return *self.x; //~ ERROR use of partially moved value: `*self.x`
return *self.x; //~ ERROR use of moved value: `*self.x`
}

pub fn bar(self) {}
Expand Down

1 comment on commit 02c6ebd

@nikomatsakis
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+

Please sign in to comment.