Skip to content

Commit e47d2f6

Browse files
committed
extend liveness to treat bindings more like other variables
This results in a lot of warnings in rustc. I left them in because many are bugs and we should fix our code, but Graydon asked that I not touch every file in the codebase.
1 parent aa024ac commit e47d2f6

File tree

7 files changed

+158
-25
lines changed

7 files changed

+158
-25
lines changed

src/libsyntax/print/pprust.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1450,8 +1450,10 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
14501450
word_nbsp(s, ~"ref");
14511451
print_mutability(s, mutbl);
14521452
}
1453+
ast::bind_by_move => {
1454+
word_nbsp(s, ~"move");
1455+
}
14531456
ast::bind_by_implicit_ref |
1454-
ast::bind_by_move | // this is totally the wrong thing
14551457
ast::bind_by_value => {}
14561458
}
14571459
print_path(s, path, true);

src/rustc/middle/lint.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ enum lint {
5252
vecs_implicitly_copyable,
5353
deprecated_mode,
5454
deprecated_pattern,
55-
non_camel_case_types
55+
non_camel_case_types,
56+
57+
// FIXME(#3266)--make liveness warnings lintable
58+
// unused_variable,
59+
// dead_assignment
5660
}
5761

5862
fn level_to_str(lv: level) -> ~str {
@@ -134,7 +138,19 @@ fn get_lint_dict() -> lint_dict {
134138
(~"non_camel_case_types",
135139
@{lint: non_camel_case_types,
136140
desc: ~"types, variants and traits must have camel case names",
137-
default: allow})
141+
default: allow}),
142+
143+
/* FIXME(#3266)--make liveness warnings lintable
144+
(~"unused_variable",
145+
@{lint: unused_variable,
146+
desc: ~"detect variables which are not used in any way",
147+
default: warn}),
148+
149+
(~"dead_assignment",
150+
@{lint: dead_assignment,
151+
desc: ~"detect assignments that will never be read",
152+
default: warn}),
153+
*/
138154
];
139155
hash_from_strs(v)
140156
}

src/rustc/middle/liveness.rs

