Skip to content

Commit b8d6214

Browse files
committed
auto merge of #13418 : ktt3ja/rust/move-out-of, r=brson
This commit changes the way move errors are reported when some value is captured by a PatIdent. First, we collect all of the "cannot move out of" errors before reporting them, and those errors with the same "move source" are reported together. If the move is caused by a PatIdent (that binds by value), we add a note indicating where it is and suggest the user to put `ref` if they don't want the value to move. This makes the "cannot move out of" error in match expression nicer (though the extra note may not feel that helpful in other places :P). For example, with the following code snippet, ```rust enum Foo { Foo1(~u32, ~u32), Foo2(~u32), Foo3, } fn main() { let f = &Foo1(~1u32, ~2u32); match *f { Foo1(num1, num2) => (), Foo2(num) => (), Foo3 => () } } ``` Errors before the change: ```rust test.rs:10:9: 10:25 error: cannot move out of dereference of `&`-pointer test.rs:10 Foo1(num1, num2) => (), ^~~~~~~~~~~~~~~~ test.rs:10:9: 10:25 error: cannot move out of dereference of `&`-pointer test.rs:10 Foo1(num1, num2) => (), ^~~~~~~~~~~~~~~~ test.rs:11:9: 11:18 error: cannot move out of dereference of `&`-pointer test.rs:11 Foo2(num) => (), ^~~~~~~~~ ``` After: ```rust test.rs:9:11: 9:13 error: cannot move out of dereference of `&`-pointer test.rs:9 match *f { ^~ test.rs:10:14: 10:18 note: attempting to move value to here (to prevent the move, use `ref num1` or `ref mut num1` to capture value by reference) test.rs:10 Foo1(num1, num2) => (), ^~~~ test.rs:10:20: 10:24 note: and here (use `ref num2` or `ref mut num2`) test.rs:10 Foo1(num1, num2) => (), ^~~~ test.rs:11:14: 11:17 note: and here (use `ref num` or `ref mut num`) test.rs:11 Foo2(num) => (), ^~~ ``` Close #8064
2 parents bfaf171 + 13d6c35 commit b8d6214

File tree

7 files changed

+323
-49
lines changed

7 files changed

+323
-49
lines changed

src/librustc/middle/borrowck/gather_loans/gather_moves.rs

+67-32
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,21 @@
1414

1515
use mc = middle::mem_categorization;
1616
use middle::borrowck::*;
17+
use middle::borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
18+
use middle::borrowck::gather_loans::move_error::MoveSpanAndPath;
1719
use middle::borrowck::move_data::*;
1820
use middle::moves;
1921
use middle::ty;
2022
use syntax::ast;
2123
use syntax::codemap::Span;
22-
use util::ppaux::{Repr, UserString};
24+
use util::ppaux::Repr;
25+
26+
struct GatherMoveInfo {
27+
id: ast::NodeId,
28+
kind: MoveKind,
29+
cmt: mc::cmt,
30+
span_path_opt: Option<MoveSpanAndPath>
31+
}
2332

