Skip to content
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

Issue #4715: Give a better error message when move results from a binding.. #6674

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,

if !any_by_move { return; } // pointless micro-optimization
for pats.each |pat| {
do walk_pat(*pat) |p| {
for walk_pat(*pat) |p| {
if pat_is_binding(def_map, p) {
match p.node {
pat_ident(_, _, sub) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/dataflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
debug!("DataFlowContext::walk_pat(pat=%s, in_out=%s)",
pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out)));

do ast_util::walk_pat(pat) |p| {
for ast_util::walk_pat(pat) |p| {
debug!(" p.id=%? in_out=%s", p.id, bits_to_str(reslice(in_out)));
self.merge_with_entry_set(p.id, in_out);
self.dfcx.apply_gen_kill(p.id, in_out);
Expand Down
137 changes: 65 additions & 72 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1443,10 +1443,10 @@ fn check_expr(expr: @expr, this: @Liveness, vt: vt<@Liveness>) {

match this.ir.variable_moves_map.find(&expr.id) {
None => {}
Some(&entire_expr) => {
Some(&reason) => {
debug!("(checking expr) is a move: `%s`",
expr_to_str(expr, this.tcx.sess.intr()));
this.check_move_from_var(ln, *var, entire_expr);
this.check_move_from_var(ln, *var, reason);
}
}
}
Expand All @@ -1459,7 +1459,7 @@ fn check_expr(expr: @expr, this: @Liveness, vt: vt<@Liveness>) {
for caps.each |cap| {
let var = this.variable(cap.var_nid, expr.span);
if cap.is_move {
this.check_move_from_var(cap.ln, var, expr);
this.check_move_from_var(cap.ln, var, moves::MovedByExpr(expr));
}
}

Expand Down Expand Up @@ -1549,24 +1549,20 @@ pub impl Liveness {
fn check_move_from_var(&self,
ln: LiveNode,
var: Variable,
move_expr: @expr) {
move_reason: moves::Reason) {
/*!
* Checks whether `var` is live on entry to any of the
* successors of `ln`. If it is, report an error.
* `move_expr` is the expression which caused the variable
* `move_reason` is the reason which caused the variable
* to be moved.
*
* Note that `move_expr` is not necessarily a reference to the
* variable. It might be an expression like `x.f` which could
* cause a move of the variable `x`, or a closure creation.
*/

debug!("check_move_from_var(%s, %s)",
ln.to_str(), var.to_str());

match self.live_on_exit(ln, var) {
None => {}
Some(lnk) => self.report_illegal_move(lnk, var, move_expr)
Some(lnk) => self.report_illegal_move(lnk, var, move_reason)
}
}

Expand Down Expand Up @@ -1640,82 +1636,79 @@ pub impl Liveness {

fn report_illegal_move(&self, lnk: LiveNodeKind,
var: Variable,
move_expr: @expr) {
// the only time that it is possible to have a moved variable
// used by ExitNode would be arguments or fields in a ctor.
// we give a slightly different error message in those cases.
if lnk == ExitNode {
// FIXME #4715: this seems like it should be reported in the
// borrow checker
let vk = self.ir.var_kinds[*var];
match vk {
Arg(_, name) => {
self.tcx.sess.span_err(
move_expr.span,
fmt!("illegal move from argument `%s`, which is not \
copy or move mode", *self.tcx.sess.str_of(name)));
return;
}
Local(*) | ImplicitRet => {
self.tcx.sess.span_bug(
move_expr.span,
fmt!("illegal reader (%?) for `%?`",
lnk, vk));
}
move_reason: moves::Reason) {
match move_reason {
moves::MovedByExpr(move_expr) => {
match move_expr.node {
expr_fn_block(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
let name = self.ir.variable_name(var);
self.tcx.sess.span_note(
move_expr.span,
fmt!("`%s` moved into closure environment here \
because its type is moved by default",
*name));
}
expr_path(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
self.report_move_location(
move_expr.id, move_expr.span, var,
"", "it", "copy");
}
expr_field(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr.id, move_expr.span, var,
"field of ", "the field", "copy");
}
expr_index(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr.id, move_expr.span, var,
"element of ", "the element", "copy");
}
_ => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr.id, move_expr.span, var,
"subcomponent of ", "the subcomponent", "copy");
}
}
}
}

match move_expr.node {
expr_fn_block(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
let name = self.ir.variable_name(var);
self.tcx.sess.span_note(
move_expr.span,
fmt!("`%s` moved into closure environment here \
because its type is moved by default",
*name));
}
expr_path(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
self.report_move_location(
move_expr, var, "", "it");
}
expr_field(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "field of ", "the field");
}
expr_index(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "element of ", "the element");
}
_ => {
moves::MovedByPat(move_binding) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
move_binding.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "subcomponent of ", "the subcomponent");
move_binding.id, move_binding.span, var,
"", "the binding", "ref");
}
};
}

fn report_move_location(&self,
move_expr: @expr,
move_loc_id: node_id,
move_loc_span: span,
var: Variable,
expr_descr: &str,
pronoun: &str) {
let move_expr_ty = ty::expr_ty(self.tcx, move_expr);
pronoun: &str,
override: &str) {
let move_expr_ty = ty::node_id_to_type(self.tcx, move_loc_id);
let name = self.ir.variable_name(var);
self.tcx.sess.span_note(
move_expr.span,
move_loc_span,
fmt!("%s`%s` moved here because %s has type %s, \
which is moved by default (use `copy` to override)",
expr_descr, *name, pronoun,
ty_to_str(self.tcx, move_expr_ty)));
which is moved by default (use `%s` to override)",
expr_descr,
*name,
pronoun,
ty_to_str(self.tcx, move_expr_ty),
override));
}

fn report_illegal_read(&self,
Expand Down
Loading