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