Skip to content

Commit c4155f5

Browse files
committed
Change the kind checker to ignore results of last-use
and require explicit moves. Also provide more info in some error messages. Also: check that non-copyable struct fields don't get copied. Closes #3481
1 parent 9abc7f0 commit c4155f5

File tree

1 file changed

+49
-37
lines changed

1 file changed

+49
-37
lines changed

src/rustc/middle/kind.rs

+49-37
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ fn check_crate(tcx: ty::ctxt,
8989
tcx.sess.abort_if_errors();
9090
}
9191
92+
// bool flag is only used for checking closures,
93+
// where it refers to whether a var is 'move' in the
94+
// capture clause
9295
type check_fn = fn@(ctx, node_id, Option<@freevar_entry>,
93-
bool, ty::t, sp: span);
96+
bool, ty::t, sp: span);
9497
9598
// Yields the appropriate function to check the kind of closed over
9699
// variables. `id` is the node_id for some expression that creates the
@@ -111,7 +114,6 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
111114
"to copy values into a ~fn closure, use a \
112115
capture clause: `fn~(copy x)` or `|copy x|`")));
113116
}
114-
115117
// check that only immutable variables are implicitly copied in
116118
for fv.each |fv| {
117119
check_imm_free_var(cx, fv.def, fv.span);
@@ -132,7 +134,6 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
132134
"to copy values into a @fn closure, use a \
133135
capture clause: `fn~(copy x)` or `|copy x|`")));
134136
}
135-
136137
// check that only immutable variables are implicitly copied in
137138
for fv.each |fv| {
138139
check_imm_free_var(cx, fv.def, fv.span);
@@ -151,7 +152,7 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
151152
}
152153

153154
fn check_for_bare(cx: ctx, _id: node_id, _fv: Option<@freevar_entry>,
154-
_is_move: bool,_var_t: ty::t, sp: span) {
155+
_is_move: bool, _var_t: ty::t, sp: span) {
155156
cx.tcx.sess.span_err(sp, ~"attempted dynamic environment capture");
156157
}
157158

@@ -189,6 +190,7 @@ fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
189190
let cap_def = cx.tcx.def_map.get(cap_item.id);
190191
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
191192
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
193+
// Here's where is_move isn't always false...
192194
chk(cx, fn_id, None, cap_item.is_move, ty, cap_item.span);
193195
cap_def_id
194196
};
@@ -201,17 +203,10 @@ fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
201203
// skip over free variables that appear in the cap clause
202204
if captured_vars.contains(&id) { loop; }
203205

204-
// if this is the last use of the variable, then it will be
205-
// a move and not a copy
206-
let is_move = {
207-
match cx.last_use_map.find(fn_id) {
208-
Some(vars) => (*vars).contains(&id),
209-
None => false
210-
}
211-
};
212-
213206
let ty = ty::node_id_to_type(cx.tcx, id);
214-
chk(cx, fn_id, Some(*fv), is_move, ty, fv.span);
207+
// is_move is always false here. See the let captured_vars...
208+
// code above for where it's not always false.
209+
chk(cx, fn_id, Some(*fv), false, ty, fv.span);
215210
}
216211
}
217212

@@ -220,7 +215,9 @@ fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
220215

