748 lines
23 KiB
Rust
748 lines
23 KiB
Rust
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
// 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.
|
|
|
|
/*!
|
|
|
|
Data structures used for tracking moves. Please see the extensive
|
|
comments in the section "Moves and initialization" and in `doc.rs`.
|
|
|
|
*/
|
|
|
|
use std::cell::RefCell;
|
|
use std::hashmap::{HashMap, HashSet};
|
|
use std::uint;
|
|
use middle::borrowck::*;
|
|
use middle::dataflow::DataFlowContext;
|
|
use middle::dataflow::DataFlowOperator;
|
|
use middle::ty;
|
|
use middle::typeck;
|
|
use syntax::ast;
|
|
use syntax::ast_util;
|
|
use syntax::codemap::Span;
|
|
use syntax::opt_vec::OptVec;
|
|
use syntax::opt_vec;
|
|
use util::ppaux::Repr;
|
|
|
|
pub struct MoveData {
|
|
/// Move paths. See section "Move paths" in `doc.rs`.
|
|
paths: RefCell<~[MovePath]>,
|
|
|
|
/// Cache of loan path to move path index, for easy lookup.
|
|
path_map: RefCell<HashMap<@LoanPath, MovePathIndex>>,
|
|
|
|
/// Each move or uninitialized variable gets an entry here.
|
|
moves: RefCell<~[Move]>,
|
|
|
|
/// Assignments to a variable, like `x = foo`. These are assigned
|
|
/// bits for dataflow, since we must track them to ensure that
|
|
/// immutable variables are assigned at most once along each path.
|
|
var_assignments: RefCell<~[Assignment]>,
|
|
|
|
/// Assignments to a path, like `x.f = foo`. These are not
|
|
/// assigned dataflow bits, but we track them because they still
|
|
/// kill move bits.
|
|
path_assignments: RefCell<~[Assignment]>,
|
|
assignee_ids: RefCell<HashSet<ast::NodeId>>,
|
|
}
|
|
|
|
pub struct FlowedMoveData {
|
|
move_data: MoveData,
|
|
|
|
dfcx_moves: MoveDataFlow,
|
|
|
|
// We could (and maybe should, for efficiency) combine both move
|
|
// and assign data flow into one, but this way it's easier to
|
|
// distinguish the bits that correspond to moves and assignments.
|
|
dfcx_assign: AssignDataFlow
|
|
}
|
|
|
|
/// Index into `MoveData.paths`, used like a pointer
|
|
#[deriving(Eq)]
|
|
pub struct MovePathIndex(uint);
|
|
|
|
impl MovePathIndex {
|
|
fn get(&self) -> uint {
|
|
let MovePathIndex(v) = *self; v
|
|
}
|
|
}
|
|
|
|
impl Clone for MovePathIndex {
|
|
fn clone(&self) -> MovePathIndex {
|
|
MovePathIndex(self.get())
|
|
}
|
|
}
|
|
|
|
static InvalidMovePathIndex: MovePathIndex =
|
|
MovePathIndex(uint::MAX);
|
|
|
|
/// Index into `MoveData.moves`, used like a pointer
|
|
#[deriving(Eq)]
|
|
pub struct MoveIndex(uint);
|
|
|
|
impl MoveIndex {
|
|
fn get(&self) -> uint {
|
|
let MoveIndex(v) = *self; v
|
|
}
|
|
}
|
|
|
|
static InvalidMoveIndex: MoveIndex =
|
|
MoveIndex(uint::MAX);
|
|
|
|
pub struct MovePath {
|
|
/// Loan path corresponding to this move path
|
|
loan_path: @LoanPath,
|
|
|
|
/// Parent pointer, `InvalidMovePathIndex` if root
|
|
parent: MovePathIndex,
|
|
|
|
/// Head of linked list of moves to this path,
|
|
/// `InvalidMoveIndex` if not moved
|
|
first_move: MoveIndex,
|
|
|
|
/// First node in linked list of children, `InvalidMovePathIndex` if leaf
|
|
first_child: MovePathIndex,
|
|
|
|
/// Next node in linked list of parent's children (siblings),
|
|
/// `InvalidMovePathIndex` if none.
|
|
next_sibling: MovePathIndex,
|
|
}
|
|
|
|
pub enum MoveKind {
|
|
Declared, // When declared, variables start out "moved".
|
|
MoveExpr, // Expression or binding that moves a variable
|
|
MovePat, // By-move binding
|
|
Captured // Closure creation that moves a value
|
|
}
|
|
|
|
pub struct Move {
|
|
/// Path being moved.
|
|
path: MovePathIndex,
|
|
|
|
/// id of node that is doing the move.
|
|
id: ast::NodeId,
|
|
|
|
/// Kind of move, for error messages.
|
|
kind: MoveKind,
|
|
|
|
/// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
|
|
next_move: MoveIndex
|
|
}
|
|
|
|
pub struct Assignment {
|
|
/// Path being assigned.
|
|
path: MovePathIndex,
|
|
|
|
/// id where assignment occurs
|
|
id: ast::NodeId,
|
|
|
|
/// span of node where assignment occurs
|
|
span: Span,
|
|
}
|
|
|
|
pub struct MoveDataFlowOperator;
|
|
|
|
/// FIXME(pcwalton): Should just be #[deriving(Clone)], but that doesn't work
|
|
/// yet on unit structs.
|
|
impl Clone for MoveDataFlowOperator {
|
|
fn clone(&self) -> MoveDataFlowOperator {
|
|
MoveDataFlowOperator
|
|
}
|
|
}
|
|
|
|
pub type MoveDataFlow = DataFlowContext<MoveDataFlowOperator>;
|
|
|
|
pub struct AssignDataFlowOperator;
|
|
|
|
/// FIXME(pcwalton): Should just be #[deriving(Clone)], but that doesn't work
|
|
/// yet on unit structs.
|
|
impl Clone for AssignDataFlowOperator {
|
|
fn clone(&self) -> AssignDataFlowOperator {
|
|
AssignDataFlowOperator
|
|
}
|
|
}
|
|
|
|
pub type AssignDataFlow = DataFlowContext<AssignDataFlowOperator>;
|
|
|
|
impl MoveData {
|
|
pub fn new() -> MoveData {
|
|
MoveData {
|
|
paths: RefCell::new(~[]),
|
|
path_map: RefCell::new(HashMap::new()),
|
|
moves: RefCell::new(~[]),
|
|
path_assignments: RefCell::new(~[]),
|
|
var_assignments: RefCell::new(~[]),
|
|
assignee_ids: RefCell::new(HashSet::new()),
|
|
}
|
|
}
|
|
|
|
fn path_loan_path(&self, index: MovePathIndex) -> @LoanPath {
|
|
let paths = self.paths.borrow();
|
|
paths.get()[index.get()].loan_path
|
|
}
|
|
|
|
fn path_parent(&self, index: MovePathIndex) -> MovePathIndex {
|
|
let paths = self.paths.borrow();
|
|
paths.get()[index.get()].parent
|
|
}
|
|
|
|
fn path_first_move(&self, index: MovePathIndex) -> MoveIndex {
|
|
let paths = self.paths.borrow();
|
|
paths.get()[index.get()].first_move
|
|
}
|
|
|
|
fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex {
|
|
let paths = self.paths.borrow();
|
|
paths.get()[index.get()].first_child
|
|
}
|
|
|
|
fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex {
|
|
let paths = self.paths.borrow();
|
|
paths.get()[index.get()].next_sibling
|
|
}
|
|
|
|
fn set_path_first_move(&self,
|
|
index: MovePathIndex,
|
|
first_move: MoveIndex) {
|
|
let mut paths = self.paths.borrow_mut();
|
|
paths.get()[index.get()].first_move = first_move
|
|
}
|
|
|
|
fn set_path_first_child(&self,
|
|
index: MovePathIndex,
|
|
first_child: MovePathIndex) {
|
|
let mut paths = self.paths.borrow_mut();
|
|
paths.get()[index.get()].first_child = first_child
|
|
}
|
|
|
|
fn move_next_move(&self, index: MoveIndex) -> MoveIndex {
|
|
//! Type safe indexing operator
|
|
let moves = self.moves.borrow();
|
|
moves.get()[index.get()].next_move
|
|
}
|
|
|
|
fn is_var_path(&self, index: MovePathIndex) -> bool {
|
|
//! True if `index` refers to a variable
|
|
self.path_parent(index) == InvalidMovePathIndex
|
|
}
|
|
|
|
pub fn move_path(&self,
|
|
tcx: ty::ctxt,
|
|
lp: @LoanPath) -> MovePathIndex {
|
|
/*!
|
|
* Returns the existing move path index for `lp`, if any,
|
|
* and otherwise adds a new index for `lp` and any of its
|
|
* base paths that do not yet have an index.
|
|
*/
|
|
|
|
{
|
|
let path_map = self.path_map.borrow();
|
|
match path_map.get().find(&lp) {
|
|
Some(&index) => {
|
|
return index;
|
|
}
|
|
None => {}
|
|
}
|
|
}
|
|
|
|
let index = match *lp {
|
|
LpVar(..) => {
|
|
let mut paths = self.paths.borrow_mut();
|
|
let index = MovePathIndex(paths.get().len());
|
|
|
|
paths.get().push(MovePath {
|
|
loan_path: lp,
|
|
parent: InvalidMovePathIndex,
|
|
first_move: InvalidMoveIndex,
|
|
first_child: InvalidMovePathIndex,
|
|
next_sibling: InvalidMovePathIndex,
|
|
});
|
|
|
|
index
|
|
}
|
|
|
|
LpExtend(base, _, _) => {
|
|
let parent_index = self.move_path(tcx, base);
|
|
|
|
let index = {
|
|
let paths = self.paths.borrow();
|
|
MovePathIndex(paths.get().len())
|
|
};
|
|
|
|
let next_sibling = self.path_first_child(parent_index);
|
|
self.set_path_first_child(parent_index, index);
|
|
|
|
{
|
|
let mut paths = self.paths.borrow_mut();
|
|
paths.get().push(MovePath {
|
|
loan_path: lp,
|
|
parent: parent_index,
|
|
first_move: InvalidMoveIndex,
|
|
first_child: InvalidMovePathIndex,
|
|
next_sibling: next_sibling,
|
|
});
|
|
}
|
|
|
|
index
|
|
}
|
|
};
|
|
|
|
debug!("move_path(lp={}, index={:?})",
|
|
lp.repr(tcx),
|
|
index);
|
|
|
|
let paths = self.paths.borrow();
|
|
assert_eq!(index.get(), paths.get().len() - 1);
|
|
|
|
let mut path_map = self.path_map.borrow_mut();
|
|
path_map.get().insert(lp, index);
|
|
return index;
|
|
}
|
|
|
|
fn existing_move_path(&self,
|
|
lp: @LoanPath)
|
|
-> Option<MovePathIndex> {
|
|
let path_map = self.path_map.borrow();
|
|
path_map.get().find_copy(&lp)
|
|
}
|
|
|
|
fn existing_base_paths(&self,
|
|
lp: @LoanPath)
|
|
-> OptVec<MovePathIndex> {
|
|
let mut result = opt_vec::Empty;
|
|
self.add_existing_base_paths(lp, &mut result);
|
|
result
|
|
}
|
|
|
|
fn add_existing_base_paths(&self,
|
|
lp: @LoanPath,
|
|
result: &mut OptVec<MovePathIndex>) {
|
|
/*!
|
|
* Adds any existing move path indices for `lp` and any base
|
|
* paths of `lp` to `result`, but does not add new move paths
|
|
*/
|
|
|
|
let index_opt = {
|
|
let path_map = self.path_map.borrow();
|
|
path_map.get().find_copy(&lp)
|
|
};
|
|
match index_opt {
|
|
Some(index) => {
|
|
self.each_base_path(index, |p| {
|
|
result.push(p);
|
|
true
|
|
});
|
|
}
|
|
None => {
|
|
match *lp {
|
|
LpVar(..) => { }
|
|
LpExtend(b, _, _) => {
|
|
self.add_existing_base_paths(b, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
pub fn add_move(&self,
|
|
tcx: ty::ctxt,
|
|
lp: @LoanPath,
|
|
id: ast::NodeId,
|
|
kind: MoveKind) {
|
|
/*!
|
|
* Adds a new move entry for a move of `lp` that occurs at
|
|
* location `id` with kind `kind`.
|
|
*/
|
|
|
|
debug!("add_move(lp={}, id={:?}, kind={:?})",
|
|
lp.repr(tcx),
|
|
id,
|
|
kind);
|
|
|
|
let path_index = self.move_path(tcx, lp);
|
|
let move_index = {
|
|
let moves = self.moves.borrow();
|
|
MoveIndex(moves.get().len())
|
|
};
|
|
|
|
let next_move = self.path_first_move(path_index);
|
|
self.set_path_first_move(path_index, move_index);
|
|
|
|
{
|
|
let mut moves = self.moves.borrow_mut();
|
|
moves.get().push(Move {
|
|
path: path_index,
|
|
id: id,
|
|
kind: kind,
|
|
next_move: next_move
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn add_assignment(&self,
|
|
tcx: ty::ctxt,
|
|
lp: @LoanPath,
|
|
assign_id: ast::NodeId,
|
|
span: Span,
|
|
assignee_id: ast::NodeId) {
|
|
/*!
|
|
* Adds a new record for an assignment to `lp` that occurs at
|
|
* location `id` with the given `span`.
|
|
*/
|
|
|
|
debug!("add_assignment(lp={}, assign_id={:?}, assignee_id={:?}",
|
|
lp.repr(tcx), assign_id, assignee_id);
|
|
|
|
let path_index = self.move_path(tcx, lp);
|
|
|
|
{
|
|
let mut assignee_ids = self.assignee_ids.borrow_mut();
|
|
assignee_ids.get().insert(assignee_id);
|
|
}
|
|
|
|
let assignment = Assignment {
|
|
path: path_index,
|
|
id: assign_id,
|
|
span: span,
|
|
};
|
|
|
|
if self.is_var_path(path_index) {
|
|
let mut var_assignments = self.var_assignments.borrow_mut();
|
|
debug!("add_assignment[var](lp={}, assignment={}, path_index={:?})",
|
|
lp.repr(tcx), var_assignments.get().len(), path_index);
|
|
|
|
var_assignments.get().push(assignment);
|
|
} else {
|
|
debug!("add_assignment[path](lp={}, path_index={:?})",
|
|
lp.repr(tcx), path_index);
|
|
|
|
{
|
|
let mut path_assignments = self.path_assignments.borrow_mut();
|
|
path_assignments.get().push(assignment);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn add_gen_kills(&self,
|
|
tcx: ty::ctxt,
|
|
dfcx_moves: &mut MoveDataFlow,
|
|
dfcx_assign: &mut AssignDataFlow) {
|
|
/*!
|
|
* Adds the gen/kills for the various moves and
|
|
* assignments into the provided data flow contexts.
|
|
* Moves are generated by moves and killed by assignments and
|
|
* scoping. Assignments are generated by assignment to variables and
|
|
* killed by scoping. See `doc.rs` for more details.
|
|
*/
|
|
|
|
{
|
|
let moves = self.moves.borrow();
|
|
for (i, move) in moves.get().iter().enumerate() {
|
|
dfcx_moves.add_gen(move.id, i);
|
|
}
|
|
}
|
|
|
|
{
|
|
let var_assignments = self.var_assignments.borrow();
|
|
for (i, assignment) in var_assignments.get().iter().enumerate() {
|
|
dfcx_assign.add_gen(assignment.id, i);
|
|
self.kill_moves(assignment.path, assignment.id, dfcx_moves);
|
|
}
|
|
}
|
|
|
|
{
|
|
let path_assignments = self.path_assignments.borrow();
|
|
for assignment in path_assignments.get().iter() {
|
|
self.kill_moves(assignment.path, assignment.id, dfcx_moves);
|
|
}
|
|
}
|
|
|
|
// Kill all moves related to a variable `x` when it goes out
|
|
// of scope:
|
|
{
|
|
let paths = self.paths.borrow();
|
|
for path in paths.get().iter() {
|
|
match *path.loan_path {
|
|
LpVar(id) => {
|
|
let kill_id = tcx.region_maps.var_scope(id);
|
|
let path = {
|
|
let path_map = self.path_map.borrow();
|
|
*path_map.get().get(&path.loan_path)
|
|
};
|
|
self.kill_moves(path, kill_id, dfcx_moves);
|
|
}
|
|
LpExtend(..) => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kill all assignments when the variable goes out of scope:
|
|
{
|
|
let var_assignments = self.var_assignments.borrow();
|
|
for (assignment_index, assignment) in
|
|
var_assignments.get().iter().enumerate() {
|
|
match *self.path_loan_path(assignment.path) {
|
|
LpVar(id) => {
|
|
let kill_id = tcx.region_maps.var_scope(id);
|
|
dfcx_assign.add_kill(kill_id, assignment_index);
|
|
}
|
|
LpExtend(..) => {
|
|
tcx.sess.bug("Var assignment for non var path");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn each_base_path(&self, index: MovePathIndex, f: |MovePathIndex| -> bool)
|
|
-> bool {
|
|
let mut p = index;
|
|
while p != InvalidMovePathIndex {
|
|
if !f(p) {
|
|
return false;
|
|
}
|
|
p = self.path_parent(p);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
fn each_extending_path(&self,
|
|
index: MovePathIndex,
|
|
f: |MovePathIndex| -> bool)
|
|
-> bool {
|
|
if !f(index) {
|
|
return false;
|
|
}
|
|
|
|
let mut p = self.path_first_child(index);
|
|
while p != InvalidMovePathIndex {
|
|
if !self.each_extending_path(p, |x| f(x)) {
|
|
return false;
|
|
}
|
|
p = self.path_next_sibling(p);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
fn each_applicable_move(&self,
|
|
index0: MovePathIndex,
|
|
f: |MoveIndex| -> bool)
|
|
-> bool {
|
|
let mut ret = true;
|
|
self.each_extending_path(index0, |index| {
|
|
let mut p = self.path_first_move(index);
|
|
while p != InvalidMoveIndex {
|
|
if !f(p) {
|
|
ret = false;
|
|
break;
|
|
}
|
|
p = self.move_next_move(p);
|
|
}
|
|
ret
|
|
});
|
|
ret
|
|
}
|
|
|
|
fn kill_moves(&self,
|
|
path: MovePathIndex,
|
|
kill_id: ast::NodeId,
|
|
dfcx_moves: &mut MoveDataFlow) {
|
|
self.each_applicable_move(path, |move_index| {
|
|
dfcx_moves.add_kill(kill_id, move_index.get());
|
|
true
|
|
});
|
|
}
|
|
}
|
|
|
|
impl FlowedMoveData {
|
|
pub fn new(move_data: MoveData,
|
|
tcx: ty::ctxt,
|
|
method_map: typeck::method_map,
|
|
id_range: ast_util::IdRange,
|
|
body: &ast::Block)
|
|
-> FlowedMoveData {
|
|
let mut dfcx_moves = {
|
|
let moves = move_data.moves.borrow();
|
|
DataFlowContext::new(tcx,
|
|
method_map,
|
|
MoveDataFlowOperator,
|
|
id_range,
|
|
moves.get().len())
|
|
};
|
|
let mut dfcx_assign = {
|
|
let var_assignments = move_data.var_assignments.borrow();
|
|
DataFlowContext::new(tcx,
|
|
method_map,
|
|
AssignDataFlowOperator,
|
|
id_range,
|
|
var_assignments.get().len())
|
|
};
|
|
move_data.add_gen_kills(tcx, &mut dfcx_moves, &mut dfcx_assign);
|
|
dfcx_moves.propagate(body);
|
|
dfcx_assign.propagate(body);
|
|
FlowedMoveData {
|
|
move_data: move_data,
|
|
dfcx_moves: dfcx_moves,
|
|
dfcx_assign: dfcx_assign,
|
|
}
|
|
}
|
|
|
|
pub fn each_path_moved_by(&self,
|
|
id: ast::NodeId,
|
|
f: |&Move, @LoanPath| -> bool)
|
|
-> bool {
|
|
/*!
|
|
* Iterates through each path moved by `id`
|
|
*/
|
|
|
|
self.dfcx_moves.each_gen_bit_frozen(id, |index| {
|
|
let moves = self.move_data.moves.borrow();
|
|
let move = &moves.get()[index];
|
|
let moved_path = move.path;
|
|
f(move, self.move_data.path_loan_path(moved_path))
|
|
})
|
|
}
|
|
|
|
pub fn each_move_of(&self,
|
|
id: ast::NodeId,
|
|
loan_path: @LoanPath,
|
|
f: |&Move, @LoanPath| -> bool)
|
|
-> bool {
|
|
/*!
|
|
* Iterates through each move of `loan_path` (or some base path
|
|
* of `loan_path`) that *may* have occurred on entry to `id` without
|
|
* an intervening assignment. In other words, any moves that
|
|
* would invalidate a reference to `loan_path` at location `id`.
|
|
*/
|
|
|
|
// Bad scenarios:
|
|
//
|
|
// 1. Move of `a.b.c`, use of `a.b.c`
|
|
// 2. Move of `a.b.c`, use of `a.b.c.d`
|
|
// 3. Move of `a.b.c`, use of `a` or `a.b`
|
|
//
|
|
// OK scenario:
|
|
//
|
|
// 4. move of `a.b.c`, use of `a.b.d`
|
|
|
|
let base_indices = self.move_data.existing_base_paths(loan_path);
|
|
if base_indices.is_empty() {
|
|
return true;
|
|
}
|
|
|
|
let opt_loan_path_index = self.move_data.existing_move_path(loan_path);
|
|
|
|
let mut ret = true;
|
|
|
|
self.dfcx_moves.each_bit_on_entry_frozen(id, |index| {
|
|
let moves = self.move_data.moves.borrow();
|
|
let move = &moves.get()[index];
|
|
let moved_path = move.path;
|
|
if base_indices.iter().any(|x| x == &moved_path) {
|
|
// Scenario 1 or 2: `loan_path` or some base path of
|
|
// `loan_path` was moved.
|
|
if !f(move, self.move_data.path_loan_path(moved_path)) {
|
|
ret = false;
|
|
}
|
|
} else {
|
|
for &loan_path_index in opt_loan_path_index.iter() {
|
|
let cont = self.move_data.each_base_path(moved_path, |p| {
|
|
if p == loan_path_index {
|
|
// Scenario 3: some extension of `loan_path`
|
|
// was moved
|
|
f(move, self.move_data.path_loan_path(moved_path))
|
|
} else {
|
|
true
|
|
}
|
|
});
|
|
if !cont { ret = false; break }
|
|
}
|
|
}
|
|
ret
|
|
})
|
|
}
|
|
|
|
pub fn is_assignee(&self,
|
|
id: ast::NodeId)
|
|
-> bool {
|
|
//! True if `id` is the id of the LHS of an assignment
|
|
|
|
let assignee_ids = self.move_data.assignee_ids.borrow();
|
|
assignee_ids.get().iter().any(|x| x == &id)
|
|
}
|
|
|
|
pub fn each_assignment_of(&self,
|
|
id: ast::NodeId,
|
|
loan_path: @LoanPath,
|
|
f: |&Assignment| -> bool)
|
|
-> bool {
|
|
/*!
|
|
* Iterates through every assignment to `loan_path` that
|
|
* may have occurred on entry to `id`. `loan_path` must be
|
|
* a single variable.
|
|
*/
|
|
|
|
let loan_path_index = {
|
|
match self.move_data.existing_move_path(loan_path) {
|
|
Some(i) => i,
|
|
None => {
|
|
// if there were any assignments, it'd have an index
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
|
|
self.dfcx_assign.each_bit_on_entry_frozen(id, |index| {
|
|
let var_assignments = self.move_data.var_assignments.borrow();
|
|
let assignment = &var_assignments.get()[index];
|
|
if assignment.path == loan_path_index && !f(assignment) {
|
|
false
|
|
} else {
|
|
true
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl DataFlowOperator for MoveDataFlowOperator {
|
|
#[inline]
|
|
fn initial_value(&self) -> bool {
|
|
false // no loans in scope by default
|
|
}
|
|
|
|
#[inline]
|
|
fn join(&self, succ: uint, pred: uint) -> uint {
|
|
succ | pred // moves from both preds are in scope
|
|
}
|
|
|
|
#[inline]
|
|
fn walk_closures(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl DataFlowOperator for AssignDataFlowOperator {
|
|
#[inline]
|
|
fn initial_value(&self) -> bool {
|
|
false // no assignments in scope by default
|
|
}
|
|
|
|
#[inline]
|
|
fn join(&self, succ: uint, pred: uint) -> uint {
|
|
succ | pred // moves from both preds are in scope
|
|
}
|
|
|
|
#[inline]
|
|
fn walk_closures(&self) -> bool {
|
|
true
|
|
}
|
|
}
|