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