2014-01-25 01:37:51 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 18:48:01 -06:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2012-07-04 16:53:12 -05:00
|
|
|
/*!
|
|
|
|
* A classic liveness analysis based on dataflow over the AST. Computes,
|
|
|
|
* for each local variable in a function, whether that variable is live
|
|
|
|
* at a given point. Program execution points are identified by their
|
|
|
|
* id.
|
|
|
|
*
|
|
|
|
* # Basic idea
|
|
|
|
*
|
|
|
|
* The basic model is that each local variable is assigned an index. We
|
|
|
|
* represent sets of local variables using a vector indexed by this
|
|
|
|
* index. The value in the vector is either 0, indicating the variable
|
|
|
|
* is dead, or the id of an expression that uses the variable.
|
|
|
|
*
|
|
|
|
* We conceptually walk over the AST in reverse execution order. If we
|
|
|
|
* find a use of a variable, we add it to the set of live variables. If
|
|
|
|
* we find an assignment to a variable, we remove it from the set of live
|
|
|
|
* variables. When we have to merge two flows, we take the union of
|
|
|
|
* those two flows---if the variable is live on both paths, we simply
|
|
|
|
* pick one id. In the event of loops, we continue doing this until a
|
|
|
|
* fixed point is reached.
|
|
|
|
*
|
|
|
|
* ## Checking initialization
|
|
|
|
*
|
|
|
|
* At the function entry point, all variables must be dead. If this is
|
|
|
|
* not the case, we can report an error using the id found in the set of
|
|
|
|
* live variables, which identifies a use of the variable which is not
|
|
|
|
* dominated by an assignment.
|
|
|
|
*
|
|
|
|
* ## Checking moves
|
|
|
|
*
|
|
|
|
* After each explicit move, the variable must be dead.
|
|
|
|
*
|
|
|
|
* ## Computing last uses
|
|
|
|
*
|
|
|
|
* Any use of the variable where the variable is dead afterwards is a
|
|
|
|
* last use.
|
|
|
|
*
|
|
|
|
* # Implementation details
|
|
|
|
*
|
|
|
|
* The actual implementation contains two (nested) walks over the AST.
|
|
|
|
* The outer walk has the job of building up the ir_maps instance for the
|
|
|
|
* enclosing function. On the way down the tree, it identifies those AST
|
|
|
|
* nodes and variable IDs that will be needed for the liveness analysis
|
|
|
|
* and assigns them contiguous IDs. The liveness id for an AST node is
|
|
|
|
* called a `live_node` (it's a newtype'd uint) and the id for a variable
|
|
|
|
* is called a `variable` (another newtype'd uint).
|
|
|
|
*
|
|
|
|
* On the way back up the tree, as we are about to exit from a function
|
|
|
|
* declaration we allocate a `liveness` instance. Now that we know
|
|
|
|
* precisely how many nodes and variables we need, we can allocate all
|
|
|
|
* the various arrays that we will need to precisely the right size. We then
|
|
|
|
* perform the actual propagation on the `liveness` instance.
|
|
|
|
*
|
|
|
|
* This propagation is encoded in the various `propagate_through_*()`
|
|
|
|
* methods. It effectively does a reverse walk of the AST; whenever we
|
|
|
|
* reach a loop node, we iterate until a fixed point is reached.
|
|
|
|
*
|
2013-02-04 16:02:01 -06:00
|
|
|
* ## The `Users` struct
|
2012-07-04 16:53:12 -05:00
|
|
|
*
|
|
|
|
* At each live node `N`, we track three pieces of information for each
|
2013-02-04 16:02:01 -06:00
|
|
|
* variable `V` (these are encapsulated in the `Users` struct):
|
2012-07-04 16:53:12 -05:00
|
|
|
*
|
2012-08-23 11:14:19 -05:00
|
|
|
* - `reader`: the `LiveNode` ID of some node which will read the value
|
2012-07-04 16:53:12 -05:00
|
|
|
* that `V` holds on entry to `N`. Formally: a node `M` such
|
|
|
|
* that there exists a path `P` from `N` to `M` where `P` does not
|
|
|
|
* write `V`. If the `reader` is `invalid_node()`, then the current
|
|
|
|
* value will never be read (the variable is dead, essentially).
|
|
|
|
*
|
2012-08-23 11:14:19 -05:00
|
|
|
* - `writer`: the `LiveNode` ID of some node which will write the
|
2012-07-04 16:53:12 -05:00
|
|
|
* variable `V` and which is reachable from `N`. Formally: a node `M`
|
|
|
|
* such that there exists a path `P` from `N` to `M` and `M` writes
|
|
|
|
* `V`. If the `writer` is `invalid_node()`, then there is no writer
|
|
|
|
* of `V` that follows `N`.
|
|
|
|
*
|
|
|
|
* - `used`: a boolean value indicating whether `V` is *used*. We
|
|
|
|
* distinguish a *read* from a *use* in that a *use* is some read that
|
|
|
|
* is not just used to generate a new value. For example, `x += 1` is
|
|
|
|
* a read but not a use. This is used to generate better warnings.
|
|
|
|
*
|
|
|
|
* ## Special Variables
|
|
|
|
*
|
|
|
|
* We generate various special variables for various, well, special purposes.
|
|
|
|
* These are described in the `specials` struct:
|
|
|
|
*
|
|
|
|
* - `exit_ln`: a live node that is generated to represent every 'exit' from
|
|
|
|
* the function, whether it be by explicit return, fail, or other means.
|
|
|
|
*
|
|
|
|
* - `fallthrough_ln`: a live node that represents a fallthrough
|
|
|
|
*
|
|
|
|
* - `no_ret_var`: a synthetic variable that is only 'read' from, the
|
|
|
|
* fallthrough node. This allows us to detect functions where we fail
|
|
|
|
* to return explicitly.
|
|
|
|
*/
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def::*;
|
2014-04-21 18:21:53 -05:00
|
|
|
use middle::freevars;
|
2014-06-01 18:35:01 -05:00
|
|
|
use middle::mem_categorization::Typer;
|
2012-12-23 16:41:37 -06:00
|
|
|
use middle::pat_util;
|
|
|
|
use middle::ty;
|
2014-06-04 16:35:58 -05:00
|
|
|
use lint;
|
2014-02-28 16:34:26 -06:00
|
|
|
use util::nodemap::NodeMap;
|
2012-12-13 15:05:22 -06:00
|
|
|
|
2014-02-19 20:56:33 -06:00
|
|
|
use std::fmt;
|
2014-05-16 12:15:33 -05:00
|
|
|
use std::gc::Gc;
|
2013-11-11 00:46:32 -06:00
|
|
|
use std::io;
|
2014-05-16 12:15:33 -05:00
|
|
|
use std::mem::transmute;
|
2014-03-06 07:46:26 -06:00
|
|
|
use std::rc::Rc;
|
2013-10-13 20:48:47 -05:00
|
|
|
use std::str;
|
2013-06-28 17:32:26 -05:00
|
|
|
use std::uint;
|
2012-09-04 13:54:36 -05:00
|
|
|
use syntax::ast::*;
|
2014-04-09 15:42:25 -05:00
|
|
|
use syntax::codemap::{BytePos, original_sp, Span};
|
2012-12-10 13:58:37 -06:00
|
|
|
use syntax::parse::token::special_idents;
|
2014-01-31 14:33:42 -06:00
|
|
|
use syntax::parse::token;
|
2014-06-21 05:39:03 -05:00
|
|
|
use syntax::print::pprust::{expr_to_string, block_to_string};
|
2013-08-14 09:29:40 -05:00
|
|
|
use syntax::{visit, ast_util};
|
2014-01-09 07:05:33 -06:00
|
|
|
use syntax::visit::{Visitor, FnKind};
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-07-21 22:54:28 -05:00
|
|
|
/// For use with `propagate_through_loop`.
|
|
|
|
#[deriving(PartialEq, Eq)]
|
|
|
|
enum LoopKind {
|
|
|
|
/// An endless `loop` loop.
|
|
|
|
LoopLoop,
|
|
|
|
/// A `while` loop, with the given expression as condition.
|
|
|
|
WhileLoop(Gc<Expr>),
|
|
|
|
/// A `for` loop.
|
|
|
|
ForLoop,
|
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq)]
|
2014-03-06 07:46:26 -06:00
|
|
|
struct Variable(uint);
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq)]
|
2014-03-06 07:46:26 -06:00
|
|
|
struct LiveNode(uint);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-11-01 20:06:31 -05:00
|
|
|
impl Variable {
|
|
|
|
fn get(&self) -> uint { let Variable(v) = *self; v }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LiveNode {
|
|
|
|
fn get(&self) -> uint { let LiveNode(v) = *self; v }
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:47:32 -05:00
|
|
|
impl Clone for LiveNode {
|
|
|
|
fn clone(&self) -> LiveNode {
|
2013-11-01 20:06:31 -05:00
|
|
|
LiveNode(self.get())
|
2013-07-02 14:47:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq)]
|
2014-03-06 07:46:26 -06:00
|
|
|
enum LiveNodeKind {
|
2013-08-31 11:13:04 -05:00
|
|
|
FreeVarNode(Span),
|
|
|
|
ExprNode(Span),
|
|
|
|
VarDefNode(Span),
|
2012-08-23 11:14:19 -05:00
|
|
|
ExitNode
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
fn live_node_kind_to_string(lnk: LiveNodeKind, cx: &ty::ctxt) -> String {
|
2014-03-16 13:56:24 -05:00
|
|
|
let cm = cx.sess.codemap();
|
2012-10-18 14:20:18 -05:00
|
|
|
match lnk {
|
2014-05-09 20:45:36 -05:00
|
|
|
FreeVarNode(s) => {
|
2014-06-21 05:39:03 -05:00
|
|
|
format!("Free var node [{}]", cm.span_to_string(s))
|
2014-05-09 20:45:36 -05:00
|
|
|
}
|
|
|
|
ExprNode(s) => {
|
2014-06-21 05:39:03 -05:00
|
|
|
format!("Expr node [{}]", cm.span_to_string(s))
|
2014-05-09 20:45:36 -05:00
|
|
|
}
|
|
|
|
VarDefNode(s) => {
|
2014-06-21 05:39:03 -05:00
|
|
|
format!("Var def node [{}]", cm.span_to_string(s))
|
2014-05-09 20:45:36 -05:00
|
|
|
}
|
2014-05-25 05:17:19 -05:00
|
|
|
ExitNode => "Exit node".to_string(),
|
2012-10-18 14:20:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
impl<'a> Visitor<()> for IrMaps<'a> {
|
|
|
|
fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, n: NodeId, _: ()) {
|
|
|
|
visit_fn(self, fk, fd, b, s, n);
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
2014-03-06 07:46:26 -06:00
|
|
|
fn visit_local(&mut self, l: &Local, _: ()) { visit_local(self, l); }
|
|
|
|
fn visit_expr(&mut self, ex: &Expr, _: ()) { visit_expr(self, ex); }
|
|
|
|
fn visit_arm(&mut self, a: &Arm, _: ()) { visit_arm(self, a); }
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
pub fn check_crate(tcx: &ty::ctxt,
|
2014-02-05 15:15:24 -06:00
|
|
|
krate: &Crate) {
|
2014-05-28 14:36:05 -05:00
|
|
|
visit::walk_crate(&mut IrMaps::new(tcx), krate, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
tcx.sess.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
2014-02-19 20:56:33 -06:00
|
|
|
impl fmt::Show for LiveNode {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2014-05-10 16:05:06 -05:00
|
|
|
write!(f, "ln({})", self.get())
|
2014-02-19 20:56:33 -06:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-02-19 20:56:33 -06:00
|
|
|
impl fmt::Show for Variable {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2014-05-10 16:05:06 -05:00
|
|
|
write!(f, "v({})", self.get())
|
2014-02-19 20:56:33 -06:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// ______________________________________________________________________
|
|
|
|
// Creating ir_maps
|
|
|
|
//
|
|
|
|
// This is the first pass and the one that drives the main
|
|
|
|
// computation. It walks up and down the IR once. On the way down,
|
|
|
|
// we count for each function the number of variables as well as
|
|
|
|
// liveness nodes. A liveness node is basically an expression or
|
|
|
|
// capture clause that does something of interest: either it has
|
|
|
|
// interesting control flow or it uses/defines a local variable.
|
|
|
|
//
|
|
|
|
// On the way back up, at each function node we create liveness sets
|
|
|
|
// (we now know precisely how big to make our various vectors and so
|
|
|
|
// forth) and then do the data-flow propagation to compute the set
|
|
|
|
// of live variables at each program point.
|
|
|
|
//
|
|
|
|
// Finally, we run back over the IR one last time and, using the
|
|
|
|
// computed liveness, check various safety conditions. For example,
|
|
|
|
// there must be no live nodes at the definition site for a variable
|
|
|
|
// unless it has an initializer. Similarly, each non-mutable local
|
|
|
|
// variable must not be assigned if there is some successor
|
|
|
|
// assignment. And so forth.
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
impl LiveNode {
|
2014-03-06 07:46:26 -06:00
|
|
|
fn is_valid(&self) -> bool {
|
2014-01-25 01:37:51 -06:00
|
|
|
self.get() != uint::MAX
|
2013-05-31 17:17:22 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-01-25 01:37:51 -06:00
|
|
|
fn invalid_node() -> LiveNode { LiveNode(uint::MAX) }
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
struct CaptureInfo {
|
2012-10-08 13:49:01 -05:00
|
|
|
ln: LiveNode,
|
2013-07-27 03:25:59 -05:00
|
|
|
var_nid: NodeId
|
2012-10-08 13:49:01 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
struct LocalInfo {
|
2013-07-27 03:25:59 -05:00
|
|
|
id: NodeId,
|
2014-06-06 08:51:42 -05:00
|
|
|
ident: Ident
|
2012-05-25 02:14:40 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
enum VarKind {
|
2013-09-01 19:50:59 -05:00
|
|
|
Arg(NodeId, Ident),
|
2012-08-23 11:14:19 -05:00
|
|
|
Local(LocalInfo),
|
|
|
|
ImplicitRet
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
struct IrMaps<'a> {
|
2014-03-05 21:07:47 -06:00
|
|
|
tcx: &'a ty::ctxt,
|
2012-08-23 11:14:19 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
num_live_nodes: uint,
|
|
|
|
num_vars: uint,
|
|
|
|
live_node_map: NodeMap<LiveNode>,
|
|
|
|
variable_map: NodeMap<Variable>,
|
|
|
|
capture_info_map: NodeMap<Rc<Vec<CaptureInfo>>>,
|
|
|
|
var_kinds: Vec<VarKind>,
|
|
|
|
lnks: Vec<LiveNodeKind>,
|
2012-08-23 11:14:19 -05:00
|
|
|
}
|
|
|
|
|
2014-05-28 14:36:05 -05:00
|
|
|
impl<'a> IrMaps<'a> {
|
|
|
|
fn new(tcx: &'a ty::ctxt) -> IrMaps<'a> {
|
|
|
|
IrMaps {
|
|
|
|
tcx: tcx,
|
|
|
|
num_live_nodes: 0,
|
|
|
|
num_vars: 0,
|
|
|
|
live_node_map: NodeMap::new(),
|
|
|
|
variable_map: NodeMap::new(),
|
|
|
|
capture_info_map: NodeMap::new(),
|
|
|
|
var_kinds: Vec::new(),
|
|
|
|
lnks: Vec::new(),
|
|
|
|
}
|
2012-08-23 11:14:19 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode {
|
|
|
|
let ln = LiveNode(self.num_live_nodes);
|
|
|
|
self.lnks.push(lnk);
|
|
|
|
self.num_live_nodes += 1;
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("{} is of kind {}", ln.to_string(),
|
|
|
|
live_node_kind_to_string(lnk, self.tcx));
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
ln
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn add_live_node_for_node(&mut self, node_id: NodeId, lnk: LiveNodeKind) {
|
2012-05-19 07:52:01 -05:00
|
|
|
let ln = self.add_live_node(lnk);
|
2014-03-06 07:46:26 -06:00
|
|
|
self.live_node_map.insert(node_id, ln);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("{} is node {}", ln.to_string(), node_id);
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn add_variable(&mut self, vk: VarKind) -> Variable {
|
|
|
|
let v = Variable(self.num_vars);
|
|
|
|
self.var_kinds.push(vk);
|
|
|
|
self.num_vars += 1;
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2012-08-06 14:34:08 -05:00
|
|
|
match vk {
|
2013-11-28 14:22:53 -06:00
|
|
|
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
|
2014-03-06 07:46:26 -06:00
|
|
|
self.variable_map.insert(node_id, v);
|
2013-04-24 03:29:46 -05:00
|
|
|
},
|
2012-12-10 13:58:37 -06:00
|
|
|
ImplicitRet => {}
|
2012-05-25 02:14:40 -05:00
|
|
|
}
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("{} is {:?}", v.to_string(), vk);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
v
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn variable(&self, node_id: NodeId, span: Span) -> Variable {
|
|
|
|
match self.variable_map.find(&node_id) {
|
2013-03-22 21:26:41 -05:00
|
|
|
Some(&var) => var,
|
2012-08-20 14:23:37 -05:00
|
|
|
None => {
|
2014-05-09 20:45:36 -05:00
|
|
|
self.tcx
|
|
|
|
.sess
|
|
|
|
.span_bug(span, format!("no variable registered for id {}",
|
2014-05-16 12:45:16 -05:00
|
|
|
node_id).as_slice());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
fn variable_name(&self, var: Variable) -> String {
|
2014-03-06 07:46:26 -06:00
|
|
|
match self.var_kinds.get(var.get()) {
|
2014-03-08 14:36:22 -06:00
|
|
|
&Local(LocalInfo { ident: nm, .. }) | &Arg(_, nm) => {
|
2014-06-21 05:39:03 -05:00
|
|
|
token::get_ident(nm).get().to_string()
|
2013-04-24 03:29:46 -05:00
|
|
|
},
|
2014-05-25 05:17:19 -05:00
|
|
|
&ImplicitRet => "<implicit-ret>".to_string()
|
2012-05-25 02:14:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn set_captures(&mut self, node_id: NodeId, cs: Vec<CaptureInfo>) {
|
|
|
|
self.capture_info_map.insert(node_id, Rc::new(cs));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn lnk(&self, ln: LiveNode) -> LiveNodeKind {
|
|
|
|
*self.lnks.get(ln.get())
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
impl<'a> Visitor<()> for Liveness<'a> {
|
2014-01-09 07:05:33 -06:00
|
|
|
fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl, b: &Block, s: Span, n: NodeId, _: ()) {
|
2013-09-25 04:01:09 -05:00
|
|
|
check_fn(self, fk, fd, b, s, n);
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
2014-01-06 06:00:46 -06:00
|
|
|
fn visit_local(&mut self, l: &Local, _: ()) {
|
2013-09-25 04:01:09 -05:00
|
|
|
check_local(self, l);
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
2014-01-06 06:00:46 -06:00
|
|
|
fn visit_expr(&mut self, ex: &Expr, _: ()) {
|
2013-09-25 04:01:09 -05:00
|
|
|
check_expr(self, ex);
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
2014-01-06 06:00:46 -06:00
|
|
|
fn visit_arm(&mut self, a: &Arm, _: ()) {
|
2013-09-25 04:01:09 -05:00
|
|
|
check_arm(self, a);
|
2013-08-14 09:29:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn visit_fn(ir: &mut IrMaps,
|
2014-01-09 07:05:33 -06:00
|
|
|
fk: &FnKind,
|
|
|
|
decl: &FnDecl,
|
2014-01-06 06:00:46 -06:00
|
|
|
body: &Block,
|
2013-08-31 11:13:04 -05:00
|
|
|
sp: Span,
|
2014-03-06 07:46:26 -06:00
|
|
|
id: NodeId) {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("visit_fn: id={}", id);
|
2012-12-23 16:41:37 -06:00
|
|
|
let _i = ::util::common::indenter();
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// swap in a new set of IR maps for this function body:
|
2014-05-28 14:36:05 -05:00
|
|
|
let mut fn_maps = IrMaps::new(ir.tcx);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-04-22 16:27:30 -05:00
|
|
|
unsafe {
|
2014-06-25 14:47:34 -05:00
|
|
|
debug!("creating fn_maps: {}",
|
|
|
|
transmute::<&IrMaps, *const IrMaps>(&fn_maps));
|
2013-04-22 16:27:30 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-08-03 11:45:23 -05:00
|
|
|
for arg in decl.inputs.iter() {
|
2014-04-22 11:06:43 -05:00
|
|
|
pat_util::pat_bindings(&ir.tcx.def_map,
|
2014-05-16 12:15:33 -05:00
|
|
|
&*arg.pat,
|
2014-06-30 20:02:14 -05:00
|
|
|
|_bm, arg_id, _x, path1| {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("adding argument {}", arg_id);
|
2014-06-30 20:02:14 -05:00
|
|
|
let ident = path1.node;
|
2013-04-24 03:29:46 -05:00
|
|
|
fn_maps.add_variable(Arg(arg_id, ident));
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-23 22:53:49 -05:00
|
|
|
};
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// gather up the various local variables, significant expressions,
|
|
|
|
// and so forth:
|
2014-05-14 01:20:25 -05:00
|
|
|
visit::walk_fn(&mut fn_maps, fk, decl, body, sp, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// Special nodes and variables:
|
2012-08-01 19:30:05 -05:00
|
|
|
// - exit_ln represents the end of the fn, either by return or fail
|
2012-05-19 07:52:01 -05:00
|
|
|
// - implicit_ret_var is a pseudo-variable that represents
|
|
|
|
// an implicit return
|
2013-02-04 16:02:01 -06:00
|
|
|
let specials = Specials {
|
|
|
|
exit_ln: fn_maps.add_live_node(ExitNode),
|
|
|
|
fallthrough_ln: fn_maps.add_live_node(ExitNode),
|
|
|
|
no_ret_var: fn_maps.add_variable(ImplicitRet)
|
2012-05-19 07:52:01 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// compute liveness
|
2014-05-28 14:36:05 -05:00
|
|
|
let mut lsets = Liveness::new(&mut fn_maps, specials);
|
2013-09-25 04:01:09 -05:00
|
|
|
let entry_ln = lsets.compute(decl, body);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// check for various error conditions
|
2013-09-25 04:01:09 -05:00
|
|
|
lsets.visit_block(body, ());
|
2014-01-11 15:29:46 -06:00
|
|
|
lsets.check_ret(id, sp, fk, entry_ln, body);
|
2012-11-06 20:41:06 -06:00
|
|
|
lsets.warn_about_unused_args(decl, entry_ln);
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn visit_local(ir: &mut IrMaps, local: &Local) {
|
2014-06-30 20:02:14 -05:00
|
|
|
pat_util::pat_bindings(&ir.tcx.def_map, &*local.pat, |_, p_id, sp, path1| {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("adding local variable {}", p_id);
|
2014-06-30 20:02:14 -05:00
|
|
|
let name = path1.node;
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(p_id, VarDefNode(sp));
|
|
|
|
ir.add_variable(Local(LocalInfo {
|
2012-08-24 13:04:07 -05:00
|
|
|
id: p_id,
|
2014-06-06 08:51:42 -05:00
|
|
|
ident: name
|
2012-08-23 11:14:19 -05:00
|
|
|
}));
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2014-03-06 07:46:26 -06:00
|
|
|
visit::walk_local(ir, local, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn visit_arm(ir: &mut IrMaps, arm: &Arm) {
|
2013-08-03 11:45:23 -05:00
|
|
|
for pat in arm.pats.iter() {
|
2014-06-30 20:02:14 -05:00
|
|
|
pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("adding local variable {} from match with bm {:?}",
|
2012-08-24 13:04:07 -05:00
|
|
|
p_id, bm);
|
2014-06-30 20:02:14 -05:00
|
|
|
let name = path1.node;
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(p_id, VarDefNode(sp));
|
|
|
|
ir.add_variable(Local(LocalInfo {
|
2012-08-24 13:04:07 -05:00
|
|
|
id: p_id,
|
2014-06-06 08:51:42 -05:00
|
|
|
ident: name
|
2012-08-24 13:04:07 -05:00
|
|
|
}));
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-08-24 13:04:07 -05:00
|
|
|
}
|
2014-03-06 07:46:26 -06:00
|
|
|
visit::walk_arm(ir, arm, ());
|
2012-08-24 13:04:07 -05:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:21:53 -05:00
|
|
|
fn moved_variable_node_id_from_def(def: Def) -> Option<NodeId> {
|
|
|
|
match def {
|
|
|
|
DefBinding(nid, _) |
|
|
|
|
DefArg(nid, _) |
|
|
|
|
DefLocal(nid, _) => Some(nid),
|
|
|
|
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
|
2012-08-06 14:34:08 -05:00
|
|
|
match expr.node {
|
2012-05-19 07:52:01 -05:00
|
|
|
// live nodes required for uses or definitions of variables:
|
2014-01-27 06:18:36 -06:00
|
|
|
ExprPath(_) => {
|
2014-03-20 21:49:20 -05:00
|
|
|
let def = ir.tcx.def_map.borrow().get_copy(&expr.id);
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("expr {}: path that leads to {:?}", expr.id, def);
|
2014-04-21 18:21:53 -05:00
|
|
|
if moved_variable_node_id_from_def(def).is_some() {
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2014-03-06 07:46:26 -06:00
|
|
|
visit::walk_expr(ir, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2014-05-29 00:26:56 -05:00
|
|
|
ExprFnBlock(..) | ExprProc(..) | ExprUnboxedFn(..) => {
|
2012-10-22 11:44:56 -05:00
|
|
|
// Interesting control flow (for loops can contain labeled
|
|
|
|
// breaks or continues)
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2012-05-19 07:52:01 -05:00
|
|
|
// Make a live_node for each captured variable, with the span
|
|
|
|
// being the location that the variable is used. This results
|
|
|
|
// in better error messages than just pointing at the closure
|
|
|
|
// construction site.
|
2014-03-04 12:02:49 -06:00
|
|
|
let mut call_caps = Vec::new();
|
2014-04-21 18:21:53 -05:00
|
|
|
freevars::with_freevars(ir.tcx, expr.id, |freevars| {
|
|
|
|
for fv in freevars.iter() {
|
|
|
|
match moved_variable_node_id_from_def(fv.def) {
|
|
|
|
Some(rv) => {
|
|
|
|
let fv_ln = ir.add_live_node(FreeVarNode(fv.span));
|
|
|
|
call_caps.push(CaptureInfo {ln: fv_ln,
|
|
|
|
var_nid: rv});
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2014-04-21 18:21:53 -05:00
|
|
|
});
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.set_captures(expr.id, call_caps);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
visit::walk_expr(ir, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// live nodes required for interesting control flow:
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprIf(..) | ExprMatch(..) | ExprWhile(..) | ExprLoop(..) => {
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
|
|
|
visit::walk_expr(ir, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2014-07-21 22:54:28 -05:00
|
|
|
ExprForLoop(ref pat, _, _, _) => {
|
|
|
|
pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| {
|
|
|
|
debug!("adding local variable {} from for loop with bm {:?}",
|
|
|
|
p_id, bm);
|
|
|
|
let name = path1.node;
|
|
|
|
ir.add_live_node_for_node(p_id, VarDefNode(sp));
|
|
|
|
ir.add_variable(Local(LocalInfo {
|
|
|
|
id: p_id,
|
|
|
|
ident: name
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
|
|
|
visit::walk_expr(ir, expr, ());
|
|
|
|
}
|
2014-02-26 08:06:45 -06:00
|
|
|
ExprBinary(op, _, _) if ast_util::lazy_binop(op) => {
|
2014-03-06 07:46:26 -06:00
|
|
|
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
|
|
|
visit::walk_expr(ir, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, live nodes are not required:
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprIndex(..) | ExprField(..) | ExprVstore(..) | ExprVec(..) |
|
2014-03-09 00:36:10 -06:00
|
|
|
ExprCall(..) | ExprMethodCall(..) | ExprTup(..) |
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprBinary(..) | ExprAddrOf(..) |
|
2014-01-27 18:03:32 -06:00
|
|
|
ExprCast(..) | ExprUnary(..) | ExprBreak(_) |
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprAgain(_) | ExprLit(_) | ExprRet(..) | ExprBlock(..) |
|
|
|
|
ExprAssign(..) | ExprAssignOp(..) | ExprMac(..) |
|
|
|
|
ExprStruct(..) | ExprRepeat(..) | ExprParen(..) |
|
2013-12-17 18:46:18 -06:00
|
|
|
ExprInlineAsm(..) | ExprBox(..) => {
|
2014-03-06 07:46:26 -06:00
|
|
|
visit::walk_expr(ir, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ______________________________________________________________________
|
|
|
|
// Computing liveness sets
|
|
|
|
//
|
|
|
|
// Actually we compute just a bit more than just liveness, but we use
|
|
|
|
// the same basic propagation framework in all cases.
|
|
|
|
|
2013-07-02 14:47:32 -05:00
|
|
|
#[deriving(Clone)]
|
2014-03-06 07:46:26 -06:00
|
|
|
struct Users {
|
2012-08-23 11:14:19 -05:00
|
|
|
reader: LiveNode,
|
|
|
|
writer: LiveNode,
|
2012-05-23 22:53:49 -05:00
|
|
|
used: bool
|
2013-02-04 16:02:01 -06:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-02-04 16:02:01 -06:00
|
|
|
fn invalid_users() -> Users {
|
|
|
|
Users {
|
|
|
|
reader: invalid_node(),
|
|
|
|
writer: invalid_node(),
|
|
|
|
used: false
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
struct Specials {
|
2012-08-23 11:14:19 -05:00
|
|
|
exit_ln: LiveNode,
|
|
|
|
fallthrough_ln: LiveNode,
|
2012-10-08 13:49:01 -05:00
|
|
|
no_ret_var: Variable
|
2013-02-04 16:02:01 -06:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-03-22 16:00:15 -05:00
|
|
|
static ACC_READ: uint = 1u;
|
|
|
|
static ACC_WRITE: uint = 2u;
|
|
|
|
static ACC_USE: uint = 4u;
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
struct Liveness<'a> {
|
|
|
|
ir: &'a mut IrMaps<'a>,
|
2012-09-06 21:40:15 -05:00
|
|
|
s: Specials,
|
2014-03-06 07:46:26 -06:00
|
|
|
successors: Vec<LiveNode>,
|
|
|
|
users: Vec<Users>,
|
2012-10-18 14:20:18 -05:00
|
|
|
// The list of node IDs for the nested loop scopes
|
|
|
|
// we're in.
|
2014-03-06 07:46:26 -06:00
|
|
|
loop_scope: Vec<NodeId>,
|
2012-10-18 14:20:18 -05:00
|
|
|
// mappings from loop node ID to LiveNode
|
|
|
|
// ("break" label should map to loop node ID,
|
|
|
|
// it probably doesn't now)
|
2014-03-06 07:46:26 -06:00
|
|
|
break_ln: NodeMap<LiveNode>,
|
|
|
|
cont_ln: NodeMap<LiveNode>
|
2012-08-23 11:14:19 -05:00
|
|
|
}
|
|
|
|
|
2014-05-28 14:36:05 -05:00
|
|
|
impl<'a> Liveness<'a> {
|
|
|
|
fn new(ir: &'a mut IrMaps<'a>, specials: Specials) -> Liveness<'a> {
|
2014-06-13 22:48:09 -05:00
|
|
|
let num_live_nodes = ir.num_live_nodes;
|
|
|
|
let num_vars = ir.num_vars;
|
2014-05-28 14:36:05 -05:00
|
|
|
Liveness {
|
|
|
|
ir: ir,
|
|
|
|
s: specials,
|
2014-06-13 22:48:09 -05:00
|
|
|
successors: Vec::from_elem(num_live_nodes, invalid_node()),
|
|
|
|
users: Vec::from_elem(num_live_nodes * num_vars, invalid_users()),
|
2014-05-28 14:36:05 -05:00
|
|
|
loop_scope: Vec::new(),
|
|
|
|
break_ln: NodeMap::new(),
|
|
|
|
cont_ln: NodeMap::new(),
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn live_node(&self, node_id: NodeId, span: Span) -> LiveNode {
|
|
|
|
match self.ir.live_node_map.find(&node_id) {
|
2013-03-22 21:26:41 -05:00
|
|
|
Some(&ln) => ln,
|
2012-08-20 14:23:37 -05:00
|
|
|
None => {
|
2012-05-19 07:52:01 -05:00
|
|
|
// This must be a mismatch between the ir_map construction
|
|
|
|
// above and the propagation code below; the two sets of
|
|
|
|
// code have to agree about which AST nodes are worth
|
|
|
|
// creating liveness nodes for.
|
2014-03-06 07:46:26 -06:00
|
|
|
self.ir.tcx.sess.span_bug(
|
2014-05-16 12:45:16 -05:00
|
|
|
span,
|
|
|
|
format!("no live node registered for node {}",
|
|
|
|
node_id).as_slice());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn variable(&self, node_id: NodeId, span: Span) -> Variable {
|
2013-01-10 12:59:58 -06:00
|
|
|
self.ir.variable(node_id, span)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn pat_bindings(&mut self,
|
|
|
|
pat: &Pat,
|
|
|
|
f: |&mut Liveness<'a>, LiveNode, Variable, Span, NodeId|) {
|
2014-04-22 11:06:43 -05:00
|
|
|
pat_util::pat_bindings(&self.ir.tcx.def_map, pat, |_bm, p_id, sp, _n| {
|
2012-05-23 22:53:49 -05:00
|
|
|
let ln = self.live_node(p_id, sp);
|
|
|
|
let var = self.variable(p_id, sp);
|
2014-03-06 07:46:26 -06:00
|
|
|
f(self, ln, var, sp, p_id);
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn arm_pats_bindings(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
pats: &[Gc<Pat>],
|
2014-03-06 07:46:26 -06:00
|
|
|
f: |&mut Liveness<'a>, LiveNode, Variable, Span, NodeId|) {
|
2012-08-24 13:04:07 -05:00
|
|
|
// only consider the first pattern; any later patterns must have
|
|
|
|
// the same bindings, and we also consider the first pattern to be
|
2014-04-20 23:49:39 -05:00
|
|
|
// the "authoritative" set of ids
|
2012-08-24 13:04:07 -05:00
|
|
|
if !pats.is_empty() {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.pat_bindings(&*pats[0], f)
|
2012-08-24 13:04:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn define_bindings_in_pat(&mut self, pat: Gc<Pat>, succ: LiveNode)
|
2014-03-06 07:46:26 -06:00
|
|
|
-> LiveNode {
|
2012-08-24 13:04:07 -05:00
|
|
|
self.define_bindings_in_arm_pats([pat], succ)
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn define_bindings_in_arm_pats(&mut self, pats: &[Gc<Pat>], succ: LiveNode)
|
2014-03-06 07:46:26 -06:00
|
|
|
-> LiveNode {
|
2012-08-24 13:04:07 -05:00
|
|
|
let mut succ = succ;
|
2014-03-06 07:46:26 -06:00
|
|
|
self.arm_pats_bindings(pats, |this, ln, var, _sp, _id| {
|
|
|
|
this.init_from_succ(ln, succ);
|
|
|
|
this.define(ln, var);
|
2012-08-24 13:04:07 -05:00
|
|
|
succ = ln;
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2012-08-24 13:04:07 -05:00
|
|
|
succ
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn idx(&self, ln: LiveNode, var: Variable) -> uint {
|
|
|
|
ln.get() * self.ir.num_vars + var.get()
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn live_on_entry(&self, ln: LiveNode, var: Variable)
|
|
|
|
-> Option<LiveNodeKind> {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(ln.is_valid());
|
2014-03-06 07:46:26 -06:00
|
|
|
let reader = self.users.get(self.idx(ln, var)).reader;
|
2013-01-10 12:59:58 -06:00
|
|
|
if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
/*
|
|
|
|
Is this variable live on entry to any of its successor nodes?
|
|
|
|
*/
|
2014-03-06 07:46:26 -06:00
|
|
|
fn live_on_exit(&self, ln: LiveNode, var: Variable)
|
|
|
|
-> Option<LiveNodeKind> {
|
|
|
|
let successor = *self.successors.get(ln.get());
|
2013-12-22 16:08:42 -06:00
|
|
|
self.live_on_entry(successor, var)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(ln.is_valid());
|
2014-03-06 07:46:26 -06:00
|
|
|
self.users.get(self.idx(ln, var)).used
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
|
|
|
|
-> Option<LiveNodeKind> {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(ln.is_valid());
|
2014-03-06 07:46:26 -06:00
|
|
|
let writer = self.users.get(self.idx(ln, var)).writer;
|
2013-01-10 12:59:58 -06:00
|
|
|
if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn assigned_on_exit(&self, ln: LiveNode, var: Variable)
|
|
|
|
-> Option<LiveNodeKind> {
|
|
|
|
let successor = *self.successors.get(ln.get());
|
2013-12-22 16:08:42 -06:00
|
|
|
self.assigned_on_entry(successor, var)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn indices2(&mut self,
|
|
|
|
ln: LiveNode,
|
|
|
|
succ_ln: LiveNode,
|
|
|
|
op: |&mut Liveness<'a>, uint, uint|) {
|
2012-08-23 11:14:19 -05:00
|
|
|
let node_base_idx = self.idx(ln, Variable(0u));
|
|
|
|
let succ_base_idx = self.idx(succ_ln, Variable(0u));
|
2014-03-06 07:46:26 -06:00
|
|
|
for var_idx in range(0u, self.ir.num_vars) {
|
|
|
|
op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn write_vars(&self,
|
|
|
|
wr: &mut io::Writer,
|
|
|
|
ln: LiveNode,
|
|
|
|
test: |uint| -> LiveNode) -> io::IoResult<()> {
|
2012-10-18 14:20:18 -05:00
|
|
|
let node_base_idx = self.idx(ln, Variable(0));
|
2014-03-06 07:46:26 -06:00
|
|
|
for var_idx in range(0u, self.ir.num_vars) {
|
2012-05-19 07:52:01 -05:00
|
|
|
let idx = node_base_idx + var_idx;
|
|
|
|
if test(idx).is_valid() {
|
2014-06-21 05:39:03 -05:00
|
|
|
try!(write!(wr, " {}", Variable(var_idx).to_string()));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
2014-01-29 20:42:19 -06:00
|
|
|
Ok(())
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn find_loop_scope(&self,
|
|
|
|
opt_label: Option<Ident>,
|
|
|
|
id: NodeId,
|
|
|
|
sp: Span)
|
|
|
|
-> NodeId {
|
2012-10-18 14:20:18 -05:00
|
|
|
match opt_label {
|
2013-12-23 13:15:16 -06:00
|
|
|
Some(_) => {
|
|
|
|
// Refers to a labeled loop. Use the results of resolve
|
|
|
|
// to find with one
|
2014-03-20 21:49:20 -05:00
|
|
|
match self.ir.tcx.def_map.borrow().find(&id) {
|
2013-09-01 20:45:37 -05:00
|
|
|
Some(&DefLabel(loop_id)) => loop_id,
|
2014-03-06 07:46:26 -06:00
|
|
|
_ => self.ir.tcx.sess.span_bug(sp, "label on break/loop \
|
|
|
|
doesn't refer to a loop")
|
2013-12-23 13:15:16 -06:00
|
|
|
}
|
|
|
|
}
|
2013-03-16 13:11:31 -05:00
|
|
|
None => {
|
2012-10-18 14:20:18 -05:00
|
|
|
// Vanilla 'break' or 'loop', so use the enclosing
|
|
|
|
// loop scope
|
2014-03-06 07:46:26 -06:00
|
|
|
if self.loop_scope.len() == 0 {
|
|
|
|
self.ir.tcx.sess.span_bug(sp, "break outside loop");
|
2013-05-02 13:32:37 -05:00
|
|
|
} else {
|
2014-06-18 04:40:41 -05:00
|
|
|
*self.loop_scope.last().unwrap()
|
2012-10-18 14:20:18 -05:00
|
|
|
}
|
2013-03-16 13:11:31 -05:00
|
|
|
}
|
2012-10-18 14:20:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-29 20:42:19 -06:00
|
|
|
#[allow(unused_must_use)]
|
2014-05-22 18:57:53 -05:00
|
|
|
fn ln_str(&self, ln: LiveNode) -> String {
|
2014-01-15 15:25:09 -06:00
|
|
|
let mut wr = io::MemWriter::new();
|
|
|
|
{
|
|
|
|
let wr = &mut wr as &mut io::Writer;
|
2014-03-06 07:46:26 -06:00
|
|
|
write!(wr, "[ln({}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
|
|
|
|
self.write_vars(wr, ln, |idx| self.users.get(idx).reader);
|
|
|
|
write!(wr, " writes");
|
|
|
|
self.write_vars(wr, ln, |idx| self.users.get(idx).writer);
|
2014-06-21 05:39:03 -05:00
|
|
|
write!(wr, " precedes {}]", self.successors.get(ln.get()).to_string());
|
2014-01-15 15:25:09 -06:00
|
|
|
}
|
2014-05-25 05:17:19 -05:00
|
|
|
str::from_utf8(wr.unwrap().as_slice()).unwrap().to_string()
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
|
|
|
*self.successors.get_mut(ln.get()) = succ_ln;
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// It is not necessary to initialize the
|
|
|
|
// values to empty because this is the value
|
|
|
|
// they have when they are created, and the sets
|
|
|
|
// only grow during iterations.
|
|
|
|
//
|
|
|
|
// self.indices(ln) { |idx|
|
|
|
|
// self.users[idx] = invalid_users();
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
2012-05-19 07:52:01 -05:00
|
|
|
// more efficient version of init_empty() / merge_from_succ()
|
2014-03-06 07:46:26 -06:00
|
|
|
*self.successors.get_mut(ln.get()) = succ_ln;
|
2013-12-22 16:08:42 -06:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
|
|
|
*this.users.get_mut(idx) = *this.users.get(succ_idx)
|
2012-06-26 15:55:56 -05:00
|
|
|
});
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("init_from_succ(ln={}, succ={})",
|
2012-08-22 19:24:52 -05:00
|
|
|
self.ln_str(ln), self.ln_str(succ_ln));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn merge_from_succ(&mut self,
|
|
|
|
ln: LiveNode,
|
|
|
|
succ_ln: LiveNode,
|
|
|
|
first_merge: bool)
|
|
|
|
-> bool {
|
2012-08-01 19:30:05 -05:00
|
|
|
if ln == succ_ln { return false; }
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
let mut changed = false;
|
2014-03-06 07:46:26 -06:00
|
|
|
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
|
|
|
changed |= copy_if_invalid(this.users.get(succ_idx).reader,
|
|
|
|
&mut this.users.get_mut(idx).reader);
|
|
|
|
changed |= copy_if_invalid(this.users.get(succ_idx).writer,
|
|
|
|
&mut this.users.get_mut(idx).writer);
|
|
|
|
if this.users.get(succ_idx).used && !this.users.get(idx).used {
|
|
|
|
this.users.get_mut(idx).used = true;
|
2012-05-23 22:53:49 -05:00
|
|
|
changed = true;
|
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("merge_from_succ(ln={}, succ={}, first_merge={}, changed={})",
|
2014-06-21 05:39:03 -05:00
|
|
|
ln.to_string(), self.ln_str(succ_ln), first_merge, changed);
|
2012-08-01 19:30:05 -05:00
|
|
|
return changed;
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2012-10-05 18:17:10 -05:00
|
|
|
fn copy_if_invalid(src: LiveNode, dst: &mut LiveNode) -> bool {
|
2014-03-06 07:46:26 -06:00
|
|
|
if src.is_valid() && !dst.is_valid() {
|
|
|
|
*dst = src;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Indicates that a local variable was *defined*; we know that no
|
|
|
|
// uses of the variable can precede the definition (resolve checks
|
|
|
|
// this) so we just clear out all the data.
|
2014-03-06 07:46:26 -06:00
|
|
|
fn define(&mut self, writer: LiveNode, var: Variable) {
|
2012-05-19 07:52:01 -05:00
|
|
|
let idx = self.idx(writer, var);
|
2014-03-06 07:46:26 -06:00
|
|
|
self.users.get_mut(idx).reader = invalid_node();
|
|
|
|
self.users.get_mut(idx).writer = invalid_node();
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("{} defines {} (idx={}): {}", writer.to_string(), var.to_string(),
|
2012-08-22 19:24:52 -05:00
|
|
|
idx, self.ln_str(writer));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Either read, write, or both depending on the acc bitset
|
2014-03-06 07:46:26 -06:00
|
|
|
fn acc(&mut self, ln: LiveNode, var: Variable, acc: uint) {
|
|
|
|
debug!("{} accesses[{:x}] {}: {}",
|
2014-06-21 05:39:03 -05:00
|
|
|
ln.to_string(), acc, var.to_string(), self.ln_str(ln));
|
2014-03-06 07:46:26 -06:00
|
|
|
|
2012-05-23 22:53:49 -05:00
|
|
|
let idx = self.idx(ln, var);
|
2014-03-06 07:46:26 -06:00
|
|
|
let user = self.users.get_mut(idx);
|
2012-05-23 22:53:49 -05:00
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
if (acc & ACC_WRITE) != 0 {
|
2012-05-23 22:53:49 -05:00
|
|
|
user.reader = invalid_node();
|
|
|
|
user.writer = ln;
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// Important: if we both read/write, must do read second
|
|
|
|
// or else the write will override.
|
2012-10-18 14:20:18 -05:00
|
|
|
if (acc & ACC_READ) != 0 {
|
2012-05-23 22:53:49 -05:00
|
|
|
user.reader = ln;
|
|
|
|
}
|
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
if (acc & ACC_USE) != 0 {
|
2013-02-25 19:03:31 -06:00
|
|
|
user.used = true;
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// _______________________________________________________________________
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn compute(&mut self, decl: &FnDecl, body: &Block) -> LiveNode {
|
2012-07-13 17:45:24 -05:00
|
|
|
// if there is a `break` or `again` at the top level, then it's
|
2012-05-19 07:52:01 -05:00
|
|
|
// effectively a return---this only occurs in `for` loops,
|
|
|
|
// where the body is really a closure.
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("compute: using id for block, {}", block_to_string(body));
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2014-06-13 22:48:09 -05:00
|
|
|
let exit_ln = self.s.exit_ln;
|
2012-08-23 11:14:19 -05:00
|
|
|
let entry_ln: LiveNode =
|
2014-06-13 22:48:09 -05:00
|
|
|
self.with_loop_nodes(body.id, exit_ln, exit_ln,
|
2014-03-06 07:46:26 -06:00
|
|
|
|this| this.propagate_through_fn_block(decl, body));
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2013-10-21 15:08:31 -05:00
|
|
|
// hack to skip the loop unless debug! is enabled:
|
|
|
|
debug!("^^ liveness computation results for body {} (entry={})",
|
2012-05-19 07:52:01 -05:00
|
|
|
{
|
2014-03-06 07:46:26 -06:00
|
|
|
for ln_idx in range(0u, self.ir.num_live_nodes) {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("{}", self.ln_str(LiveNode(ln_idx)));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2013-07-16 13:08:35 -05:00
|
|
|
body.id
|
2012-05-19 07:52:01 -05:00
|
|
|
},
|
2014-06-21 05:39:03 -05:00
|
|
|
entry_ln.to_string());
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
entry_ln
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_fn_block(&mut self, _: &FnDecl, blk: &Block)
|
|
|
|
-> LiveNode {
|
2012-05-23 22:53:49 -05:00
|
|
|
// the fallthrough exit is only for those cases where we do not
|
|
|
|
// explicitly return:
|
2014-06-13 22:48:09 -05:00
|
|
|
let s = self.s;
|
|
|
|
self.init_from_succ(s.fallthrough_ln, s.exit_ln);
|
2013-07-16 13:08:35 -05:00
|
|
|
if blk.expr.is_none() {
|
2014-06-13 22:48:09 -05:00
|
|
|
self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-06-13 22:48:09 -05:00
|
|
|
self.propagate_through_block(blk, s.fallthrough_ln)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_block(&mut self, blk: &Block, succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2013-07-16 13:08:35 -05:00
|
|
|
let succ = self.propagate_through_opt_expr(blk.expr, succ);
|
2014-03-16 18:04:29 -05:00
|
|
|
blk.stmts.iter().rev().fold(succ, |succ, stmt| {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_stmt(&**stmt, succ)
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_stmt(&mut self, stmt: &Stmt, succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2012-08-06 14:34:08 -05:00
|
|
|
match stmt.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
StmtDecl(ref decl, _) => {
|
|
|
|
self.propagate_through_decl(&**decl, succ)
|
2014-03-06 07:46:26 -06:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => {
|
|
|
|
self.propagate_through_expr(&**expr, succ)
|
2014-03-06 07:46:26 -06:00
|
|
|
}
|
2012-11-12 22:06:55 -06:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
StmtMac(..) => {
|
|
|
|
self.ir.tcx.sess.span_bug(stmt.span, "unexpanded macro");
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_decl(&mut self, decl: &Decl, succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2013-03-20 00:17:42 -05:00
|
|
|
match decl.node {
|
2013-09-01 20:45:37 -05:00
|
|
|
DeclLocal(ref local) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_local(&**local, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2013-09-01 20:45:37 -05:00
|
|
|
DeclItem(_) => succ,
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_local(&mut self, local: &Local, succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2012-05-19 07:52:01 -05:00
|
|
|
// Note: we mark the variable as defined regardless of whether
|
|
|
|
// there is an initializer. Initially I had thought to only mark
|
|
|
|
// the live variable as defined if it was initialized, and then we
|
|
|
|
// could check for uninit variables just by scanning what is live
|
|
|
|
// at the start of the function. But that doesn't work so well for
|
|
|
|
// immutable variables defined in a loop:
|
|
|
|
// loop { let x; x = 5; }
|
|
|
|
// because the "assignment" loops back around and generates an error.
|
|
|
|
//
|
|
|
|
// So now we just check that variables defined w/o an
|
|
|
|
// initializer are not live at the point of their
|
|
|
|
// initialization, which is mildly more complex than checking
|
|
|
|
// once at the func header but otherwise equivalent.
|
|
|
|
|
2013-07-19 00:38:55 -05:00
|
|
|
let succ = self.propagate_through_opt_expr(local.init, succ);
|
|
|
|
self.define_bindings_in_pat(local.pat, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn propagate_through_exprs(&mut self, exprs: &[Gc<Expr>], succ: LiveNode)
|
2014-03-06 07:46:26 -06:00
|
|
|
-> LiveNode {
|
Deprecate the rev_iter pattern in all places where a DoubleEndedIterator is provided (everywhere but treemap)
This commit deprecates rev_iter, mut_rev_iter, move_rev_iter everywhere (except treemap) and also
deprecates related functions like rsplit, rev_components, and rev_str_components. In every case,
these functions can be replaced with the non-reversed form followed by a call to .rev(). To make this
more concrete, a translation table for all functional changes necessary follows:
* container.rev_iter() -> container.iter().rev()
* container.mut_rev_iter() -> container.mut_iter().rev()
* container.move_rev_iter() -> container.move_iter().rev()
* sliceorstr.rsplit(sep) -> sliceorstr.split(sep).rev()
* path.rev_components() -> path.components().rev()
* path.rev_str_components() -> path.str_components().rev()
In terms of the type system, this change also deprecates any specialized reversed iterator types (except
in treemap), opting instead to use Rev directly if any type annotations are needed. However, since
methods directly returning reversed iterators are now discouraged, the need for such annotations should
be small. However, in those cases, the general pattern for conversion is to take whatever follows Rev in
the original reversed name and surround it with Rev<>:
* RevComponents<'a> -> Rev<Components<'a>>
* RevStrComponents<'a> -> Rev<StrComponents<'a>>
* RevItems<'a, T> -> Rev<Items<'a, T>>
* etc.
The reasoning behind this change is that it makes the standard API much simpler without reducing readability,
performance, or power. The presence of functions such as rev_iter adds more boilerplate code to libraries
(all of which simply call .iter().rev()), clutters up the documentation, and only helps code by saving two
characters. Additionally, the numerous type synonyms that were used to make the type signatures look nice
like RevItems add even more boilerplate and clutter up the docs even more. With this change, all that cruft
goes away.
[breaking-change]
2014-04-20 23:59:12 -05:00
|
|
|
exprs.iter().rev().fold(succ, |succ, expr| {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**expr, succ)
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_opt_expr(&mut self,
|
2014-05-16 12:15:33 -05:00
|
|
|
opt_expr: Option<Gc<Expr>>,
|
2014-03-06 07:46:26 -06:00
|
|
|
succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2013-11-21 17:42:55 -06:00
|
|
|
opt_expr.iter().fold(succ, |succ, expr| {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**expr, succ)
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2014-06-21 05:39:03 -05:00
|
|
|
debug!("propagate_through_expr: {}", expr_to_string(expr));
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2013-03-20 00:17:42 -05:00
|
|
|
match expr.node {
|
2012-05-19 07:52:01 -05:00
|
|
|
// Interesting cases with control flow or which gen/kill
|
|
|
|
|
2014-01-27 06:18:36 -06:00
|
|
|
ExprPath(_) => {
|
2012-10-08 13:49:01 -05:00
|
|
|
self.access_path(expr, succ, ACC_READ | ACC_USE)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprField(ref e, _, _) => {
|
|
|
|
self.propagate_through_expr(&**e, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-29 00:26:56 -05:00
|
|
|
ExprFnBlock(_, ref blk) |
|
|
|
|
ExprProc(_, ref blk) |
|
|
|
|
ExprUnboxedFn(_, ref blk) => {
|
|
|
|
debug!("{} is an ExprFnBlock, ExprProc, or ExprUnboxedFn",
|
|
|
|
expr_to_string(expr));
|
2012-10-18 14:20:18 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
The next-node for a break is the successor of the entire
|
|
|
|
loop. The next-node for a continue is the top of this loop.
|
|
|
|
*/
|
2014-03-06 07:46:26 -06:00
|
|
|
let node = self.live_node(expr.id, expr.span);
|
|
|
|
self.with_loop_nodes(blk.id, succ, node, |this| {
|
2012-10-18 14:20:18 -05:00
|
|
|
|
|
|
|
// the construction of a closure itself is not important,
|
|
|
|
// but we have to consider the closed over variables.
|
2014-03-06 07:46:26 -06:00
|
|
|
let caps = match this.ir.capture_info_map.find(&expr.id) {
|
|
|
|
Some(caps) => caps.clone(),
|
|
|
|
None => {
|
|
|
|
this.ir.tcx.sess.span_bug(expr.span, "no registered caps");
|
|
|
|
}
|
|
|
|
};
|
2014-03-21 00:10:44 -05:00
|
|
|
caps.iter().rev().fold(succ, |succ, cap| {
|
2014-03-06 07:46:26 -06:00
|
|
|
this.init_from_succ(cap.ln, succ);
|
|
|
|
let var = this.variable(cap.var_nid, expr.span);
|
|
|
|
this.acc(cap.ln, var, ACC_READ | ACC_USE);
|
2012-10-18 14:20:18 -05:00
|
|
|
cap.ln
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-10-18 14:20:18 -05:00
|
|
|
})
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprIf(ref cond, ref then, ref els) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
//
|
|
|
|
// (cond)
|
|
|
|
// |
|
|
|
|
// v
|
|
|
|
// (expr)
|
|
|
|
// / \
|
|
|
|
// | |
|
|
|
|
// v v
|
|
|
|
// (then)(els)
|
|
|
|
// | |
|
|
|
|
// v v
|
|
|
|
// ( succ )
|
|
|
|
//
|
2014-05-16 12:15:33 -05:00
|
|
|
let else_ln = self.propagate_through_opt_expr(els.clone(), succ);
|
|
|
|
let then_ln = self.propagate_through_block(&**then, succ);
|
2012-05-19 07:52:01 -05:00
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
self.init_from_succ(ln, else_ln);
|
|
|
|
self.merge_from_succ(ln, then_ln, false);
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**cond, ln)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprWhile(ref cond, ref blk) => {
|
2014-07-21 22:54:28 -05:00
|
|
|
self.propagate_through_loop(expr,
|
|
|
|
WhileLoop(cond.clone()),
|
|
|
|
&**blk,
|
|
|
|
succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-07-21 22:54:28 -05:00
|
|
|
ExprForLoop(_, ref head, ref blk, _) => {
|
|
|
|
let ln = self.propagate_through_loop(expr, ForLoop, &**blk, succ);
|
|
|
|
self.propagate_through_expr(&**head, ln)
|
|
|
|
}
|
2013-07-29 19:25:00 -05:00
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
// Note that labels have been resolved, so we don't need to look
|
|
|
|
// at the label ident
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprLoop(ref blk, _) => {
|
2014-07-21 22:54:28 -05:00
|
|
|
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprMatch(ref e, ref arms) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
//
|
|
|
|
// (e)
|
|
|
|
// |
|
|
|
|
// v
|
|
|
|
// (expr)
|
|
|
|
// / | \
|
|
|
|
// | | |
|
|
|
|
// v v v
|
|
|
|
// (..arms..)
|
|
|
|
// | | |
|
|
|
|
// v v v
|
|
|
|
// ( succ )
|
|
|
|
//
|
|
|
|
//
|
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
self.init_empty(ln, succ);
|
|
|
|
let mut first_merge = true;
|
2013-08-03 11:45:23 -05:00
|
|
|
for arm in arms.iter() {
|
2012-08-24 13:04:07 -05:00
|
|
|
let body_succ =
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&*arm.body, succ);
|
2012-08-24 13:04:07 -05:00
|
|
|
let guard_succ =
|
|
|
|
self.propagate_through_opt_expr(arm.guard, body_succ);
|
2012-05-19 07:52:01 -05:00
|
|
|
let arm_succ =
|
2014-02-28 17:25:15 -06:00
|
|
|
self.define_bindings_in_arm_pats(arm.pats.as_slice(),
|
|
|
|
guard_succ);
|
2012-05-19 07:52:01 -05:00
|
|
|
self.merge_from_succ(ln, arm_succ, first_merge);
|
|
|
|
first_merge = false;
|
|
|
|
};
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**e, ln)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprRet(o_e) => {
|
2012-08-03 21:59:04 -05:00
|
|
|
// ignore succ and subst exit_ln:
|
2014-06-13 22:48:09 -05:00
|
|
|
let exit_ln = self.s.exit_ln;
|
|
|
|
self.propagate_through_opt_expr(o_e, exit_ln)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprBreak(opt_label) => {
|
2012-10-18 14:20:18 -05:00
|
|
|
// Find which label this break jumps to
|
|
|
|
let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
// Now that we know the label we're going to,
|
|
|
|
// look it up in the break loop nodes table
|
2012-08-14 21:20:56 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
match self.break_ln.find(&sc) {
|
2013-03-22 21:26:41 -05:00
|
|
|
Some(&b) => b,
|
2014-03-06 07:46:26 -06:00
|
|
|
None => self.ir.tcx.sess.span_bug(expr.span,
|
|
|
|
"break to unknown label")
|
2012-10-18 14:20:18 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprAgain(opt_label) => {
|
2013-06-06 02:38:41 -05:00
|
|
|
// Find which label this expr continues to
|
2012-10-18 14:20:18 -05:00
|
|
|
let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
// Now that we know the label we're going to,
|
|
|
|
// look it up in the continue loop nodes table
|
2012-08-14 21:20:56 -05:00
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
match self.cont_ln.find(&sc) {
|
2013-03-22 21:26:41 -05:00
|
|
|
Some(&b) => b,
|
2014-03-06 07:46:26 -06:00
|
|
|
None => self.ir.tcx.sess.span_bug(expr.span,
|
|
|
|
"loop to unknown label")
|
2012-10-18 14:20:18 -05:00
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprAssign(ref l, ref r) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
// see comment on lvalues in
|
|
|
|
// propagate_through_lvalue_components()
|
2014-05-16 12:15:33 -05:00
|
|
|
let succ = self.write_lvalue(&**l, succ, ACC_WRITE);
|
|
|
|
let succ = self.propagate_through_lvalue_components(&**l, succ);
|
|
|
|
self.propagate_through_expr(&**r, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprAssignOp(_, ref l, ref r) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
// see comment on lvalues in
|
|
|
|
// propagate_through_lvalue_components()
|
2014-05-16 12:15:33 -05:00
|
|
|
let succ = self.write_lvalue(&**l, succ, ACC_WRITE|ACC_READ);
|
|
|
|
let succ = self.propagate_through_expr(&**r, succ);
|
|
|
|
self.propagate_through_lvalue_components(&**l, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Uninteresting cases: just propagate in rev exec order
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprVstore(ref expr, _) => {
|
|
|
|
self.propagate_through_expr(&**expr, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-04-04 05:12:18 -05:00
|
|
|
ExprVec(ref exprs) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
self.propagate_through_exprs(exprs.as_slice(), succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprRepeat(ref element, ref count) => {
|
|
|
|
let succ = self.propagate_through_expr(&**count, succ);
|
|
|
|
self.propagate_through_expr(&**element, succ)
|
2012-08-03 20:01:30 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprStruct(_, ref fields, ref with_expr) => {
|
|
|
|
let succ = self.propagate_through_opt_expr(with_expr.clone(), succ);
|
2014-03-16 18:04:29 -05:00
|
|
|
fields.iter().rev().fold(succ, |succ, field| {
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&*field.expr, succ)
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-07-23 18:39:18 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprCall(ref f, ref args) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
// calling a fn with bot return type means that the fn
|
|
|
|
// will fail, and hence the successors can be ignored
|
2014-06-01 18:35:01 -05:00
|
|
|
let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
|
2014-05-16 12:15:33 -05:00
|
|
|
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
|
2014-06-01 18:35:01 -05:00
|
|
|
ty::type_is_bot(t_ret)
|
|
|
|
};
|
|
|
|
let succ = if is_bot {
|
|
|
|
self.s.exit_ln
|
|
|
|
} else {
|
|
|
|
succ
|
|
|
|
};
|
2014-02-28 17:25:15 -06:00
|
|
|
let succ = self.propagate_through_exprs(args.as_slice(), succ);
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**f, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-02-26 08:06:45 -06:00
|
|
|
ExprMethodCall(_, _, ref args) => {
|
2012-11-30 13:18:25 -06:00
|
|
|
// calling a method with bot return type means that the method
|
|
|
|
// will fail, and hence the successors can be ignored
|
2014-03-06 07:46:26 -06:00
|
|
|
let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id);
|
2012-11-30 13:18:25 -06:00
|
|
|
let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
|
|
|
|
else {succ};
|
2014-02-28 17:25:15 -06:00
|
|
|
self.propagate_through_exprs(args.as_slice(), succ)
|
2012-11-30 13:18:25 -06:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprTup(ref exprs) => {
|
2014-02-28 17:25:15 -06:00
|
|
|
self.propagate_through_exprs(exprs.as_slice(), succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprBinary(op, ref l, ref r) if ast_util::lazy_binop(op) => {
|
|
|
|
let r_succ = self.propagate_through_expr(&**r, succ);
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
self.init_from_succ(ln, succ);
|
|
|
|
self.merge_from_succ(ln, r_succ, false);
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
self.propagate_through_expr(&**l, ln)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprIndex(ref l, ref r) |
|
|
|
|
ExprBinary(_, ref l, ref r) |
|
|
|
|
ExprBox(ref l, ref r) => {
|
|
|
|
self.propagate_through_exprs([l.clone(), r.clone()], succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprAddrOf(_, ref e) |
|
|
|
|
ExprCast(ref e, _) |
|
|
|
|
ExprUnary(_, ref e) |
|
|
|
|
ExprParen(ref e) => {
|
|
|
|
self.propagate_through_expr(&**e, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-10-17 13:24:41 -05:00
|
|
|
ExprInlineAsm(ref ia) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
|
2013-10-17 13:24:41 -05:00
|
|
|
// see comment on lvalues in
|
|
|
|
// propagate_through_lvalue_components()
|
2014-05-16 12:15:33 -05:00
|
|
|
let succ = self.write_lvalue(&**expr, succ, ACC_WRITE);
|
|
|
|
self.propagate_through_lvalue_components(&**expr, succ)
|
2014-03-09 17:41:18 -05:00
|
|
|
});
|
|
|
|
// Inputs are executed first. Propagate last because of rev order
|
2014-05-16 12:15:33 -05:00
|
|
|
ia.inputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
|
|
|
|
self.propagate_through_expr(&**expr, succ)
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2013-03-12 19:53:25 -05:00
|
|
|
}
|
|
|
|
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprLit(..) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
succ
|
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprBlock(ref blk) => {
|
|
|
|
self.propagate_through_block(&**blk, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprMac(..) => {
|
2014-03-06 07:46:26 -06:00
|
|
|
self.ir.tcx.sess.span_bug(expr.span, "unexpanded macro");
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_lvalue_components(&mut self,
|
|
|
|
expr: &Expr,
|
|
|
|
succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2012-05-19 07:52:01 -05:00
|
|
|
// # Lvalues
|
|
|
|
//
|
|
|
|
// In general, the full flow graph structure for an
|
|
|
|
// assignment/move/etc can be handled in one of two ways,
|
|
|
|
// depending on whether what is being assigned is a "tracked
|
2012-10-08 13:49:01 -05:00
|
|
|
// value" or not. A tracked value is basically a local
|
|
|
|
// variable or argument.
|
2012-05-19 07:52:01 -05:00
|
|
|
//
|
|
|
|
// The two kinds of graphs are:
|
|
|
|
//
|
|
|
|
// Tracked lvalue Untracked lvalue
|
|
|
|
// ----------------------++-----------------------
|
|
|
|
// ||
|
|
|
|
// | || |
|
|
|
|
// v || v
|
|
|
|
// (rvalue) || (rvalue)
|
|
|
|
// | || |
|
2012-05-25 02:14:40 -05:00
|
|
|
// v || v
|
|
|
|
// (write of lvalue) || (lvalue components)
|
2012-05-19 07:52:01 -05:00
|
|
|
// | || |
|
|
|
|
// v || v
|
|
|
|
// (succ) || (succ)
|
|
|
|
// ||
|
|
|
|
// ----------------------++-----------------------
|
|
|
|
//
|
|
|
|
// I will cover the two cases in turn:
|
|
|
|
//
|
|
|
|
// # Tracked lvalues
|
|
|
|
//
|
2012-10-08 13:49:01 -05:00
|
|
|
// A tracked lvalue is a local variable/argument `x`. In
|
2012-05-19 07:52:01 -05:00
|
|
|
// these cases, the link_node where the write occurs is linked
|
2012-10-08 13:49:01 -05:00
|
|
|
// to node id of `x`. The `write_lvalue()` routine generates
|
|
|
|
// the contents of this node. There are no subcomponents to
|
|
|
|
// consider.
|
2012-05-19 07:52:01 -05:00
|
|
|
//
|
|
|
|
// # Non-tracked lvalues
|
|
|
|
//
|
|
|
|
// These are lvalues like `x[5]` or `x.f`. In that case, we
|
|
|
|
// basically ignore the value which is written to but generate
|
|
|
|
// reads for the components---`x` in these two examples. The
|
|
|
|
// components reads are generated by
|
|
|
|
// `propagate_through_lvalue_components()` (this fn).
|
|
|
|
//
|
|
|
|
// # Illegal lvalues
|
|
|
|
//
|
|
|
|
// It is still possible to observe assignments to non-lvalues;
|
|
|
|
// these errors are detected in the later pass borrowck. We
|
|
|
|
// just ignore such cases and treat them as reads.
|
|
|
|
|
2012-08-06 14:34:08 -05:00
|
|
|
match expr.node {
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprPath(_) => succ,
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprField(ref e, _, _) => self.propagate_through_expr(&**e, succ),
|
2012-10-08 13:49:01 -05:00
|
|
|
_ => self.propagate_through_expr(expr, succ)
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// see comment on propagate_through_lvalue()
|
2014-03-06 07:46:26 -06:00
|
|
|
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
|
|
|
|
-> LiveNode {
|
2012-08-06 14:34:08 -05:00
|
|
|
match expr.node {
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprPath(_) => self.access_path(expr, succ, acc),
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// We do not track other lvalues, so just propagate through
|
|
|
|
// to their subcomponents. Also, it may happen that
|
|
|
|
// non-lvalues occur here, because those are detected in the
|
|
|
|
// later pass borrowck.
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => succ
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn access_path(&mut self, expr: &Expr, succ: LiveNode, acc: uint)
|
|
|
|
-> LiveNode {
|
2014-03-20 21:49:20 -05:00
|
|
|
let def = self.ir.tcx.def_map.borrow().get_copy(&expr.id);
|
2014-04-21 18:21:53 -05:00
|
|
|
match moved_variable_node_id_from_def(def) {
|
2012-10-08 13:49:01 -05:00
|
|
|
Some(nid) => {
|
2012-05-19 07:52:01 -05:00
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
if acc != 0u {
|
|
|
|
self.init_from_succ(ln, succ);
|
|
|
|
let var = self.variable(nid, expr.span);
|
|
|
|
self.acc(ln, var, acc);
|
|
|
|
}
|
|
|
|
ln
|
|
|
|
}
|
2012-08-20 14:23:37 -05:00
|
|
|
None => succ
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn propagate_through_loop(&mut self,
|
|
|
|
expr: &Expr,
|
2014-07-21 22:54:28 -05:00
|
|
|
kind: LoopKind,
|
2014-03-06 07:46:26 -06:00
|
|
|
body: &Block,
|
|
|
|
succ: LiveNode)
|
|
|
|
-> LiveNode {
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
We model control flow like this:
|
|
|
|
|
|
|
|
(cond) <--+
|
|
|
|
| |
|
|
|
|
v |
|
|
|
|
+-- (expr) |
|
|
|
|
| | |
|
|
|
|
| v |
|
|
|
|
| (body) ---+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
(succ)
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2012-05-19 07:52:01 -05:00
|
|
|
// first iteration:
|
|
|
|
let mut first_merge = true;
|
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
self.init_empty(ln, succ);
|
2014-07-21 22:54:28 -05:00
|
|
|
if kind != LoopLoop {
|
|
|
|
// If this is not a `loop` loop, then it's possible we bypass
|
|
|
|
// the body altogether. Otherwise, the only way is via a `break`
|
|
|
|
// in the loop body.
|
2012-05-19 07:52:01 -05:00
|
|
|
self.merge_from_succ(ln, succ, first_merge);
|
|
|
|
first_merge = false;
|
|
|
|
}
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("propagate_through_loop: using id for loop body {} {}",
|
2014-06-21 05:39:03 -05:00
|
|
|
expr.id, block_to_string(body));
|
2012-10-18 14:20:18 -05:00
|
|
|
|
2014-07-21 22:54:28 -05:00
|
|
|
let cond_ln = match kind {
|
|
|
|
LoopLoop | ForLoop => ln,
|
|
|
|
WhileLoop(ref cond) => self.propagate_through_expr(&**cond, ln),
|
|
|
|
};
|
2014-03-06 07:46:26 -06:00
|
|
|
let body_ln = self.with_loop_nodes(expr.id, succ, ln, |this| {
|
|
|
|
this.propagate_through_block(body, cond_ln)
|
2012-06-26 15:55:56 -05:00
|
|
|
});
|
2012-05-19 07:52:01 -05:00
|
|
|
|
|
|
|
// repeat until fixed point is reached:
|
|
|
|
while self.merge_from_succ(ln, body_ln, first_merge) {
|
|
|
|
first_merge = false;
|
2014-07-21 22:54:28 -05:00
|
|
|
|
|
|
|
let new_cond_ln = match kind {
|
|
|
|
LoopLoop | ForLoop => ln,
|
|
|
|
WhileLoop(ref cond) => {
|
|
|
|
self.propagate_through_expr(&**cond, ln)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
assert!(cond_ln == new_cond_ln);
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(body_ln == self.with_loop_nodes(expr.id, succ, ln,
|
2014-03-06 07:46:26 -06:00
|
|
|
|this| this.propagate_through_block(body, cond_ln)));
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
cond_ln
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn with_loop_nodes<R>(&mut self,
|
|
|
|
loop_node_id: NodeId,
|
|
|
|
break_ln: LiveNode,
|
|
|
|
cont_ln: LiveNode,
|
|
|
|
f: |&mut Liveness<'a>| -> R)
|
|
|
|
-> R {
|
2013-11-01 20:06:31 -05:00
|
|
|
debug!("with_loop_nodes: {} {}", loop_node_id, break_ln.get());
|
2014-03-06 07:46:26 -06:00
|
|
|
self.loop_scope.push(loop_node_id);
|
|
|
|
self.break_ln.insert(loop_node_id, break_ln);
|
|
|
|
self.cont_ln.insert(loop_node_id, cont_ln);
|
|
|
|
let r = f(self);
|
|
|
|
self.loop_scope.pop();
|
2013-02-15 03:14:34 -06:00
|
|
|
r
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// _______________________________________________________________________
|
|
|
|
// Checking for error conditions
|
|
|
|
|
2014-01-06 06:00:46 -06:00
|
|
|
fn check_local(this: &mut Liveness, local: &Local) {
|
2013-07-19 00:38:55 -05:00
|
|
|
match local.init {
|
2014-03-06 08:58:34 -06:00
|
|
|
Some(_) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
this.warn_about_unused_or_dead_vars_in_pat(&*local.pat);
|
2014-03-06 08:58:34 -06:00
|
|
|
},
|
|
|
|
None => {
|
2014-05-16 12:15:33 -05:00
|
|
|
this.pat_bindings(&*local.pat, |this, ln, var, sp, id| {
|
2014-03-06 08:58:34 -06:00
|
|
|
this.warn_about_unused(sp, id, ln, var);
|
|
|
|
})
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_local(this, local, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
fn check_arm(this: &mut Liveness, arm: &Arm) {
|
2014-03-06 07:46:26 -06:00
|
|
|
this.arm_pats_bindings(arm.pats.as_slice(), |this, ln, var, sp, id| {
|
2013-05-10 17:15:06 -05:00
|
|
|
this.warn_about_unused(sp, id, ln, var);
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_arm(this, arm, ());
|
2012-08-24 13:04:07 -05:00
|
|
|
}
|
|
|
|
|
2014-01-06 06:00:46 -06:00
|
|
|
fn check_expr(this: &mut Liveness, expr: &Expr) {
|
2013-03-20 00:17:42 -05:00
|
|
|
match expr.node {
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprAssign(ref l, ref r) => {
|
|
|
|
this.check_lvalue(&**l);
|
|
|
|
this.visit_expr(&**r, ());
|
2012-05-23 22:53:49 -05:00
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_expr(this, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
ExprAssignOp(_, ref l, _) => {
|
|
|
|
this.check_lvalue(&**l);
|
2012-05-23 22:53:49 -05:00
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_expr(this, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprInlineAsm(ref ia) => {
|
2014-05-16 12:15:33 -05:00
|
|
|
for &(_, ref input) in ia.inputs.iter() {
|
|
|
|
this.visit_expr(&**input, ());
|
2013-03-12 19:53:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Output operands must be lvalues
|
2014-05-16 12:15:33 -05:00
|
|
|
for &(_, ref out) in ia.outputs.iter() {
|
|
|
|
this.check_lvalue(&**out);
|
|
|
|
this.visit_expr(&**out, ());
|
2013-03-12 19:53:25 -05:00
|
|
|
}
|
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_expr(this, expr, ());
|
2013-03-12 19:53:25 -05:00
|
|
|
}
|
|
|
|
|
2012-05-19 07:52:01 -05:00
|
|
|
// no correctness conditions related to liveness
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprCall(..) | ExprMethodCall(..) | ExprIf(..) | ExprMatch(..) |
|
|
|
|
ExprWhile(..) | ExprLoop(..) | ExprIndex(..) | ExprField(..) |
|
2014-03-09 00:36:10 -06:00
|
|
|
ExprVstore(..) | ExprVec(..) | ExprTup(..) |
|
2014-01-27 18:03:32 -06:00
|
|
|
ExprBinary(..) |
|
2013-11-28 14:22:53 -06:00
|
|
|
ExprCast(..) | ExprUnary(..) | ExprRet(..) | ExprBreak(..) |
|
|
|
|
ExprAgain(..) | ExprLit(_) | ExprBlock(..) |
|
|
|
|
ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) |
|
2014-05-29 00:26:56 -05:00
|
|
|
ExprParen(..) | ExprFnBlock(..) | ExprProc(..) | ExprUnboxedFn(..) |
|
2014-07-21 22:54:28 -05:00
|
|
|
ExprPath(..) | ExprBox(..) | ExprForLoop(..) => {
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_expr(this, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 04:01:09 -05:00
|
|
|
fn check_fn(_v: &Liveness,
|
2014-01-09 07:05:33 -06:00
|
|
|
_fk: &FnKind,
|
|
|
|
_decl: &FnDecl,
|
2013-07-19 20:42:11 -05:00
|
|
|
_body: &Block,
|
2013-08-31 11:13:04 -05:00
|
|
|
_sp: Span,
|
2013-09-25 04:01:09 -05:00
|
|
|
_id: NodeId) {
|
2012-05-19 07:52:01 -05:00
|
|
|
// do not check contents of nested fns
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:07:47 -06:00
|
|
|
impl<'a> Liveness<'a> {
|
2014-03-06 07:46:26 -06:00
|
|
|
fn check_ret(&self,
|
|
|
|
id: NodeId,
|
|
|
|
sp: Span,
|
|
|
|
_fk: &FnKind,
|
|
|
|
entry_ln: LiveNode,
|
|
|
|
body: &Block) {
|
2012-08-23 11:14:19 -05:00
|
|
|
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
|
2012-05-19 07:52:01 -05:00
|
|
|
// if no_ret_var is live, then we fall off the end of the
|
|
|
|
// function without any kind of return expression:
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
|
2012-05-19 07:52:01 -05:00
|
|
|
if ty::type_is_nil(t_ret) {
|
|
|
|
// for nil return types, it is ok to not return a value expl.
|
|
|
|
} else if ty::type_is_bot(t_ret) {
|
|
|
|
// for bot return types, not ok. Function should fail.
|
2014-03-06 07:46:26 -06:00
|
|
|
self.ir.tcx.sess.span_err(
|
2013-04-30 11:47:52 -05:00
|
|
|
sp, "some control paths may return");
|
2012-05-19 07:52:01 -05:00
|
|
|
} else {
|
2014-01-11 15:29:46 -06:00
|
|
|
let ends_with_stmt = match body.expr {
|
|
|
|
None if body.stmts.len() > 0 =>
|
2013-12-23 08:08:23 -06:00
|
|
|
match body.stmts.last().unwrap().node {
|
2014-05-16 12:15:33 -05:00
|
|
|
StmtSemi(ref e, _) => {
|
|
|
|
let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
|
2014-01-11 15:29:46 -06:00
|
|
|
ty::get(t_stmt).sty == ty::get(t_ret).sty
|
|
|
|
},
|
|
|
|
_ => false
|
|
|
|
},
|
|
|
|
_ => false
|
|
|
|
};
|
2014-07-04 16:25:27 -05:00
|
|
|
self.ir.tcx.sess.span_err(
|
|
|
|
sp, "not all control paths return a value");
|
2014-01-11 15:29:46 -06:00
|
|
|
if ends_with_stmt {
|
2013-12-23 08:08:23 -06:00
|
|
|
let last_stmt = body.stmts.last().unwrap();
|
2014-04-09 15:42:25 -05:00
|
|
|
let original_span = original_sp(last_stmt.span, sp);
|
2014-01-11 15:29:46 -06:00
|
|
|
let span_semicolon = Span {
|
2014-04-09 15:42:25 -05:00
|
|
|
lo: original_span.hi - BytePos(1),
|
|
|
|
hi: original_span.hi,
|
|
|
|
expn_info: original_span.expn_info
|
2014-01-11 15:29:46 -06:00
|
|
|
};
|
2014-03-06 07:46:26 -06:00
|
|
|
self.ir.tcx.sess.span_note(
|
2014-01-11 15:29:46 -06:00
|
|
|
span_semicolon, "consider removing this semicolon:");
|
|
|
|
}
|
|
|
|
}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn check_lvalue(&mut self, expr: &Expr) {
|
2012-08-06 14:34:08 -05:00
|
|
|
match expr.node {
|
2013-09-01 20:45:37 -05:00
|
|
|
ExprPath(_) => {
|
2014-03-20 21:49:20 -05:00
|
|
|
match self.ir.tcx.def_map.borrow().get_copy(&expr.id) {
|
2013-09-01 20:45:37 -05:00
|
|
|
DefLocal(nid, _) => {
|
2013-04-12 00:09:54 -05:00
|
|
|
// Assignment to an immutable variable or argument: only legal
|
|
|
|
// if there is no later assignment. If this local is actually
|
|
|
|
// mutable, then check for a reassignment to flag the mutability
|
|
|
|
// as being used.
|
2012-08-23 11:14:19 -05:00
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
let var = self.variable(nid, expr.span);
|
2013-04-09 12:16:27 -05:00
|
|
|
self.warn_about_dead_assign(expr.span, expr.id, ln, var);
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
2012-08-03 21:59:04 -05:00
|
|
|
def => {
|
2014-04-21 18:21:53 -05:00
|
|
|
match moved_variable_node_id_from_def(def) {
|
2012-10-08 13:49:01 -05:00
|
|
|
Some(nid) => {
|
2012-08-23 11:14:19 -05:00
|
|
|
let ln = self.live_node(expr.id, expr.span);
|
|
|
|
let var = self.variable(nid, expr.span);
|
2013-04-09 12:16:27 -05:00
|
|
|
self.warn_about_dead_assign(expr.span, expr.id, ln, var);
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
2012-08-20 14:23:37 -05:00
|
|
|
None => {}
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-03 21:59:04 -05:00
|
|
|
_ => {
|
2012-05-19 07:52:01 -05:00
|
|
|
// For other kinds of lvalues, no checks are required,
|
|
|
|
// and any embedded expressions are actually rvalues
|
2013-09-25 04:01:09 -05:00
|
|
|
visit::walk_expr(self, expr, ());
|
2012-05-19 07:52:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
fn should_warn(&self, var: Variable) -> Option<String> {
|
2013-01-10 12:59:58 -06:00
|
|
|
let name = self.ir.variable_name(var);
|
2014-06-19 20:22:33 -05:00
|
|
|
if name.len() == 0 || name.as_bytes()[0] == ('_' as u8) {
|
2014-05-09 20:45:36 -05:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(name)
|
|
|
|
}
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn warn_about_unused_args(&self, decl: &FnDecl, entry_ln: LiveNode) {
|
2013-08-03 11:45:23 -05:00
|
|
|
for arg in decl.inputs.iter() {
|
2014-04-22 11:06:43 -05:00
|
|
|
pat_util::pat_bindings(&self.ir.tcx.def_map,
|
2014-05-16 12:15:33 -05:00
|
|
|
&*arg.pat,
|
2014-06-30 20:02:14 -05:00
|
|
|
|_bm, p_id, sp, path1| {
|
2012-11-06 20:41:06 -06:00
|
|
|
let var = self.variable(p_id, sp);
|
2014-01-27 06:18:36 -06:00
|
|
|
// Ignore unused self.
|
2014-06-30 20:02:14 -05:00
|
|
|
let ident = path1.node;
|
2014-01-27 06:18:36 -06:00
|
|
|
if ident.name != special_idents::self_.name {
|
|
|
|
self.warn_about_unused(sp, p_id, entry_ln, var);
|
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat: &Pat) {
|
|
|
|
self.pat_bindings(pat, |this, ln, var, sp, id| {
|
|
|
|
if !this.warn_about_unused(sp, id, ln, var) {
|
|
|
|
this.warn_about_dead_assign(sp, id, ln, var);
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
})
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn warn_about_unused(&self,
|
|
|
|
sp: Span,
|
|
|
|
id: NodeId,
|
|
|
|
ln: LiveNode,
|
|
|
|
var: Variable)
|
|
|
|
-> bool {
|
2012-08-23 11:14:19 -05:00
|
|
|
if !self.used_on_entry(ln, var) {
|
2013-06-10 16:50:12 -05:00
|
|
|
let r = self.should_warn(var);
|
2013-08-03 11:45:23 -05:00
|
|
|
for name in r.iter() {
|
2012-05-25 00:44:30 -05:00
|
|
|
|
|
|
|
// annoying: for parameters in funcs like `fn(x: int)
|
|
|
|
// {ret}`, there is only one node, so asking about
|
|
|
|
// assigned_on_exit() is not meaningful.
|
|
|
|
let is_assigned = if ln == self.s.exit_ln {
|
|
|
|
false
|
|
|
|
} else {
|
2012-08-23 11:14:19 -05:00
|
|
|
self.assigned_on_exit(ln, var).is_some()
|
2012-05-25 00:44:30 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
if is_assigned {
|
2014-06-13 15:13:05 -05:00
|
|
|
self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
|
2014-05-27 22:44:58 -05:00
|
|
|
format!("variable `{}` is assigned to, but never used",
|
|
|
|
*name));
|
2012-05-25 00:44:30 -05:00
|
|
|
} else {
|
2014-06-13 15:13:05 -05:00
|
|
|
self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp,
|
2014-05-27 22:44:58 -05:00
|
|
|
format!("unused variable: `{}`", *name));
|
2012-05-25 00:44:30 -05:00
|
|
|
}
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
2013-06-10 16:50:12 -05:00
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 07:46:26 -06:00
|
|
|
fn warn_about_dead_assign(&self,
|
|
|
|
sp: Span,
|
|
|
|
id: NodeId,
|
|
|
|
ln: LiveNode,
|
|
|
|
var: Variable) {
|
2012-08-23 11:14:19 -05:00
|
|
|
if self.live_on_exit(ln, var).is_none() {
|
2013-06-10 16:50:12 -05:00
|
|
|
let r = self.should_warn(var);
|
2013-08-03 11:45:23 -05:00
|
|
|
for name in r.iter() {
|
2014-06-13 15:13:05 -05:00
|
|
|
self.ir.tcx.sess.add_lint(lint::builtin::DEAD_ASSIGNMENT, id, sp,
|
2014-05-27 22:44:58 -05:00
|
|
|
format!("value assigned to `{}` is never read", *name));
|
2012-05-23 22:53:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-06-06 16:19:52 -05:00
|
|
|
}
|