Skip to content

Commit 62fe587

Browse files
committed
revisit error message; create spill map
1 parent 123c5c4 commit 62fe587

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

src/rustc/middle/liveness.rs

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,20 @@ import capture::{cap_move, cap_drop, cap_copy, cap_ref};
5858
export check_crate;
5959
export last_use_map;
6060

61+
// Maps from an expr id to a list of variable ids for which this expr
62+
// is the last use. Typically, the expr is a path and the node id is
63+
// the local/argument/etc that the path refers to. However, it also
64+
// possible for the expr to be a closure, in which case the list is a
65+
// list of closed over variables that can be moved into the closure.
6166
type last_use_map = hashmap<node_id, @dvec<node_id>>;
6267

68+
// A set of variable ids which must be spilled (stored on the stack).
69+
// We add in any variables or arguments where:
70+
// (1) the variables are moved;
71+
// (2) the address of the variable/argument is taken;
72+
// or (3) we find a last use (as they may be moved).
73+
type spill_map = hashmap<node_id, ()>;
74+
6375
enum variable = uint;
6476
enum live_node = uint;
6577

@@ -81,7 +93,9 @@ fn check_crate(tcx: ty::ctxt,
8193
});
8294

8395
let last_use_map = int_hash();
84-
let initial_maps = @ir_maps(tcx, method_map, last_use_map);
96+
let spill_map = int_hash();
97+
let initial_maps = @ir_maps(tcx, method_map,
98+
last_use_map, spill_map);
8599
visit::visit_crate(*crate, initial_maps, visitor);
86100
tcx.sess.abort_if_errors();
87101
ret last_use_map;
@@ -141,6 +155,7 @@ class ir_maps {
141155
let tcx: ty::ctxt;
142156
let method_map: typeck::method_map;
143157
let last_use_map: last_use_map;
158+
let spill_map: spill_map;
144159

145160
let mut num_live_nodes: uint;
146161
let mut num_vars: uint;
@@ -152,10 +167,11 @@ class ir_maps {
152167
let mut lnks: [live_node_kind];
153168

154169
new(tcx: ty::ctxt, method_map: typeck::method_map,
155-
last_use_map: hashmap<node_id, @dvec<node_id>>) {
170+
last_use_map: last_use_map, spill_map: spill_map) {
156171
self.tcx = tcx;
157172
self.method_map = method_map;
158173
self.last_use_map = last_use_map;
174+
self.spill_map = spill_map;
159175

160176
self.num_live_nodes = 0u;
161177
self.num_vars = 0u;
@@ -236,6 +252,11 @@ class ir_maps {
236252
expr_id, id, name];
237253
(*v).push(id);
238254
}
255+
256+
fn add_spill(var: variable) {
257+
let id = self.var_infos[*var].id;
258+
if id != 0 { self.spill_map.insert(id, ()); }
259+
}
239260
}
240261

241262
fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
@@ -245,7 +266,7 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
245266

246267
// swap in a new set of IR maps for this function body:
247268
let fn_maps = @ir_maps(self.tcx, self.method_map,
248-
self.last_use_map);
269+
self.last_use_map, self.spill_map);
249270

250271
#debug["creating fn_maps: %x", ptr::addr_of(*fn_maps) as uint];
251272

@@ -453,6 +474,18 @@ class liveness {
453474
}
454475
}
455476

