]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/liveness.rs
77cb5f667fd000c9b7fbe57655718d4181ea901c
[rust.git] / src / librustc / middle / liveness.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12  * A classic liveness analysis based on dataflow over the AST.  Computes,
13  * for each local variable in a function, whether that variable is live
14  * at a given point.  Program execution points are identified by their
15  * id.
16  *
17  * # Basic idea
18  *
19  * The basic model is that each local variable is assigned an index.  We
20  * represent sets of local variables using a vector indexed by this
21  * index.  The value in the vector is either 0, indicating the variable
22  * is dead, or the id of an expression that uses the variable.
23  *
24  * We conceptually walk over the AST in reverse execution order.  If we
25  * find a use of a variable, we add it to the set of live variables.  If
26  * we find an assignment to a variable, we remove it from the set of live
27  * variables.  When we have to merge two flows, we take the union of
28  * those two flows---if the variable is live on both paths, we simply
29  * pick one id.  In the event of loops, we continue doing this until a
30  * fixed point is reached.
31  *
32  * ## Checking initialization
33  *
34  * At the function entry point, all variables must be dead.  If this is
35  * not the case, we can report an error using the id found in the set of
36  * live variables, which identifies a use of the variable which is not
37  * dominated by an assignment.
38  *
39  * ## Checking moves
40  *
41  * After each explicit move, the variable must be dead.
42  *
43  * ## Computing last uses
44  *
45  * Any use of the variable where the variable is dead afterwards is a
46  * last use.
47  *
48  * # Implementation details
49  *
50  * The actual implementation contains two (nested) walks over the AST.
51  * The outer walk has the job of building up the ir_maps instance for the
52  * enclosing function.  On the way down the tree, it identifies those AST
53  * nodes and variable IDs that will be needed for the liveness analysis
54  * and assigns them contiguous IDs.  The liveness id for an AST node is
55  * called a `live_node` (it's a newtype'd uint) and the id for a variable
56  * is called a `variable` (another newtype'd uint).
57  *
58  * On the way back up the tree, as we are about to exit from a function
59  * declaration we allocate a `liveness` instance.  Now that we know
60  * precisely how many nodes and variables we need, we can allocate all
61  * the various arrays that we will need to precisely the right size.  We then
62  * perform the actual propagation on the `liveness` instance.
63  *
64  * This propagation is encoded in the various `propagate_through_*()`
65  * methods.  It effectively does a reverse walk of the AST; whenever we
66  * reach a loop node, we iterate until a fixed point is reached.
67  *
68  * ## The `Users` struct
69  *
70  * At each live node `N`, we track three pieces of information for each
71  * variable `V` (these are encapsulated in the `Users` struct):
72  *
73  * - `reader`: the `LiveNode` ID of some node which will read the value
74  *    that `V` holds on entry to `N`.  Formally: a node `M` such
75  *    that there exists a path `P` from `N` to `M` where `P` does not
76  *    write `V`.  If the `reader` is `invalid_node()`, then the current
77  *    value will never be read (the variable is dead, essentially).
78  *
79  * - `writer`: the `LiveNode` ID of some node which will write the
80  *    variable `V` and which is reachable from `N`.  Formally: a node `M`
81  *    such that there exists a path `P` from `N` to `M` and `M` writes
82  *    `V`.  If the `writer` is `invalid_node()`, then there is no writer
83  *    of `V` that follows `N`.
84  *
85  * - `used`: a boolean value indicating whether `V` is *used*.  We
86  *   distinguish a *read* from a *use* in that a *use* is some read that
87  *   is not just used to generate a new value.  For example, `x += 1` is
88  *   a read but not a use.  This is used to generate better warnings.
89  *
90  * ## Special Variables
91  *
92  * We generate various special variables for various, well, special purposes.
93  * These are described in the `specials` struct:
94  *
95  * - `exit_ln`: a live node that is generated to represent every 'exit' from
96  *   the function, whether it be by explicit return, fail, or other means.
97  *
98  * - `fallthrough_ln`: a live node that represents a fallthrough
99  *
100  * - `no_ret_var`: a synthetic variable that is only 'read' from, the
101  *   fallthrough node.  This allows us to detect functions where we fail
102  *   to return explicitly.
103  */
104
105 use middle::def::*;
106 use middle::freevars;
107 use middle::mem_categorization::Typer;
108 use middle::pat_util;
109 use middle::ty;
110 use lint;
111 use util::nodemap::NodeMap;
112
113 use std::fmt;
114 use std::gc::Gc;
115 use std::io;
116 use std::mem::transmute;
117 use std::rc::Rc;
118 use std::str;
119 use std::uint;
120 use syntax::ast::*;
121 use syntax::codemap::{BytePos, original_sp, Span};
122 use syntax::parse::token::special_idents;
123 use syntax::parse::token;
124 use syntax::print::pprust::{expr_to_str, block_to_str};
125 use syntax::{visit, ast_util};
126 use syntax::visit::{Visitor, FnKind};
127
128 #[deriving(PartialEq)]
129 struct Variable(uint);
130 #[deriving(PartialEq)]
131 struct LiveNode(uint);
132
133 impl Variable {
134     fn get(&self) -> uint { let Variable(v) = *self; v }
135 }
136
137 impl LiveNode {
138     fn get(&self) -> uint { let LiveNode(v) = *self; v }
139 }
140
141 impl Clone for LiveNode {
142     fn clone(&self) -> LiveNode {
143         LiveNode(self.get())
144     }
145 }
146
147 #[deriving(PartialEq)]
148 enum LiveNodeKind {
149     FreeVarNode(Span),
150     ExprNode(Span),
151     VarDefNode(Span),
152     ExitNode
153 }
154
155 fn live_node_kind_to_str(lnk: LiveNodeKind, cx: &ty::ctxt) -> String {
156     let cm = cx.sess.codemap();
157     match lnk {
158         FreeVarNode(s) => {
159             format!("Free var node [{}]", cm.span_to_str(s))
160         }
161         ExprNode(s) => {
162             format!("Expr node [{}]", cm.span_to_str(s))
163         }
164         VarDefNode(s) => {
165             format!("Var def node [{}]", cm.span_to_str(s))
166         }
167         ExitNode => "Exit node".to_string(),
168     }
169 }
170
171 impl<'a> Visitor<()> for IrMaps<'a> {
172     fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, n: NodeId, _: ()) {
173         visit_fn(self, fk, fd, b, s, n);
174     }
175     fn visit_local(&mut self, l: &Local, _: ()) { visit_local(self, l); }
176     fn visit_expr(&mut self, ex: &Expr, _: ()) { visit_expr(self, ex); }
177     fn visit_arm(&mut self, a: &Arm, _: ()) { visit_arm(self, a); }
178 }
179
180 pub fn check_crate(tcx: &ty::ctxt,
181                    krate: &Crate) {
182     visit::walk_crate(&mut IrMaps::new(tcx), krate, ());
183     tcx.sess.abort_if_errors();
184 }
185
186 impl fmt::Show for LiveNode {
187     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188         write!(f, "ln({})", self.get())
189     }
190 }
191
192 impl fmt::Show for Variable {
193     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
194         write!(f, "v({})", self.get())
195     }
196 }
197
198 // ______________________________________________________________________
199 // Creating ir_maps
200 //
201 // This is the first pass and the one that drives the main
202 // computation.  It walks up and down the IR once.  On the way down,
203 // we count for each function the number of variables as well as
204 // liveness nodes.  A liveness node is basically an expression or
205 // capture clause that does something of interest: either it has
206 // interesting control flow or it uses/defines a local variable.
207 //
208 // On the way back up, at each function node we create liveness sets
209 // (we now know precisely how big to make our various vectors and so
210 // forth) and then do the data-flow propagation to compute the set
211 // of live variables at each program point.
212 //
213 // Finally, we run back over the IR one last time and, using the
214 // computed liveness, check various safety conditions.  For example,
215 // there must be no live nodes at the definition site for a variable
216 // unless it has an initializer.  Similarly, each non-mutable local
217 // variable must not be assigned if there is some successor
218 // assignment.  And so forth.
219
220 impl LiveNode {
221     fn is_valid(&self) -> bool {
222         self.get() != uint::MAX
223     }
224 }
225
226 fn invalid_node() -> LiveNode { LiveNode(uint::MAX) }
227
228 struct CaptureInfo {
229     ln: LiveNode,
230     var_nid: NodeId
231 }
232
233 struct LocalInfo {
234     id: NodeId,
235     ident: Ident
236 }
237
238 enum VarKind {
239     Arg(NodeId, Ident),
240     Local(LocalInfo),
241     ImplicitRet
242 }
243
244 struct IrMaps<'a> {
245     tcx: &'a ty::ctxt,
246
247     num_live_nodes: uint,
248     num_vars: uint,
249     live_node_map: NodeMap<LiveNode>,
250     variable_map: NodeMap<Variable>,
251     capture_info_map: NodeMap<Rc<Vec<CaptureInfo>>>,
252     var_kinds: Vec<VarKind>,
253     lnks: Vec<LiveNodeKind>,
254 }
255
256 impl<'a> IrMaps<'a> {
257     fn new(tcx: &'a ty::ctxt) -> IrMaps<'a> {
258         IrMaps {
259             tcx: tcx,
260             num_live_nodes: 0,
261             num_vars: 0,
262             live_node_map: NodeMap::new(),
263             variable_map: NodeMap::new(),
264             capture_info_map: NodeMap::new(),
265             var_kinds: Vec::new(),
266             lnks: Vec::new(),
267         }
268     }
269
270     fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode {
271         let ln = LiveNode(self.num_live_nodes);
272         self.lnks.push(lnk);
273         self.num_live_nodes += 1;
274
275         debug!("{} is of kind {}", ln.to_str(),
276                live_node_kind_to_str(lnk, self.tcx));
277
278         ln
279     }
280
281     fn add_live_node_for_node(&mut self, node_id: NodeId, lnk: LiveNodeKind) {
282         let ln = self.add_live_node(lnk);
283         self.live_node_map.insert(node_id, ln);
284
285         debug!("{} is node {}", ln.to_str(), node_id);
286     }
287
288     fn add_variable(&mut self, vk: VarKind) -> Variable {
289         let v = Variable(self.num_vars);
290         self.var_kinds.push(vk);
291         self.num_vars += 1;
292
293         match vk {
294             Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
295                 self.variable_map.insert(node_id, v);
296             },
297             ImplicitRet => {}
298         }
299
300         debug!("{} is {:?}", v.to_str(), vk);
301
302         v
303     }
304
305     fn variable(&self, node_id: NodeId, span: Span) -> Variable {
306         match self.variable_map.find(&node_id) {
307           Some(&var) => var,
308           None => {
309             self.tcx
310                 .sess
311                 .span_bug(span, format!("no variable registered for id {}",
312                                         node_id).as_slice());
313           }
314         }
315     }
316
317     fn variable_name(&self, var: Variable) -> String {
318         match self.var_kinds.get(var.get()) {
319             &Local(LocalInfo { ident: nm, .. }) | &Arg(_, nm) => {
320                 token::get_ident(nm).get().to_str()
321             },
322             &ImplicitRet => "<implicit-ret>".to_string()
323         }
324     }
325
326     fn set_captures(&mut self, node_id: NodeId, cs: Vec<CaptureInfo>) {
327         self.capture_info_map.insert(node_id, Rc::new(cs));
328     }
329
330     fn lnk(&self, ln: LiveNode) -> LiveNodeKind {
331         *self.lnks.get(ln.get())
332     }
333 }
334
335 impl<'a> Visitor<()> for Liveness<'a> {
336     fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, n: NodeId, _: ()) {
337         check_fn(self, fk, fd, b, s, n);
338     }
339     fn visit_local(&mut self, l: &Local, _: ()) {
340         check_local(self, l);
341     }
342     fn visit_expr(&mut self, ex: &Expr, _: ()) {
343         check_expr(self, ex);
344     }
345     fn visit_arm(&mut self, a: &Arm, _: ()) {
346         check_arm(self, a);
347     }
348 }
349
350 fn visit_fn(ir: &mut IrMaps,
351             fk: &FnKind,
352             decl: &FnDecl,
353             body: &Block,
354             sp: Span,
355             id: NodeId) {
356     debug!("visit_fn: id={}", id);
357     let _i = ::util::common::indenter();
358
359     // swap in a new set of IR maps for this function body:
360     let mut fn_maps = IrMaps::new(ir.tcx);
361
362     unsafe {
363         debug!("creating fn_maps: {}",
364                transmute::<&IrMaps, *const IrMaps>(&fn_maps));
365     }
366
367     for arg in decl.inputs.iter() {
368         pat_util::pat_bindings(&ir.tcx.def_map,
369                                &*arg.pat,
370                                |_bm, arg_id, _x, path| {
371             debug!("adding argument {}", arg_id);
372             let ident = ast_util::path_to_ident(path);
373             fn_maps.add_variable(Arg(arg_id, ident));
374         })
375     };
376
377     // gather up the various local variables, significant expressions,
378     // and so forth:
379     visit::walk_fn(&mut fn_maps, fk, decl, body, sp, ());
380
381     // Special nodes and variables:
382     // - exit_ln represents the end of the fn, either by return or fail
383     // - implicit_ret_var is a pseudo-variable that represents
384     //   an implicit return
385     let specials = Specials {
386         exit_ln: fn_maps.add_live_node(ExitNode),
387         fallthrough_ln: fn_maps.add_live_node(ExitNode),
388         no_ret_var: fn_maps.add_variable(ImplicitRet)
389     };
390
391     // compute liveness
392     let mut lsets = Liveness::new(&mut fn_maps, specials);
393     let entry_ln = lsets.compute(decl, body);
394
395     // check for various error conditions
396     lsets.visit_block(body, ());
397     lsets.check_ret(id, sp, fk, entry_ln, body);
398     lsets.warn_about_unused_args(decl, entry_ln);
399 }
400
401 fn visit_local(ir: &mut IrMaps, local: &Local) {
402     pat_util::pat_bindings(&ir.tcx.def_map, &*local.pat, |_, p_id, sp, path| {
403         debug!("adding local variable {}", p_id);
404         let name = ast_util::path_to_ident(path);
405         ir.add_live_node_for_node(p_id, VarDefNode(sp));
406         ir.add_variable(Local(LocalInfo {
407           id: p_id,
408           ident: name
409         }));
410     });
411     visit::walk_local(ir, local, ());
412 }
413
414 fn visit_arm(ir: &mut IrMaps, arm: &Arm) {
415     for pat in arm.pats.iter() {
416         pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path| {
417             debug!("adding local variable {} from match with bm {:?}",
418                    p_id, bm);
419             let name = ast_util::path_to_ident(path);
420             ir.add_live_node_for_node(p_id, VarDefNode(sp));
421             ir.add_variable(Local(LocalInfo {
422                 id: p_id,
423                 ident: name
424             }));
425         })
426     }
427     visit::walk_arm(ir, arm, ());
428 }
429
430 fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
431     match def {
432         DefBinding(nid, _) |
433         DefArg(nid, _) |
434         DefLocal(nid, _) => Some(nid),
435
436       _ => None
437     }
438 }
439
440 fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
441     match expr.node {
442       // live nodes required for uses or definitions of variables:
443       ExprPath(_) => {
444         let def = ir.tcx.def_map.borrow().get_copy(&expr.id);
445         debug!("expr {}: path that leads to {:?}", expr.id, def);
446         if moved_variable_node_id_from_def(def).is_some() {
447             ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
448         }
449         visit::walk_expr(ir, expr, ());
450       }
451       ExprFnBlock(..) | ExprProc(..) => {
452         // Interesting control flow (for loops can contain labeled
453         // breaks or continues)
454         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
455
456         // Make a live_node for each captured variable, with the span
457         // being the location that the variable is used.  This results
458         // in better error messages than just pointing at the closure
459         // construction site.
460         let mut call_caps = Vec::new();
461         freevars::with_freevars(ir.tcx, expr.id, |freevars| {
462             for fv in freevars.iter() {
463                 match moved_variable_node_id_from_def(fv.def) {
464                     Some(rv) => {
465                         let fv_ln = ir.add_live_node(FreeVarNode(fv.span));
466                         call_caps.push(CaptureInfo {ln: fv_ln,
467                                                     var_nid: rv});
468                     }
469                     None => {}
470                 }
471             }
472         });
473         ir.set_captures(expr.id, call_caps);
474
475         visit::walk_expr(ir, expr, ());
476       }
477
478       // live nodes required for interesting control flow:
479       ExprIf(..) | ExprMatch(..) | ExprWhile(..) | ExprLoop(..) => {
480         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
481         visit::walk_expr(ir, expr, ());
482       }
483       ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
484       ExprBinary(op, _, _) if ast_util::lazy_binop(op) => {
485         ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
486         visit::walk_expr(ir, expr, ());
487       }
488
489       // otherwise, live nodes are not required:
490       ExprIndex(..) | ExprField(..) | ExprVstore(..) | ExprVec(..) |
491       ExprCall(..) | ExprMethodCall(..) | ExprTup(..) |
492       ExprBinary(..) | ExprAddrOf(..) |
493       ExprCast(..) | ExprUnary(..) | ExprBreak(_) |
494       ExprAgain(_) | ExprLit(_) | ExprRet(..) | ExprBlock(..) |
495       ExprAssign(..) | ExprAssignOp(..) | ExprMac(..) |
496       ExprStruct(..) | ExprRepeat(..) | ExprParen(..) |
497       ExprInlineAsm(..) | ExprBox(..) => {
498           visit::walk_expr(ir, expr, ());
499       }
500     }
501 }
502
503 // ______________________________________________________________________
504 // Computing liveness sets
505 //
506 // Actually we compute just a bit more than just liveness, but we use
507 // the same basic propagation framework in all cases.
508
509 #[deriving(Clone)]
510 struct Users {
511     reader: LiveNode,
512     writer: LiveNode,
513     used: bool
514 }
515
516 fn invalid_users() -> Users {
517     Users {
518         reader: invalid_node(),
519         writer: invalid_node(),
520         used: false
521     }
522 }
523
524 struct Specials {
525     exit_ln: LiveNode,
526     fallthrough_ln: LiveNode,
527     no_ret_var: Variable
528 }
529
530 static ACC_READ: uint = 1u;
531 static ACC_WRITE: uint = 2u;
532 static ACC_USE: uint = 4u;
533
534 struct Liveness<'a> {
535     ir: &'a mut IrMaps<'a>,
536     s: Specials,
537     successors: Vec<LiveNode>,
538     users: Vec<Users>,
539     // The list of node IDs for the nested loop scopes
540     // we're in.
541     loop_scope: Vec<NodeId>,
542     // mappings from loop node ID to LiveNode
543     // ("break" label should map to loop node ID,
544     // it probably doesn't now)
545     break_ln: NodeMap<LiveNode>,
546     cont_ln: NodeMap<LiveNode>
547 }
548
549 impl<'a> Liveness<'a> {
550     fn new(ir: &'a mut IrMaps<'a>, specials: Specials) -> Liveness<'a> {
551         let num_live_nodes = ir.num_live_nodes;
552         let num_vars = ir.num_vars;
553         Liveness {
554             ir: ir,
555             s: specials,
556             successors: Vec::from_elem(num_live_nodes, invalid_node()),
557             users: Vec::from_elem(num_live_nodes * num_vars, invalid_users()),
558             loop_scope: Vec::new(),
559             break_ln: NodeMap::new(),
560             cont_ln: NodeMap::new(),
561         }
562     }
563
564     fn live_node(&self, node_id: NodeId, span: Span) -> LiveNode {
565         match self.ir.live_node_map.find(&node_id) {
566           Some(&ln) => ln,
567           None => {
568             // This must be a mismatch between the ir_map construction
569             // above and the propagation code below; the two sets of
570             // code have to agree about which AST nodes are worth
571             // creating liveness nodes for.
572             self.ir.tcx.sess.span_bug(
573                 span,
574                 format!("no live node registered for node {}",
575                         node_id).as_slice());
576           }
577         }
578     }
579
580     fn variable(&self, node_id: NodeId, span: Span) -> Variable {
581         self.ir.variable(node_id, span)
582     }
583
584     fn pat_bindings(&mut self,
585                     pat: &Pat,
586                     f: |&mut Liveness<'a>, LiveNode, Variable, Span, NodeId|) {
587         pat_util::pat_bindings(&self.ir.tcx.def_map, pat, |_bm, p_id, sp, _n| {
588             let ln = self.live_node(p_id, sp);
589             let var = self.variable(p_id, sp);
590             f(self, ln, var, sp, p_id);
591         })
592     }
593
594     fn arm_pats_bindings(&mut self,
595                          pats: &[Gc<Pat>],
596                          f: |&mut Liveness<'a>, LiveNode, Variable, Span, NodeId|) {
597         // only consider the first pattern; any later patterns must have
598         // the same bindings, and we also consider the first pattern to be
599         // the "authoritative" set of ids
600         if !pats.is_empty() {
601             self.pat_bindings(&*pats[0], f)
602         }
603     }
604
605     fn define_bindings_in_pat(&mut self, pat: Gc<Pat>, succ: LiveNode)
606                               -> LiveNode {
607         self.define_bindings_in_arm_pats([pat], succ)
608     }
609
610     fn define_bindings_in_arm_pats(&mut self, pats: &[Gc<Pat>], succ: LiveNode)
611                                    -> LiveNode {
612         let mut succ = succ;
613         self.arm_pats_bindings(pats, |this, ln, var, _sp, _id| {
614             this.init_from_succ(ln, succ);
615             this.define(ln, var);
616             succ = ln;
617         });
618         succ
619     }
620
621     fn idx(&self, ln: LiveNode, var: Variable) -> uint {
622         ln.get() * self.ir.num_vars + var.get()
623     }
624
625     fn live_on_entry(&self, ln: LiveNode, var: Variable)
626                       -> Option<LiveNodeKind> {
627         assert!(ln.is_valid());
628         let reader = self.users.get(self.idx(ln, var)).reader;
629         if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
630     }
631
632     /*
633     Is this variable live on entry to any of its successor nodes?
634     */
635     fn live_on_exit(&self, ln: LiveNode, var: Variable)
636                     -> Option<LiveNodeKind> {
637         let successor = *self.successors.get(ln.get());
638         self.live_on_entry(successor, var)
639     }
640
641     fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
642         assert!(ln.is_valid());
643         self.users.get(self.idx(ln, var)).used
644     }
645
646     fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
647                          -> Option<LiveNodeKind> {
648         assert!(ln.is_valid());
649         let writer = self.users.get(self.idx(ln, var)).writer;
650         if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
651     }
652
653     fn assigned_on_exit(&self, ln: LiveNode, var: Variable)
654                         -> Option<LiveNodeKind> {
655         let successor = *self.successors.get(ln.get());
656         self.assigned_on_entry(successor, var)
657     }
658
659     fn indices2(&mut self,
660                 ln: LiveNode,
661                 succ_ln: LiveNode,
662                 op: |&mut Liveness<'a>, uint, uint|) {
663         let node_base_idx = self.idx(ln, Variable(0u));
664         let succ_base_idx = self.idx(succ_ln, Variable(0u));
665         for var_idx in range(0u, self.ir.num_vars) {
666             op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
667         }
668     }
669
670     fn write_vars(&self,
671                   wr: &mut io::Writer,
672                   ln: LiveNode,
673                   test: |uint| -> LiveNode) -> io::IoResult<()> {
674         let node_base_idx = self.idx(ln, Variable(0));
675         for var_idx in range(0u, self.ir.num_vars) {
676             let idx = node_base_idx + var_idx;
677             if test(idx).is_valid() {
678                 try!(write!(wr, " {}", Variable(var_idx).to_str()));
679             }
680         }
681         Ok(())
682     }
683
684     fn find_loop_scope(&self,
685                        opt_label: Option<Ident>,
686                        id: NodeId,
687                        sp: Span)
688                        -> NodeId {
689         match opt_label {
690             Some(_) => {
691                 // Refers to a labeled loop. Use the results of resolve
692                 // to find with one
693                 match self.ir.tcx.def_map.borrow().find(&id) {
694                     Some(&DefLabel(loop_id)) => loop_id,
695                     _ => self.ir.tcx.sess.span_bug(sp, "label on break/loop \
696                                                         doesn't refer to a loop")
697                 }
698             }
699             None => {
700                 // Vanilla 'break' or 'loop', so use the enclosing
701                 // loop scope
702                 if self.loop_scope.len() == 0 {
703                     self.ir.tcx.sess.span_bug(sp, "break outside loop");
704                 } else {
705                     *self.loop_scope.last().unwrap()
706                 }
707             }
708         }
709     }
710
711     #[allow(unused_must_use)]
712     fn ln_str(&self, ln: LiveNode) -> String {
713         let mut wr = io::MemWriter::new();
714         {
715             let wr = &mut wr as &mut io::Writer;
716             write!(wr, "[ln({}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
717             self.write_vars(wr, ln, |idx| self.users.get(idx).reader);
718             write!(wr, "  writes");
719             self.write_vars(wr, ln, |idx| self.users.get(idx).writer);
720             write!(wr, "  precedes {}]", self.successors.get(ln.get()).to_str());
721         }
722         str::from_utf8(wr.unwrap().as_slice()).unwrap().to_string()
723     }
724
725     fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
726         *self.successors.get_mut(ln.get()) = succ_ln;
727
728         // It is not necessary to initialize the
729         // values to empty because this is the value
730         // they have when they are created, and the sets
731         // only grow during iterations.
732         //
733         // self.indices(ln) { |idx|
734         //     self.users[idx] = invalid_users();
735         // }
736     }
737
738     fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
739         // more efficient version of init_empty() / merge_from_succ()
740         *self.successors.get_mut(ln.get()) = succ_ln;
741
742         self.indices2(ln, succ_ln, |this, idx, succ_idx| {
743             *this.users.get_mut(idx) = *this.users.get(succ_idx)
744         });
745         debug!("init_from_succ(ln={}, succ={})",
746                self.ln_str(ln), self.ln_str(succ_ln));
747     }
748
749     fn merge_from_succ(&mut self,
750                        ln: LiveNode,
751                        succ_ln: LiveNode,
752                        first_merge: bool)
753                        -> bool {
754         if ln == succ_ln { return false; }
755
756         let mut changed = false;
757         self.indices2(ln, succ_ln, |this, idx, succ_idx| {
758             changed |= copy_if_invalid(this.users.get(succ_idx).reader,
759                                        &mut this.users.get_mut(idx).reader);
760             changed |= copy_if_invalid(this.users.get(succ_idx).writer,
761                                        &mut this.users.get_mut(idx).writer);
762             if this.users.get(succ_idx).used && !this.users.get(idx).used {
763                 this.users.get_mut(idx).used = true;
764                 changed = true;
765             }
766         });
767
768         debug!("merge_from_succ(ln={}, succ={}, first_merge={}, changed={})",
769                ln.to_str(), self.ln_str(succ_ln), first_merge, changed);
770         return changed;
771
772         fn copy_if_invalid(src: LiveNode, dst: &mut LiveNode) -> bool {
773             if src.is_valid() && !dst.is_valid() {
774                 *dst = src;
775                 true
776             } else {
777                 false
778             }
779         }
780     }
781
782     // Indicates that a local variable was *defined*; we know that no
783     // uses of the variable can precede the definition (resolve checks
784     // this) so we just clear out all the data.
785     fn define(&mut self, writer: LiveNode, var: Variable) {
786         let idx = self.idx(writer, var);
787         self.users.get_mut(idx).reader = invalid_node();
788         self.users.get_mut(idx).writer = invalid_node();
789
790         debug!("{} defines {} (idx={}): {}", writer.to_str(), var.to_str(),
791                idx, self.ln_str(writer));
792     }
793
794     // Either read, write, or both depending on the acc bitset
795     fn acc(&mut self, ln: LiveNode, var: Variable, acc: uint) {
796         debug!("{} accesses[{:x}] {}: {}",
797                ln.to_str(), acc, var.to_str(), self.ln_str(ln));
798
799         let idx = self.idx(ln, var);
800         let user = self.users.get_mut(idx);
801
802         if (acc & ACC_WRITE) != 0 {
803             user.reader = invalid_node();
804             user.writer = ln;
805         }
806
807         // Important: if we both read/write, must do read second
808         // or else the write will override.
809         if (acc & ACC_READ) != 0 {
810             user.reader = ln;
811         }
812
813         if (acc & ACC_USE) != 0 {
814             user.used = true;
815         }
816     }
817
818     // _______________________________________________________________________
819
820     fn compute(&mut self, decl: &FnDecl, body: &Block) -> LiveNode {
821         // if there is a `break` or `again` at the top level, then it's
822         // effectively a return---this only occurs in `for` loops,
823         // where the body is really a closure.
824
825         debug!("compute: using id for block, {}", block_to_str(body));
826
827         let exit_ln = self.s.exit_ln;
828         let entry_ln: LiveNode =
829             self.with_loop_nodes(body.id, exit_ln, exit_ln,
830               |this| this.propagate_through_fn_block(decl, body));
831
832         // hack to skip the loop unless debug! is enabled:
833         debug!("^^ liveness computation results for body {} (entry={})",
834                {
835                    for ln_idx in range(0u, self.ir.num_live_nodes) {
836                        debug!("{}", self.ln_str(LiveNode(ln_idx)));
837                    }
838                    body.id
839                },
840                entry_ln.to_str());
841
842         entry_ln
843     }
844
845     fn propagate_through_fn_block(&mut self, _: &FnDecl, blk: &Block)
846                                   -> LiveNode {
847         // the fallthrough exit is only for those cases where we do not
848         // explicitly return:
849         let s = self.s;
850         self.init_from_succ(s.fallthrough_ln, s.exit_ln);
851         if blk.expr.is_none() {
852             self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ)
853         }
854
855         self.propagate_through_block(blk, s.fallthrough_ln)
856     }
857
858     fn propagate_through_block(&mut self, blk: &Block, succ: LiveNode)
859                                -> LiveNode {
860         let succ = self.propagate_through_opt_expr(blk.expr, succ);
861         blk.stmts.iter().rev().fold(succ, |succ, stmt| {
862             self.propagate_through_stmt(&**stmt, succ)
863         })
864     }
865
866     fn propagate_through_stmt(&mut self, stmt: &Stmt, succ: LiveNode)
867                               -> LiveNode {
868         match stmt.node {
869             StmtDecl(ref decl, _) => {
870                 self.propagate_through_decl(&**decl, succ)
871             }
872
873             StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => {
874                 self.propagate_through_expr(&**expr, succ)
875             }
876
877             StmtMac(..) => {
878                 self.ir.tcx.sess.span_bug(stmt.span, "unexpanded macro");
879             }
880         }
881     }
882
883     fn propagate_through_decl(&mut self, decl: &Decl, succ: LiveNode)
884                               -> LiveNode {
885         match decl.node {
886             DeclLocal(ref local) => {
887                 self.propagate_through_local(&**local, succ)
888             }
889             DeclItem(_) => succ,
890         }
891     }
892
893     fn propagate_through_local(&mut self, local: &Local, succ: LiveNode)
894                                -> LiveNode {
895         // Note: we mark the variable as defined regardless of whether
896         // there is an initializer.  Initially I had thought to only mark
897         // the live variable as defined if it was initialized, and then we
898         // could check for uninit variables just by scanning what is live
899         // at the start of the function. But that doesn't work so well for
900         // immutable variables defined in a loop:
901         //     loop { let x; x = 5; }
902         // because the "assignment" loops back around and generates an error.
903         //
904         // So now we just check that variables defined w/o an
905         // initializer are not live at the point of their
906         // initialization, which is mildly more complex than checking
907         // once at the func header but otherwise equivalent.
908
909         let succ = self.propagate_through_opt_expr(local.init, succ);
910         self.define_bindings_in_pat(local.pat, succ)
911     }
912
913     fn propagate_through_exprs(&mut self, exprs: &[Gc<Expr>], succ: LiveNode)
914                                -> LiveNode {
915         exprs.iter().rev().fold(succ, |succ, expr| {
916             self.propagate_through_expr(&**expr, succ)
917         })
918     }
919
920     fn propagate_through_opt_expr(&mut self,
921                                   opt_expr: Option<Gc<Expr>>,
922                                   succ: LiveNode)
923                                   -> LiveNode {
924         opt_expr.iter().fold(succ, |succ, expr| {
925             self.propagate_through_expr(&**expr, succ)
926         })
927     }
928
929     fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
930                               -> LiveNode {
931         debug!("propagate_through_expr: {}", expr_to_str(expr));
932
933         match expr.node {
934           // Interesting cases with control flow or which gen/kill
935
936           ExprPath(_) => {
937               self.access_path(expr, succ, ACC_READ | ACC_USE)
938           }
939
940           ExprField(ref e, _, _) => {
941               self.propagate_through_expr(&**e, succ)
942           }
943
944           ExprFnBlock(_, ref blk) | ExprProc(_, ref blk) => {
945               debug!("{} is an ExprFnBlock or ExprProc", expr_to_str(expr));
946
947               /*
948               The next-node for a break is the successor of the entire
949               loop. The next-node for a continue is the top of this loop.
950               */
951               let node = self.live_node(expr.id, expr.span);
952               self.with_loop_nodes(blk.id, succ, node, |this| {
953
954                  // the construction of a closure itself is not important,
955                  // but we have to consider the closed over variables.
956                  let caps = match this.ir.capture_info_map.find(&expr.id) {
957                     Some(caps) => caps.clone(),
958                     None => {
959                         this.ir.tcx.sess.span_bug(expr.span, "no registered caps");
960                      }
961                  };
962                  caps.iter().rev().fold(succ, |succ, cap| {
963                      this.init_from_succ(cap.ln, succ);
964                      let var = this.variable(cap.var_nid, expr.span);
965                      this.acc(cap.ln, var, ACC_READ | ACC_USE);
966                      cap.ln
967                  })
968               })
969           }
970
971           ExprIf(ref cond, ref then, ref els) => {
972             //
973             //     (cond)
974             //       |
975             //       v
976             //     (expr)
977             //     /   \
978             //    |     |
979             //    v     v
980             //  (then)(els)
981             //    |     |
982             //    v     v
983             //   (  succ  )
984             //
985             let else_ln = self.propagate_through_opt_expr(els.clone(), succ);
986             let then_ln = self.propagate_through_block(&**then, succ);
987             let ln = self.live_node(expr.id, expr.span);
988             self.init_from_succ(ln, else_ln);
989             self.merge_from_succ(ln, then_ln, false);
990             self.propagate_through_expr(&**cond, ln)
991           }
992
993           ExprWhile(ref cond, ref blk) => {
994             self.propagate_through_loop(expr, Some(cond.clone()), &**blk, succ)
995           }
996
997           ExprForLoop(..) => fail!("non-desugared expr_for_loop"),
998
999           // Note that labels have been resolved, so we don't need to look
1000           // at the label ident
1001           ExprLoop(ref blk, _) => {
1002             self.propagate_through_loop(expr, None, &**blk, succ)
1003           }
1004
1005           ExprMatch(ref e, ref arms) => {
1006             //
1007             //      (e)
1008             //       |
1009             //       v
1010             //     (expr)
1011             //     / | \
1012             //    |  |  |
1013             //    v  v  v
1014             //   (..arms..)
1015             //    |  |  |
1016             //    v  v  v
1017             //   (  succ  )
1018             //
1019             //
1020             let ln = self.live_node(expr.id, expr.span);
1021             self.init_empty(ln, succ);
1022             let mut first_merge = true;
1023             for arm in arms.iter() {
1024                 let body_succ =
1025                     self.propagate_through_expr(&*arm.body, succ);
1026                 let guard_succ =
1027                     self.propagate_through_opt_expr(arm.guard, body_succ);
1028                 let arm_succ =
1029                     self.define_bindings_in_arm_pats(arm.pats.as_slice(),
1030                                                      guard_succ);
1031                 self.merge_from_succ(ln, arm_succ, first_merge);
1032                 first_merge = false;
1033             };
1034             self.propagate_through_expr(&**e, ln)
1035           }
1036
1037           ExprRet(o_e) => {
1038             // ignore succ and subst exit_ln:
1039             let exit_ln = self.s.exit_ln;
1040             self.propagate_through_opt_expr(o_e, exit_ln)
1041           }
1042
1043           ExprBreak(opt_label) => {
1044               // Find which label this break jumps to
1045               let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
1046
1047               // Now that we know the label we're going to,
1048               // look it up in the break loop nodes table
1049
1050               match self.break_ln.find(&sc) {
1051                   Some(&b) => b,
1052                   None => self.ir.tcx.sess.span_bug(expr.span,
1053                                                     "break to unknown label")
1054               }
1055           }
1056
1057           ExprAgain(opt_label) => {
1058               // Find which label this expr continues to
1059               let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
1060
1061               // Now that we know the label we're going to,
1062               // look it up in the continue loop nodes table
1063
1064               match self.cont_ln.find(&sc) {
1065                   Some(&b) => b,
1066                   None => self.ir.tcx.sess.span_bug(expr.span,
1067                                                     "loop to unknown label")
1068               }
1069           }
1070
1071           ExprAssign(ref l, ref r) => {
1072             // see comment on lvalues in
1073             // propagate_through_lvalue_components()
1074             let succ = self.write_lvalue(&**l, succ, ACC_WRITE);
1075             let succ = self.propagate_through_lvalue_components(&**l, succ);
1076             self.propagate_through_expr(&**r, succ)
1077           }
1078
1079           ExprAssignOp(_, ref l, ref r) => {
1080             // see comment on lvalues in
1081             // propagate_through_lvalue_components()
1082             let succ = self.write_lvalue(&**l, succ, ACC_WRITE|ACC_READ);
1083             let succ = self.propagate_through_expr(&**r, succ);
1084             self.propagate_through_lvalue_components(&**l, succ)
1085           }
1086
1087           // Uninteresting cases: just propagate in rev exec order
1088
1089           ExprVstore(ref expr, _) => {
1090             self.propagate_through_expr(&**expr, succ)
1091           }
1092
1093           ExprVec(ref exprs) => {
1094             self.propagate_through_exprs(exprs.as_slice(), succ)
1095           }
1096
1097           ExprRepeat(ref element, ref count) => {
1098             let succ = self.propagate_through_expr(&**count, succ);
1099             self.propagate_through_expr(&**element, succ)
1100           }
1101
1102           ExprStruct(_, ref fields, ref with_expr) => {
1103             let succ = self.propagate_through_opt_expr(with_expr.clone(), succ);
1104             fields.iter().rev().fold(succ, |succ, field| {
1105                 self.propagate_through_expr(&*field.expr, succ)
1106             })
1107           }
1108
1109           ExprCall(ref f, ref args) => {
1110             // calling a fn with bot return type means that the fn
1111             // will fail, and hence the successors can be ignored
1112             let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
1113                 let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
1114                 ty::type_is_bot(t_ret)
1115             };
1116             let succ = if is_bot {
1117                 self.s.exit_ln
1118             } else {
1119                 succ
1120             };
1121             let succ = self.propagate_through_exprs(args.as_slice(), succ);
1122             self.propagate_through_expr(&**f, succ)
1123           }
1124
1125           ExprMethodCall(_, _, ref args) => {
1126             // calling a method with bot return type means that the method
1127             // will fail, and hence the successors can be ignored
1128             let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id);
1129             let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
1130                        else {succ};
1131             self.propagate_through_exprs(args.as_slice(), succ)
1132           }
1133
1134           ExprTup(ref exprs) => {
1135             self.propagate_through_exprs(exprs.as_slice(), succ)
1136           }
1137
1138           ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
1139             let r_succ = self.propagate_through_expr(&**r, succ);
1140
1141             let ln = self.live_node(expr.id, expr.span);
1142             self.init_from_succ(ln, succ);
1143             self.merge_from_succ(ln, r_succ, false);
1144
1145             self.propagate_through_expr(&**l, ln)
1146           }
1147
1148           ExprIndex(ref l, ref r) |
1149           ExprBinary(_, ref l, ref r) |
1150           ExprBox(ref l, ref r) => {
1151             self.propagate_through_exprs([l.clone(), r.clone()], succ)
1152           }
1153
1154           ExprAddrOf(_, ref e) |
1155           ExprCast(ref e, _) |
1156           ExprUnary(_, ref e) |
1157           ExprParen(ref e) => {
1158             self.propagate_through_expr(&**e, succ)
1159           }
1160
1161           ExprInlineAsm(ref ia) => {
1162             let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
1163                 // see comment on lvalues in
1164                 // propagate_through_lvalue_components()
1165                 let succ = self.write_lvalue(&**expr, succ, ACC_WRITE);
1166                 self.propagate_through_lvalue_components(&**expr, succ)
1167             });
1168             // Inputs are executed first. Propagate last because of rev order
1169             ia.inputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
1170                 self.propagate_through_expr(&**expr, succ)
1171             })
1172           }
1173
1174           ExprLit(..) => {
1175             succ
1176           }
1177
1178           ExprBlock(ref blk) => {
1179             self.propagate_through_block(&**blk, succ)
1180           }
1181
1182           ExprMac(..) => {
1183             self.ir.tcx.sess.span_bug(expr.span, "unexpanded macro");
1184           }
1185         }
1186     }
1187
1188     fn propagate_through_lvalue_components(&mut self,
1189                                            expr: &Expr,
1190                                            succ: LiveNode)
1191                                            -> LiveNode {
1192         // # Lvalues
1193         //
1194         // In general, the full flow graph structure for an
1195         // assignment/move/etc can be handled in one of two ways,
1196         // depending on whether what is being assigned is a "tracked
1197         // value" or not. A tracked value is basically a local
1198         // variable or argument.
1199         //
1200         // The two kinds of graphs are:
1201         //
1202         //    Tracked lvalue          Untracked lvalue
1203         // ----------------------++-----------------------
1204         //                       ||
1205         //         |             ||           |
1206         //         v             ||           v
1207         //     (rvalue)          ||       (rvalue)
1208         //         |             ||           |
1209         //         v             ||           v
1210         // (write of lvalue)     ||   (lvalue components)
1211         //         |             ||           |
1212         //         v             ||           v
1213         //      (succ)           ||        (succ)
1214         //                       ||
1215         // ----------------------++-----------------------
1216         //
1217         // I will cover the two cases in turn:
1218         //
1219         // # Tracked lvalues
1220         //
1221         // A tracked lvalue is a local variable/argument `x`.  In
1222         // these cases, the link_node where the write occurs is linked
1223         // to node id of `x`.  The `write_lvalue()` routine generates
1224         // the contents of this node.  There are no subcomponents to
1225         // consider.
1226         //
1227         // # Non-tracked lvalues
1228         //
1229         // These are lvalues like `x[5]` or `x.f`.  In that case, we
1230         // basically ignore the value which is written to but generate
1231         // reads for the components---`x` in these two examples.  The
1232         // components reads are generated by
1233         // `propagate_through_lvalue_components()` (this fn).
1234         //
1235         // # Illegal lvalues
1236         //
1237         // It is still possible to observe assignments to non-lvalues;
1238         // these errors are detected in the later pass borrowck.  We
1239         // just ignore such cases and treat them as reads.
1240
1241         match expr.node {
1242             ExprPath(_) => succ,
1243             ExprField(ref e, _, _) => self.propagate_through_expr(&**e, succ),
1244             _ => self.propagate_through_expr(expr, succ)
1245         }
1246     }
1247
1248     // see comment on propagate_through_lvalue()
1249     fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
1250                     -> LiveNode {
1251         match expr.node {
1252           ExprPath(_) => self.access_path(expr, succ, acc),
1253
1254           // We do not track other lvalues, so just propagate through
1255           // to their subcomponents.  Also, it may happen that
1256           // non-lvalues occur here, because those are detected in the
1257           // later pass borrowck.
1258           _ => succ
1259         }
1260     }
1261
1262     fn access_path(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
1263                    -> LiveNode {
1264         let def = self.ir.tcx.def_map.borrow().get_copy(&expr.id);
1265         match moved_variable_node_id_from_def(def) {
1266           Some(nid) => {
1267             let ln = self.live_node(expr.id, expr.span);
1268             if acc != 0u {
1269                 self.init_from_succ(ln, succ);
1270                 let var = self.variable(nid, expr.span);
1271                 self.acc(ln, var, acc);
1272             }
1273             ln
1274           }
1275           None => succ
1276         }
1277     }
1278
1279     fn propagate_through_loop(&mut self,
1280                               expr: &Expr,
1281                               cond: Option<Gc<Expr>>,
1282                               body: &Block,
1283                               succ: LiveNode)
1284                               -> LiveNode {
1285
1286         /*
1287
1288         We model control flow like this:
1289
1290               (cond) <--+
1291                 |       |
1292                 v       |
1293           +-- (expr)    |
1294           |     |       |
1295           |     v       |
1296           |   (body) ---+
1297           |
1298           |
1299           v
1300         (succ)
1301
1302         */
1303
1304
1305         // first iteration:
1306         let mut first_merge = true;
1307         let ln = self.live_node(expr.id, expr.span);
1308         self.init_empty(ln, succ);
1309         if cond.is_some() {
1310             // if there is a condition, then it's possible we bypass
1311             // the body altogether.  otherwise, the only way is via a
1312             // break in the loop body.
1313             self.merge_from_succ(ln, succ, first_merge);
1314             first_merge = false;
1315         }
1316         debug!("propagate_through_loop: using id for loop body {} {}",
1317                expr.id, block_to_str(body));
1318
1319         let cond_ln = self.propagate_through_opt_expr(cond, ln);
1320         let body_ln = self.with_loop_nodes(expr.id, succ, ln, |this| {
1321             this.propagate_through_block(body, cond_ln)
1322         });
1323
1324         // repeat until fixed point is reached:
1325         while self.merge_from_succ(ln, body_ln, first_merge) {
1326             first_merge = false;
1327             assert!(cond_ln == self.propagate_through_opt_expr(cond,
1328                                                                     ln));
1329             assert!(body_ln == self.with_loop_nodes(expr.id, succ, ln,
1330             |this| this.propagate_through_block(body, cond_ln)));
1331         }
1332
1333         cond_ln
1334     }
1335
1336     fn with_loop_nodes<R>(&mut self,
1337                           loop_node_id: NodeId,
1338                           break_ln: LiveNode,
1339                           cont_ln: LiveNode,
1340                           f: |&mut Liveness<'a>| -> R)
1341                           -> R {
1342         debug!("with_loop_nodes: {} {}", loop_node_id, break_ln.get());
1343         self.loop_scope.push(loop_node_id);
1344         self.break_ln.insert(loop_node_id, break_ln);
1345         self.cont_ln.insert(loop_node_id, cont_ln);
1346         let r = f(self);
1347         self.loop_scope.pop();
1348         r
1349     }
1350 }
1351
1352 // _______________________________________________________________________
1353 // Checking for error conditions
1354
1355 fn check_local(this: &mut Liveness, local: &Local) {
1356     match local.init {
1357         Some(_) => {
1358             this.warn_about_unused_or_dead_vars_in_pat(&*local.pat);
1359         },
1360         None => {
1361             this.pat_bindings(&*local.pat, |this, ln, var, sp, id| {
1362                 this.warn_about_unused(sp, id, ln, var);
1363             })
1364         }
1365     }
1366
1367     visit::walk_local(this, local, ());
1368 }
1369
1370 fn check_arm(this: &mut Liveness, arm: &Arm) {
1371     this.arm_pats_bindings(arm.pats.as_slice(), |this, ln, var, sp, id| {
1372         this.warn_about_unused(sp, id, ln, var);
1373     });
1374     visit::walk_arm(this, arm, ());
1375 }
1376
1377 fn check_expr(this: &mut Liveness, expr: &Expr) {
1378     match expr.node {
1379       ExprAssign(ref l, ref r) => {
1380         this.check_lvalue(&**l);
1381         this.visit_expr(&**r, ());
1382
1383         visit::walk_expr(this, expr, ());
1384       }
1385
1386       ExprAssignOp(_, ref l, _) => {
1387         this.check_lvalue(&**l);
1388
1389         visit::walk_expr(this, expr, ());
1390       }
1391
1392       ExprInlineAsm(ref ia) => {
1393         for &(_, ref input) in ia.inputs.iter() {
1394           this.visit_expr(&**input, ());
1395         }
1396
1397         // Output operands must be lvalues
1398         for &(_, ref out) in ia.outputs.iter() {
1399           this.check_lvalue(&**out);
1400           this.visit_expr(&**out, ());
1401         }
1402
1403         visit::walk_expr(this, expr, ());
1404       }
1405
1406       // no correctness conditions related to liveness
1407       ExprCall(..) | ExprMethodCall(..) | ExprIf(..) | ExprMatch(..) |
1408       ExprWhile(..) | ExprLoop(..) | ExprIndex(..) | ExprField(..) |
1409       ExprVstore(..) | ExprVec(..) | ExprTup(..) |
1410       ExprBinary(..) |
1411       ExprCast(..) | ExprUnary(..) | ExprRet(..) | ExprBreak(..) |
1412       ExprAgain(..) | ExprLit(_) | ExprBlock(..) |
1413       ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) |
1414       ExprParen(..) | ExprFnBlock(..) | ExprProc(..) | ExprPath(..) |
1415       ExprBox(..) => {
1416         visit::walk_expr(this, expr, ());
1417       }
1418       ExprForLoop(..) => fail!("non-desugared expr_for_loop")
1419     }
1420 }
1421
1422 fn check_fn(_v: &Liveness,
1423             _fk: &FnKind,
1424             _decl: &FnDecl,
1425             _body: &Block,
1426             _sp: Span,
1427             _id: NodeId) {
1428     // do not check contents of nested fns
1429 }
1430
1431 impl<'a> Liveness<'a> {
1432     fn check_ret(&self,
1433                  id: NodeId,
1434                  sp: Span,
1435                  _fk: &FnKind,
1436                  entry_ln: LiveNode,
1437                  body: &Block) {
1438         if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
1439             // if no_ret_var is live, then we fall off the end of the
1440             // function without any kind of return expression:
1441
1442             let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
1443             if ty::type_is_nil(t_ret) {
1444                 // for nil return types, it is ok to not return a value expl.
1445             } else if ty::type_is_bot(t_ret) {
1446                 // for bot return types, not ok.  Function should fail.
1447                 self.ir.tcx.sess.span_err(
1448                     sp, "some control paths may return");
1449             } else {
1450                 let ends_with_stmt = match body.expr {
1451                     None if body.stmts.len() > 0 =>
1452                         match body.stmts.last().unwrap().node {
1453                             StmtSemi(ref e, _) => {
1454                                 let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
1455                                 ty::get(t_stmt).sty == ty::get(t_ret).sty
1456                             },
1457                             _ => false
1458                         },
1459                     _ => false
1460                 };
1461                 if ends_with_stmt {
1462                     let last_stmt = body.stmts.last().unwrap();
1463                     let original_span = original_sp(last_stmt.span, sp);
1464                     let span_semicolon = Span {
1465                         lo: original_span.hi - BytePos(1),
1466                         hi: original_span.hi,
1467                         expn_info: original_span.expn_info
1468                     };
1469                     self.ir.tcx.sess.span_note(
1470                         span_semicolon, "consider removing this semicolon:");
1471                 }
1472                 self.ir.tcx.sess.span_err(
1473                     sp, "not all control paths return a value");
1474            }
1475         }
1476     }
1477
1478     fn check_lvalue(&mut self, expr: &Expr) {
1479         match expr.node {
1480           ExprPath(_) => {
1481             match self.ir.tcx.def_map.borrow().get_copy(&expr.id) {
1482               DefLocal(nid, _) => {
1483                 // Assignment to an immutable variable or argument: only legal
1484                 // if there is no later assignment. If this local is actually
1485                 // mutable, then check for a reassignment to flag the mutability
1486                 // as being used.
1487                 let ln = self.live_node(expr.id, expr.span);
1488                 let var = self.variable(nid, expr.span);
1489                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
1490               }
1491               def => {
1492                 match moved_variable_node_id_from_def(def) {
1493                   Some(nid) => {
1494                     let ln = self.live_node(expr.id, expr.span);
1495                     let var = self.variable(nid, expr.span);
1496                     self.warn_about_dead_assign(expr.span, expr.id, ln, var);
1497                   }
1498                   None => {}
1499                 }
1500               }
1501             }
1502           }
1503
1504           _ => {
1505             // For other kinds of lvalues, no checks are required,
1506             // and any embedded expressions are actually rvalues
1507             visit::walk_expr(self, expr, ());
1508           }
1509        }
1510     }
1511
1512     fn should_warn(&self, var: Variable) -> Option<String> {
1513         let name = self.ir.variable_name(var);
1514         if name.len() == 0 || name.as_slice()[0] == ('_' as u8) {
1515             None
1516         } else {
1517             Some(name)
1518         }
1519     }
1520
1521     fn warn_about_unused_args(&self, decl: &FnDecl, entry_ln: LiveNode) {
1522         for arg in decl.inputs.iter() {
1523             pat_util::pat_bindings(&self.ir.tcx.def_map,
1524                                    &*arg.pat,
1525                                    |_bm, p_id, sp, path| {
1526                 let var = self.variable(p_id, sp);
1527                 // Ignore unused self.
1528                 let ident = ast_util::path_to_ident(path);
1529                 if ident.name != special_idents::self_.name {
1530                     self.warn_about_unused(sp, p_id, entry_ln, var);
1531                 }
1532             })
1533         }
1534     }
1535
1536     fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &Pat) {
1537         self.pat_bindings(pat, |this, ln, var, sp, id| {
1538             if !this.warn_about_unused(sp, id, ln, var) {
1539                 this.warn_about_dead_assign(sp, id, ln, var);
1540             }
1541         })
1542     }
1543
1544     fn warn_about_unused(&self,
1545                          sp: Span,
1546                          id: NodeId,
1547                          ln: LiveNode,
1548                          var: Variable)
1549                          -> bool {
1550         if !self.used_on_entry(ln, var) {
1551             let r = self.should_warn(var);
1552             for name in r.iter() {
1553
1554                 // annoying: for parameters in funcs like `fn(x: int)
1555                 // {ret}`, there is only one node, so asking about
1556                 // assigned_on_exit() is not meaningful.
1557                 let is_assigned = if ln == self.s.exit_ln {
1558                     false
1559                 } else {
1560                     self.assigned_on_exit(ln, var).is_some()
1561                 };
1562
1563                 if is_assigned {
1564                     self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
1565                         format!("variable `{}` is assigned to, but never used",
1566                                 *name));
1567                 } else {
1568                     self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
1569                         format!("unused variable: `{}`", *name));
1570                 }
1571             }
1572             true
1573         } else {
1574             false
1575         }
1576     }
1577
1578     fn warn_about_dead_assign(&self,
1579                               sp: Span,
1580                               id: NodeId,
1581                               ln: LiveNode,
1582                               var: Variable) {
1583         if self.live_on_exit(ln, var).is_none() {
1584             let r = self.should_warn(var);
1585             for name in r.iter() {
1586                 self.ir.tcx.sess.add_lint(lint::builtin::DEAD_ASSIGNMENT, id, sp,
1587                     format!("value assigned to `{}` is never read", *name));
1588             }
1589         }
1590     }
1591  }