+96-20
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ fn check_crate(tcx: ty::ctxt,
140140
let visitor = visit::mk_vt(@{
141141
visit_fn: visit_fn,
142142
visit_local: visit_local,
143-
visit_expr: visit_expr
143+
visit_expr: visit_expr,
144+
visit_arm: visit_arm,
144145
with *visit::default_visitor()
145146
});
146147

@@ -191,11 +192,17 @@ enum RelevantDef { RelevantVar(node_id), RelevantSelf }
191192

192193
type CaptureInfo = {ln: LiveNode, is_move: bool, rv: RelevantDef};
193194

195+
enum LocalKind {
196+
FromMatch(binding_mode),
197+
FromLetWithInitializer,
198+
FromLetNoInitializer
199+
}
200+
194201
struct LocalInfo {
195202
id: node_id;
196203
ident: ident;
197204
is_mutbl: bool;
198-
initialized: bool;
205+
kind: LocalKind;
199206
}
200207

201208
enum VarKind {
@@ -209,7 +216,11 @@ enum VarKind {
209216
fn relevant_def(def: def) -> option<RelevantDef> {
210217
match def {
211218
def_self(_) => some(RelevantSelf),
212-
def_arg(nid, _) | def_local(nid, _) => some(RelevantVar(nid)),
219+
220+
def_binding(nid, _) |
221+
def_arg(nid, _) |
222+
def_local(nid, _) => some(RelevantVar(nid)),
223+
213224
_ => none
214225
}
215226
}
@@ -329,7 +340,16 @@ impl IrMaps {
329340
match vk {
330341
Arg(id, name, by_move) |
331342
Arg(id, name, by_copy) |
332-
Local(LocalInfo {id:id, ident:name, _}) => {
343+
Local(LocalInfo {id:id, ident:name,
344+
kind: FromLetNoInitializer, _}) |
345+
Local(LocalInfo {id:id, ident:name,
346+
kind: FromLetWithInitializer, _}) |
347+
Local(LocalInfo {id:id, ident:name,
348+
kind: FromMatch(bind_by_value), _}) |
349+
Local(LocalInfo {id:id, ident:name,
350+
kind: FromMatch(bind_by_ref(_)), _}) |
351+
Local(LocalInfo {id:id, ident:name,
352+
kind: FromMatch(bind_by_move), _}) => {
333353
let v = match self.last_use_map.find(expr_id) {
334354
some(v) => v,
335355
none => {
@@ -342,7 +362,8 @@ impl IrMaps {
342362
(*v).push(id);
343363
}
344364
Arg(_, _, by_ref) | Arg(_, _, by_mutbl_ref) |
345-
Arg(_, _, by_val) | Self | Field(_) | ImplicitRet => {
365+
Arg(_, _, by_val) | Self | Field(_) | ImplicitRet |
366+
Local(LocalInfo {kind: FromMatch(bind_by_implicit_ref), _}) => {
346367
debug!("--but it is not owned");
347368
}
348369
}
@@ -396,7 +417,8 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
396417
let check_vt = visit::mk_vt(@{
397418
visit_fn: check_fn,
398419
visit_local: check_local,
399-
visit_expr: check_expr
420+
visit_expr: check_expr,
421+
visit_arm: check_arm,
400422
with *visit::default_visitor()
401423
});
402424
check_vt.visit_block(body, lsets, check_vt);
@@ -419,16 +441,39 @@ fn visit_local(local: @local, &&self: @IrMaps, vt: vt<@IrMaps>) {
419441
debug!("adding local variable %d", p_id);
420442
let name = ast_util::path_to_ident(path);
421443
self.add_live_node_for_node(p_id, VarDefNode(sp));
444+
let kind = match local.node.init {
445+
some(_) => FromLetWithInitializer,
446+
none => FromLetNoInitializer
447+
};
422448
self.add_variable(Local(LocalInfo {
423-
id: p_id,
424-
ident: name,
425-
is_mutbl: local.node.is_mutbl,
426-
initialized: local.node.init.is_some()
449+
id: p_id,
450+
ident: name,
451+
is_mutbl: local.node.is_mutbl,
452+
kind: kind
427453
}));
428454
}
429455
visit::visit_local(local, self, vt);
430456
}
431457

458+
fn visit_arm(arm: arm, &&self: @IrMaps, vt: vt<@IrMaps>) {
459+
let def_map = self.tcx.def_map;
460+
for arm.pats.each |pat| {
461+
do pat_util::pat_bindings(def_map, pat) |bm, p_id, sp, path| {
462+
debug!("adding local variable %d from match with bm %?",
463+
p_id, bm);
464+
let name = ast_util::path_to_ident(path);
465+
self.add_live_node_for_node(p_id, VarDefNode(sp));
466+
self.add_variable(Local(LocalInfo {
467+
id: p_id,
468+
ident: name,
469+
is_mutbl: false,
470+
kind: FromMatch(bm)
471+
}));
472+
}
473+
}
474+
visit::visit_arm(arm, self, vt);
475+
}
476+
432477
fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
433478
match expr.node {
434479
// live nodes required for uses or definitions of variables:
@@ -612,6 +657,30 @@ impl Liveness {
612657
}
613658
}
614659

660+
fn arm_pats_bindings(pats: &[@pat], f: fn(LiveNode, Variable, span)) {
661+
// only consider the first pattern; any later patterns must have
662+
// the same bindings, and we also consider the first pattern to be
663+
// the "authoratative" set of ids
664+
if !pats.is_empty() {
665+
self.pat_bindings(pats[0], f)
666+
}
667+
}
668+
669+
fn define_bindings_in_pat(pat: @pat, succ: LiveNode) -> LiveNode {
670+
self.define_bindings_in_arm_pats([pat], succ)
671+
}
672+
673+
fn define_bindings_in_arm_pats(pats: &[@pat],
674+
succ: LiveNode) -> LiveNode {
675+
let mut succ = succ;
676+
do self.arm_pats_bindings(pats) |ln, var, _sp| {
677+
self.init_from_succ(ln, succ);
678+
self.define(ln, var);
679+
succ = ln;
680+
}
681+
succ
682+
}
683+
615684
fn idx(ln: LiveNode, var: Variable) -> uint {
616685
*ln * self.ir.num_vars + *var
617686
}
@@ -894,13 +963,8 @@ impl Liveness {
894963
// once at the func header but otherwise equivalent.
895964

896965
let opt_init = local.node.init.map(|i| i.expr );
897-
let mut succ = self.propagate_through_opt_expr(opt_init, succ);
898-
do self.pat_bindings(local.node.pat) |ln, var, _sp| {
899-
self.init_from_succ(ln, succ);
900-
self.define(ln, var);
901-
succ = ln;
902-
}
903-
succ
966+
let succ = self.propagate_through_opt_expr(opt_init, succ);
967+
self.define_bindings_in_pat(local.node.pat, succ)
904968
}
905969

906970
fn propagate_through_exprs(exprs: ~[@expr],
@@ -1003,10 +1067,12 @@ impl Liveness {
10031067
self.init_empty(ln, succ);
10041068
let mut first_merge = true;
10051069
for arms.each |arm| {
1070+
let body_succ =
1071+
self.propagate_through_block(arm.body, succ);
1072+
let guard_succ =
1073+
self.propagate_through_opt_expr(arm.guard, body_succ);
10061074
let arm_succ =
1007-
self.propagate_through_opt_expr(
1008-
arm.guard,
1009-
self.propagate_through_block(arm.body, succ));
1075+
self.define_bindings_in_arm_pats(arm.pats, guard_succ);
10101076
self.merge_from_succ(ln, arm_succ, first_merge);
10111077
first_merge = false;
10121078
};
@@ -1409,6 +1475,13 @@ fn check_local(local: @local, &&self: @Liveness, vt: vt<@Liveness>) {
14091475
visit::visit_local(local, self, vt);
14101476
}
14111477

1478+
fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) {
1479+
do self.arm_pats_bindings(arm.pats) |ln, var, sp| {
1480+
self.warn_about_unused(sp, ln, var);
1481+
}
1482+
visit::visit_arm(arm, self, vt);
1483+
}
1484+
14121485
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
14131486
match expr.node {
14141487
expr_path(_) => {
@@ -1798,10 +1871,12 @@ impl @Liveness {
17981871
};
17991872

18001873
if is_assigned {
1874+
// FIXME(#3266)--make liveness warnings lintable
18011875
self.tcx.sess.span_warn(
18021876
sp, fmt!("variable `%s` is assigned to, \
18031877
but never used", name));
18041878
} else {
1879+
// FIXME(#3266)--make liveness warnings lintable
18051880
self.tcx.sess.span_warn(
18061881
sp, fmt!("unused variable: `%s`", name));
18071882
}
@@ -1814,6 +1889,7 @@ impl @Liveness {
18141889
fn warn_about_dead_assign(sp: span, ln: LiveNode, var: Variable) {
18151890
if self.live_on_exit(ln, var).is_none() {
18161891
for self.should_warn(var).each |name| {
1892+
// FIXME(#3266)--make liveness warnings lintable
18171893
self.tcx.sess.span_warn(
18181894
sp,
18191895
fmt!("value assigned to `%s` is never read", name));

src/test/compile-fail/borrowck-issue-2657-1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
fn main() {
22
let x = some(~1);
33
match x { //~ NOTE loan of immutable local variable granted here
4-
some(ref y) => {
4+
some(ref _y) => {
55
let _a <- x; //~ ERROR moving out of immutable local variable prohibited due to outstanding loan
66
}
77
_ => {}

src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn main() {
88
// fact no outstanding loan of x!
99
x = some(0);
1010
}
11-
some(ref i) => {
11+
some(ref _i) => {
1212
x = some(1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
1313
}
1414
}

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

+13
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,24 @@ fn f3b() {
2929
fn f4() {
3030
match some(3) {
3131
some(i) => {
32+
//~^ WARNING unused variable: `i`
3233
}
3334
none => {}
3435
}
3536
}
3637

38+
enum tri {
39+
a(int), b(int), c(int)
40+
}
41+
42+
fn f4b() -> int {
43+
match a(3) {
44+
a(i) | b(i) | c(i) => {
45+
i
46+
}
47+
}
48+
}
49+
3750
// leave this in here just to trigger compile-fail:
3851
struct r {
3952
let x: ();

src/test/run-pass/option-unwrap.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
struct dtor {
2+
x: @mut int;
3+
4+
drop {
5+
// abuse access to shared mutable state to write this code
6+
*self.x -= 1;
7+
}
8+
}
9+
10+
fn unwrap<T>(+o: option<T>) -> T {
11+
match move o {
12+
some(move v) => v,
13+
none => fail
14+
}
15+
}
16+
17+
fn main() {
18+
let x = @mut 1;
19+
20+
{
21+
let b = some(dtor { x:x });
22+
let c = unwrap(b);
23+
}
24+
25+
assert *x == 0;
26+
}

0 commit comments

Comments
 (0)