221216
fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
222217
match b.node.expr {
223-
Some(ex) => maybe_copy(cx, ex, None),
218+
Some(ex) => maybe_copy(cx, ex,
219+
Some(("Tail expressions in blocks must be copyable",
220+
"(Try adding a move)"))),
224221
_ => ()
225222
}
226223
visit::visit_block(b, cx, v);
@@ -281,33 +278,45 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
281278
expr_assign(_, ex) |
282279
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
283280
expr_ret(Some(ex)) => {
284-
maybe_copy(cx, ex, None);
281+
maybe_copy(cx, ex, Some(("Returned values must be copyable",
282+
"Try adding a move")));
285283
}
286284
expr_cast(source, _) => {
287-
maybe_copy(cx, source, None);
285+
maybe_copy(cx, source, Some(("Casted values must be copyable",
286+
"Try adding a move")));
288287
check_cast_for_escaping_regions(cx, source, e);
289288
}
290-
expr_copy(expr) => check_copy_ex(cx, expr, false, None),
289+
expr_copy(expr) => check_copy_ex(cx, expr, false,
290+
Some(("Explicit copy requires a copyable argument", ""))),
291291
// Vector add copies, but not "implicitly"
292-
expr_assign_op(_, _, ex) => check_copy_ex(cx, ex, false, None),
292+
expr_assign_op(_, _, ex) => check_copy_ex(cx, ex, false,
293+
Some(("Assignment with operation requires \
294+
a copyable argument", ""))),
293295
expr_binary(add, ls, rs) => {
294-
check_copy_ex(cx, ls, false, None);
295-
check_copy_ex(cx, rs, false, None);
296+
let reason = Some(("Binary operators require copyable arguments",
297+
""));
298+
check_copy_ex(cx, ls, false, reason);
299+
check_copy_ex(cx, rs, false, reason);
296300
}
297-
expr_rec(fields, def) => {
298-
for fields.each |field| { maybe_copy(cx, field.node.expr, None); }
301+
expr_rec(fields, def) | expr_struct(_, fields, def) => {
302+
for fields.each |field| { maybe_copy(cx, field.node.expr,
303+
Some(("Record or struct fields require \
304+
copyable arguments", ""))); }
299305
match def {
300306
Some(ex) => {
301307
// All noncopyable fields must be overridden
302308
let t = ty::expr_ty(cx.tcx, ex);
303309
let ty_fields = match ty::get(t).sty {
304310
ty::ty_rec(f) => f,
305-
_ => cx.tcx.sess.span_bug(ex.span, ~"bad expr type in record")
311+
ty::ty_class(did, substs) =>
312+
ty::class_items_as_fields(cx.tcx, did, &substs),
313+
_ => cx.tcx.sess.span_bug(ex.span,
314+
~"bad base expr type in record")
306315
};
307316
for ty_fields.each |tf| {
308317
if !vec::any(fields, |f| f.node.ident == tf.ident ) &&
309318
!ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
310-
cx.tcx.sess.span_err(ex.span,
319+
cx.tcx.sess.span_err(e.span,
311320
~"copying a noncopyable value");
312321
}
313322
}
@@ -316,16 +325,16 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
316325
}
317326
}
318327
expr_tup(exprs) | expr_vec(exprs, _) => {
319-
for exprs.each |expr| { maybe_copy(cx, *expr, None); }
328+
for exprs.each |expr| { maybe_copy(cx, *expr,
329+
Some(("Tuple or vec elements must be copyable", ""))); }
320330
}
321331
expr_call(f, args, _) => {
322-
let mut i = 0u;
323-
for ty::ty_fn_args(ty::expr_ty(cx.tcx, f)).each |arg_t| {
332+
for ty::ty_fn_args(ty::expr_ty(cx.tcx, f)).eachi |i, arg_t| {
324333
match ty::arg_mode(cx.tcx, *arg_t) {
325-
by_copy => maybe_copy(cx, args[i], None),
334+
by_copy => maybe_copy(cx, args[i],
335+
Some(("Callee takes its argument by copy", ""))),
326336
by_ref | by_val | by_move => ()
327337
}
328-
i += 1u;
329338
}
330339
}
331340
expr_field(lhs, _, _) => {
@@ -334,7 +343,9 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
334343
match cx.method_map.find(e.id) {
335344
Some(ref mme) => {
336345
match ty::arg_mode(cx.tcx, mme.self_arg) {
337-
by_copy => maybe_copy(cx, lhs, None),
346+
by_copy => maybe_copy(cx, lhs,
347+
Some(("Method call takes its self argument by copy",
348+
""))),
338349
by_ref | by_val | by_move => ()
339350
}
340351
}
@@ -344,10 +355,12 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
344355
expr_repeat(element, count_expr, _) => {
345356
let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
346357
if count == 1 {
347-
maybe_copy(cx, element, None);
358+
maybe_copy(cx, element, Some(("Trivial repeat takes its element \
359+
by copy", "")));
348360
} else {
349361
let element_ty = ty::expr_ty(cx.tcx, element);
350-
check_copy(cx, element.id, element_ty, element.span, true, None);
362+
check_copy(cx, element.id, element_ty, element.span, true,
363+
Some(("Repeat takes its elements by copy", "")));
351364
}
352365
}
353366
_ => { }
@@ -360,7 +373,9 @@ fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
360373
stmt_decl(@{node: decl_local(locals), _}, _) => {
361374
for locals.each |local| {
362375
match local.node.init {
363-
Some({op: init_assign, expr}) => maybe_copy(cx, expr, None),
376+
Some({op: init_assign, expr}) =>
377+
maybe_copy(cx, expr, Some(("Initializer statement \
378+
takes its right-hand side by copy", ""))),
364379
_ => {}
365380
}
366381
}
@@ -434,9 +449,6 @@ fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
434449
why: Option<(&str,&str)>) {
435450
if ty::expr_is_lval(cx.tcx, cx.method_map, ex) &&
436451

437-
// this is a move
438-
!cx.last_use_map.contains_key(ex.id) &&
439-
440452
// a reference to a constant like `none`... no need to warn
441453
// about *this* even if the type is Option<~int>
442454
!is_nullary_variant(cx, ex) &&

0 commit comments

Comments
 (0)