2433
pub fn gather_decl(bccx: &BorrowckCtxt,
2534
move_data: &MoveData,
@@ -32,28 +41,56 @@ pub fn gather_decl(bccx: &BorrowckCtxt,
3241

3342
pub fn gather_move_from_expr(bccx: &BorrowckCtxt,
3443
move_data: &MoveData,
44+
move_error_collector: &MoveErrorCollector,
3545
move_expr: &ast::Expr,
3646
cmt: mc::cmt) {
37-
gather_move(bccx, move_data, move_expr.id, MoveExpr, cmt);
47+
let move_info = GatherMoveInfo {
48+
id: move_expr.id,
49+
kind: MoveExpr,
50+
cmt: cmt,
51+
span_path_opt: None,
52+
};
53+
gather_move(bccx, move_data, move_error_collector, move_info);
3854
}
3955

4056
pub fn gather_move_from_pat(bccx: &BorrowckCtxt,
4157
move_data: &MoveData,
58+
move_error_collector: &MoveErrorCollector,
4259
move_pat: &ast::Pat,
4360
cmt: mc::cmt) {
44-
gather_move(bccx, move_data, move_pat.id, MovePat, cmt);
61+
let pat_span_path_opt = match move_pat.node {
62+
ast::PatIdent(_, ref path, _) => {
63+
Some(MoveSpanAndPath::with_span_and_path(move_pat.span,
64+
(*path).clone()))
65+
},
66+
_ => None,
67+
};
68+
let move_info = GatherMoveInfo {
69+
id: move_pat.id,
70+
kind: MovePat,
71+
cmt: cmt,
72+
span_path_opt: pat_span_path_opt,
73+
};
74+
gather_move(bccx, move_data, move_error_collector, move_info);
4575
}
4676

4777
pub fn gather_captures(bccx: &BorrowckCtxt,
4878
move_data: &MoveData,
79+
move_error_collector: &MoveErrorCollector,
4980
closure_expr: &ast::Expr) {
5081
for captured_var in bccx.capture_map.get(&closure_expr.id).iter() {
5182
match captured_var.mode {
5283
moves::CapMove => {
5384
let cmt = bccx.cat_captured_var(closure_expr.id,
5485
closure_expr.span,
5586
captured_var);
56-
gather_move(bccx, move_data, closure_expr.id, Captured, cmt);
87+
let move_info = GatherMoveInfo {
88+
id: closure_expr.id,
89+
kind: Captured,
90+
cmt: cmt,
91+
span_path_opt: None
92+
};
93+
gather_move(bccx, move_data, move_error_collector, move_info);
5794
}
5895
moves::CapCopy | moves::CapRef => {}
5996
}
@@ -62,19 +99,27 @@ pub fn gather_captures(bccx: &BorrowckCtxt,
6299

63100
fn gather_move(bccx: &BorrowckCtxt,
64101
move_data: &MoveData,
65-
move_id: ast::NodeId,
66-
move_kind: MoveKind,
67-
cmt: mc::cmt) {
102+
move_error_collector: &MoveErrorCollector,
103+
move_info: GatherMoveInfo) {
68104
debug!("gather_move(move_id={}, cmt={})",
69-
move_id, cmt.repr(bccx.tcx));
70-
71-
if !check_is_legal_to_move_from(bccx, cmt, cmt) {
72-
return;
105+
move_info.id, move_info.cmt.repr(bccx.tcx));
106+
107+
let potentially_illegal_move =
108+
check_and_get_illegal_move_origin(bccx, move_info.cmt);
109+
match potentially_illegal_move {
110+
Some(illegal_move_origin) => {
111+
let error = MoveError::with_move_info(illegal_move_origin,
112+
move_info.span_path_opt);
113+
move_error_collector.add_error(error);
114+
return
115+
}
116+
None => ()
73117
}
74118

75-
match opt_loan_path(cmt) {
119+
match opt_loan_path(move_info.cmt) {
76120
Some(loan_path) => {
77-
move_data.add_move(bccx.tcx, loan_path, move_id, move_kind);
121+
move_data.add_move(bccx.tcx, loan_path,
122+
move_info.id, move_info.kind);
78123
}
79124
None => {
80125
// move from rvalue or unsafe pointer, hence ok
@@ -110,59 +155,49 @@ pub fn gather_move_and_assignment(bccx: &BorrowckCtxt,
110155
true);
111156
}
112157

113-
fn check_is_legal_to_move_from(bccx: &BorrowckCtxt,
114-
cmt0: mc::cmt,
115-
cmt: mc::cmt) -> bool {
158+
fn check_and_get_illegal_move_origin(bccx: &BorrowckCtxt,
159+
cmt: mc::cmt) -> Option<mc::cmt> {
116160
match cmt.cat {
117161
mc::cat_deref(_, _, mc::BorrowedPtr(..)) |
118162
mc::cat_deref(_, _, mc::GcPtr) |
119163
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
120164
mc::cat_upvar(..) | mc::cat_static_item |
121165
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => {
122-
bccx.span_err(
123-
cmt0.span,
124-
format!("cannot move out of {}",
125-
bccx.cmt_to_str(cmt)));
126-
false
166+
Some(cmt)
127167
}
128168

129169
// Can move out of captured upvars only if the destination closure
130170
// type is 'once'. 1-shot stack closures emit the copied_upvar form
131171
// (see mem_categorization.rs).
132172
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Once, .. }) => {
133-
true
173+
None
134174
}
135175

136176
mc::cat_rvalue(..) |
137177
mc::cat_local(..) |
138178
mc::cat_arg(..) => {
139-
true
179+
None
140180
}
141181

142182
mc::cat_downcast(b) |
143183
mc::cat_interior(b, _) => {
144184
match ty::get(b.ty).sty {
145185
ty::ty_struct(did, _) | ty::ty_enum(did, _) => {
146186
if ty::has_dtor(bccx.tcx, did) {
147-
bccx.span_err(
148-
cmt0.span,
149-
format!("cannot move out of type `{}`, \
150-
which defines the `Drop` trait",
151-
b.ty.user_string(bccx.tcx)));
152-
false
187+
Some(cmt)
153188
} else {
154-
check_is_legal_to_move_from(bccx, cmt0, b)
189+
check_and_get_illegal_move_origin(bccx, b)
155190
}
156191
}
157192
_ => {
158-
check_is_legal_to_move_from(bccx, cmt0, b)
193+
check_and_get_illegal_move_origin(bccx, b)
159194
}
160195
}
161196
}
162197

163198
mc::cat_deref(b, _, mc::OwnedPtr) |
164199
mc::cat_discr(b, _) => {
165-
check_is_legal_to_move_from(bccx, cmt0, b)
200+
check_and_get_illegal_move_origin(bccx, b)
166201
}
167202
}
168203
}

src/librustc/middle/borrowck/gather_loans/mod.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -39,6 +39,7 @@ use syntax::ast::{Expr, FnDecl, Block, NodeId, Stmt, Pat, Local};
3939
mod lifetime;
4040
mod restrictions;
4141
mod gather_moves;
42+
mod move_error;
4243

4344
/// Context used while gathering loans:
4445
///
@@ -70,6 +71,7 @@ struct GatherLoanCtxt<'a> {
7071
bccx: &'a BorrowckCtxt<'a>,
7172
id_range: IdRange,
7273
move_data: move_data::MoveData,
74+
move_error_collector: move_error::MoveErrorCollector,
7375
all_loans: Vec<Loan>,
7476
item_ub: ast::NodeId,
7577
repeating_ids: Vec<ast::NodeId> }
@@ -121,11 +123,13 @@ pub fn gather_loans_in_fn(bccx: &BorrowckCtxt, decl: &ast::FnDecl, body: &ast::B
121123
all_loans: Vec::new(),
122124
item_ub: body.id,
123125
repeating_ids: vec!(body.id),
124-
move_data: MoveData::new()
126+
move_data: MoveData::new(),
127+
move_error_collector: move_error::MoveErrorCollector::new(),
125128
};
126129
glcx.gather_fn_arg_patterns(decl, body);
127130

128131
glcx.visit_block(body, ());
132+
glcx.report_potential_errors();
129133
let GatherLoanCtxt { id_range, all_loans, move_data, .. } = glcx;
130134
(id_range, all_loans, move_data)
131135
}
@@ -180,7 +184,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
180184
if this.bccx.is_move(ex.id) {
181185
let cmt = this.bccx.cat_expr(ex);
182186
gather_moves::gather_move_from_expr(
183-
this.bccx, &this.move_data, ex, cmt);
187+
this.bccx, &this.move_data, &this.move_error_collector, ex, cmt);
184188
}
185189

186190
// Special checks for various kinds of expressions:
@@ -270,7 +274,8 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
270274
}
271275

272276
ast::ExprFnBlock(..) | ast::ExprProc(..) => {
273-
gather_moves::gather_captures(this.bccx, &this.move_data, ex);
277+
gather_moves::gather_captures(this.bccx, &this.move_data,
278+
&this.move_error_collector, ex);
274279
this.guarantee_captures(ex);
275280
visit::walk_expr(this, ex, ());
276281
}
@@ -865,7 +870,8 @@ impl<'a> GatherLoanCtxt<'a> {
865870
// No borrows here, but there may be moves
866871
if self.bccx.is_move(pat.id) {
867872
gather_moves::gather_move_from_pat(
868-
self.bccx, &self.move_data, pat, cmt);
873+
self.bccx, &self.move_data,
874+
&self.move_error_collector, pat, cmt);
869875
}
870876
}
871877
}
@@ -916,6 +922,10 @@ impl<'a> GatherLoanCtxt<'a> {
916922
pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool {
917923
pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
918924
}
925+
926+
pub fn report_potential_errors(&self) {
927+
self.move_error_collector.report_potential_errors(self.bccx);
928+
}
919929
}
920930

921931
/// Context used while gathering loans on static initializers

0 commit comments

Comments
 (0)