477+
fn variable_from_path(expr: @expr) -> option<variable> {
478+
alt expr.node {
479+
expr_path(_) {
480+
let def = self.tcx.def_map.get(expr.id);
481+
relevant_def(def).map { |rdef|
482+
self.variable_from_rdef(rdef, expr.span)
483+
}
484+
}
485+
_ {none}
486+
}
487+
}
488+
456489
fn variable(node_id: node_id, span: span) -> variable {
457490
(*self.ir).variable(node_id, span)
458491
}
@@ -682,11 +715,18 @@ class liveness {
682715
// inputs passed by & mode should be considered live on exit:
683716
for decl.inputs.each { |arg|
684717
alt ty::resolved_mode(self.tcx, arg.mode) {
685-
by_mutbl_ref {
718+
by_mutbl_ref | by_ref | by_val {
719+
// These are "non-owned" modes, so register a read at
720+
// the end. This will prevent us from moving out of
721+
// such variables but also prevent us from registering
722+
// last uses and so forth.
686723
let var = self.variable(arg.id, blk.span);
687724
self.acc(self.s.exit_ln, var, ACC_READ);
688725
}
689-
by_val | by_ref | by_move | by_copy {}
726+
by_move | by_copy {
727+
// These are owned modes. If we don't use the
728+
// variable, nobody will.
729+
}
690730
}
691731
}
692732

@@ -1325,7 +1365,11 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) {
13251365
vt.visit_expr(f, self, vt);
13261366
vec::iter2(args, targs) { |arg_expr, arg_ty|
13271367
alt ty::resolved_mode(self.tcx, arg_ty.mode) {
1328-
by_val | by_ref | by_mutbl_ref | by_copy {
1368+
by_val | by_copy {
1369+
vt.visit_expr(arg_expr, self, vt);
1370+
}
1371+
by_ref | by_mutbl_ref {
1372+
self.spill_expr(arg_expr);
13291373
vt.visit_expr(arg_expr, self, vt);
13301374
}
13311375
by_move {
@@ -1335,13 +1379,17 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) {
13351379
}
13361380
}
13371381

1382+
expr_addr_of(_, arg_expr) {
1383+
self.spill_expr(arg_expr);
1384+
}
1385+
13381386
// no correctness conditions related to liveness
13391387
expr_if_check(*) | expr_if(*) | expr_alt(*) |
13401388
expr_while(*) | expr_loop(*) |
13411389
expr_index(*) | expr_field(*) | expr_vstore(*) |
13421390
expr_vec(*) | expr_rec(*) | expr_tup(*) |
13431391
expr_bind(*) | expr_new(*) | expr_log(*) | expr_binary(*) |
1344-
expr_assert(*) | expr_check(*) | expr_addr_of(*) | expr_copy(*) |
1392+
expr_assert(*) | expr_check(*) | expr_copy(*) |
13451393
expr_loop_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) |
13461394
expr_ret(*) | expr_break | expr_cont | expr_lit(_) |
13471395
expr_block(*) | expr_swap(*) | expr_mac(*) {
@@ -1411,7 +1459,10 @@ impl check_methods for @liveness {
14111459
ln.to_str(), var.to_str()];
14121460

14131461
alt (*self).live_on_exit(ln, var) {
1414-
none {}
1462+
none {
1463+
// update spill map to include this variable, as it is moved:
1464+
(*self.ir).add_spill(var);
1465+
}
14151466
some(lnk) {
14161467
self.report_illegal_read(span, lnk, var, moved_variable);
14171468
self.tcx.sess.span_note(
@@ -1426,10 +1477,20 @@ impl check_methods for @liveness {
14261477
some(_) {}
14271478
none {
14281479
(*self.ir).add_last_use(expr.id, var);
1480+
1481+
// update spill map to include this variable, as it may be moved:
1482+
(*self.ir).add_spill(var);
14291483
}
14301484
}
14311485
}
14321486

1487+
fn spill_expr(expr: @expr) {
1488+
alt (*self).variable_from_path(expr) {
1489+
some(var) {(*self.ir).add_spill(var)}
1490+
none {}
1491+
}
1492+
}
1493+
14331494
fn check_move_from_expr(expr: @expr, vt: vt<@liveness>) {
14341495
#debug["check_move_from_expr(node %d: %s)",
14351496
expr.id, expr_to_str(expr)];
@@ -1441,12 +1502,9 @@ impl check_methods for @liveness {
14411502

14421503
alt expr.node {
14431504
expr_path(_) {
1444-
let def = self.tcx.def_map.get(expr.id);
1445-
alt relevant_def(def) {
1446-
some(rdef) {
1447-
// Moving from a variable is allowed if is is not live.
1505+
alt (*self).variable_from_path(expr) {
1506+
some(var) {
14481507
let ln = (*self).live_node(expr.id, expr.span);
1449-
let var = (*self).variable_from_rdef(rdef, expr.span);
14501508
self.check_move_from_var(expr.span, ln, var);
14511509
}
14521510
none {}
@@ -1606,8 +1664,24 @@ impl check_methods for @liveness {
16061664
fn warn_about_unused(sp: span, ln: live_node, var: variable) -> bool {
16071665
if !(*self).used_on_entry(ln, var) {
16081666
for self.should_warn(var).each { |name|
1609-
self.tcx.sess.span_warn(
1610-
sp, #fmt["unused variable: `%s`", name]);
1667+
1668+
// annoying: for parameters in funcs like `fn(x: int)
1669+
// {ret}`, there is only one node, so asking about
1670+
// assigned_on_exit() is not meaningful.
1671+
let is_assigned = if ln == self.s.exit_ln {
1672+
false
1673+
} else {
1674+
(*self).assigned_on_exit(ln, var).is_some()
1675+
};
1676+
1677+
if is_assigned {
1678+
self.tcx.sess.span_warn(
1679+
sp, #fmt["variable `%s` is assigned to, \
1680+
but never used", name]);
1681+
} else {
1682+
self.tcx.sess.span_warn(
1683+
sp, #fmt["unused variable: `%s`", name]);
1684+
}
16111685
}
16121686
ret true;
16131687
}

src/test/compile-fail/liveness-unused.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ fn f2() {
1313

1414
fn f3() {
1515
let mut x = 3;
16-
//!^ WARNING unused variable: `x`
16+
//!^ WARNING variable `x` is assigned to, but never used
1717
x += 4;
1818
//!^ WARNING value assigned to `x` is never read
1919
}
2020

2121
fn f3b() {
2222
let mut z = 3;
23-
//!^ WARNING unused variable: `z`
23+
//!^ WARNING variable `z` is assigned to, but never used
2424
loop {
2525
z += 4;
2626
}

0 commit comments

Comments
 (0)