Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| // 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" in `README.md`. | |
| pub use self::MoveKind::*; | |
| use borrowck::*; | |
| use rustc::cfg; | |
| use rustc::middle::dataflow::DataFlowContext; | |
| use rustc::middle::dataflow::BitwiseOperator; | |
| use rustc::middle::dataflow::DataFlowOperator; | |
| use rustc::middle::dataflow::KillFrom; | |
| use rustc::middle::expr_use_visitor as euv; | |
| use rustc::middle::expr_use_visitor::MutateMode; | |
| use rustc::ty::TyCtxt; | |
| use rustc::util::nodemap::{FnvHashMap, NodeSet}; | |
| use std::cell::RefCell; | |
| use std::rc::Rc; | |
| use std::usize; | |
| use syntax::ast; | |
| use syntax::codemap::Span; | |
| use rustc::hir; | |
| use rustc::hir::intravisit::IdRange; | |
| #[path="fragments.rs"] | |
| pub mod fragments; | |
| pub struct MoveData<'tcx> { | |
| /// Move paths. See section "Move paths" in `README.md`. | |
| pub paths: RefCell<Vec<MovePath<'tcx>>>, | |
| /// Cache of loan path to move path index, for easy lookup. | |
| pub path_map: RefCell<FnvHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>, | |
| /// Each move or uninitialized variable gets an entry here. | |
| pub moves: RefCell<Vec<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. | |
| pub var_assignments: RefCell<Vec<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. | |
| pub path_assignments: RefCell<Vec<Assignment>>, | |
| /// Enum variant matched within a pattern on some match arm, like | |
| /// `SomeStruct{ f: Variant1(x, y) } => ...` | |
| pub variant_matches: RefCell<Vec<VariantMatch>>, | |
| /// Assignments to a variable or path, like `x = foo`, but not `x += foo`. | |
| pub assignee_ids: RefCell<NodeSet>, | |
| /// Path-fragments from moves in to or out of parts of structured data. | |
| pub fragments: RefCell<fragments::FragmentSets>, | |
| } | |
| pub struct FlowedMoveData<'a, 'tcx: 'a> { | |
| pub move_data: MoveData<'tcx>, | |
| pub dfcx_moves: MoveDataFlow<'a, 'tcx>, | |
| // 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. | |
| pub dfcx_assign: AssignDataFlow<'a, 'tcx> | |
| } | |
| /// Index into `MoveData.paths`, used like a pointer | |
| #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] | |
| pub struct MovePathIndex(usize); | |
| impl MovePathIndex { | |
| fn get(&self) -> usize { | |
| let MovePathIndex(v) = *self; v | |
| } | |
| } | |
| impl Clone for MovePathIndex { | |
| fn clone(&self) -> MovePathIndex { | |
| MovePathIndex(self.get()) | |
| } | |
| } | |
| #[allow(non_upper_case_globals)] | |
| const InvalidMovePathIndex: MovePathIndex = MovePathIndex(usize::MAX); | |
| /// Index into `MoveData.moves`, used like a pointer | |
| #[derive(Copy, Clone, PartialEq)] | |
| pub struct MoveIndex(usize); | |
| impl MoveIndex { | |
| fn get(&self) -> usize { | |
| let MoveIndex(v) = *self; v | |
| } | |
| } | |
| #[allow(non_upper_case_globals)] | |
| const InvalidMoveIndex: MoveIndex = MoveIndex(usize::MAX); | |
| pub struct MovePath<'tcx> { | |
| /// Loan path corresponding to this move path | |
| pub loan_path: Rc<LoanPath<'tcx>>, | |
| /// Parent pointer, `InvalidMovePathIndex` if root | |
| pub parent: MovePathIndex, | |
| /// Head of linked list of moves to this path, | |
| /// `InvalidMoveIndex` if not moved | |
| pub first_move: MoveIndex, | |
| /// First node in linked list of children, `InvalidMovePathIndex` if leaf | |
| pub first_child: MovePathIndex, | |
| /// Next node in linked list of parent's children (siblings), | |
| /// `InvalidMovePathIndex` if none. | |
| pub next_sibling: MovePathIndex, | |
| } | |
| #[derive(Copy, Clone, PartialEq, Debug)] | |
| 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 | |
| } | |
| #[derive(Copy, Clone)] | |
| pub struct Move { | |
| /// Path being moved. | |
| pub path: MovePathIndex, | |
| /// id of node that is doing the move. | |
| pub id: ast::NodeId, | |
| /// Kind of move, for error messages. | |
| pub kind: MoveKind, | |
| /// Next node in linked list of moves from `path`, or `InvalidMoveIndex` | |
| pub next_move: MoveIndex | |
| } | |
| #[derive(Copy, Clone)] | |
| pub struct Assignment { | |
| /// Path being assigned. | |
| pub path: MovePathIndex, | |
| /// id where assignment occurs | |
| pub id: ast::NodeId, | |
| /// span of node where assignment occurs | |
| pub span: Span, | |
| /// id for l-value expression on lhs of assignment | |
| pub assignee_id: ast::NodeId, | |
| } | |
| #[derive(Copy, Clone)] | |
| pub struct VariantMatch { | |
| /// downcast to the variant. | |
| pub path: MovePathIndex, | |
| /// path being downcast to the variant. | |
| pub base_path: MovePathIndex, | |
| /// id where variant's pattern occurs | |
| pub id: ast::NodeId, | |
| /// says if variant established by move (and why), by copy, or by borrow. | |
| pub mode: euv::MatchMode | |
| } | |
| #[derive(Clone, Copy)] | |
| pub struct MoveDataFlowOperator; | |
| pub type MoveDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, MoveDataFlowOperator>; | |
| #[derive(Clone, Copy)] | |
| pub struct AssignDataFlowOperator; | |
| pub type AssignDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, AssignDataFlowOperator>; | |
| fn loan_path_is_precise(loan_path: &LoanPath) -> bool { | |
| match loan_path.kind { | |
| LpVar(_) | LpUpvar(_) => { | |
| true | |
| } | |
| LpExtend(_, _, LpInterior(_, InteriorKind::InteriorElement(..))) => { | |
| // Paths involving element accesses a[i] do not refer to a unique | |
| // location, as there is no accurate tracking of the indices. | |
| // | |
| // (Paths involving element accesses via slice pattern bindings | |
| // can in principle be tracked precisely, but that is future | |
| // work. For now, continue claiming that they are imprecise.) | |
| false | |
| } | |
| LpDowncast(ref lp_base, _) | | |
| LpExtend(ref lp_base, _, _) => { | |
| loan_path_is_precise(&lp_base) | |
| } | |
| } | |
| } | |
| impl<'tcx> MoveData<'tcx> { | |
| pub fn new() -> MoveData<'tcx> { | |
| MoveData { | |
| paths: RefCell::new(Vec::new()), | |
| path_map: RefCell::new(FnvHashMap()), | |
| moves: RefCell::new(Vec::new()), | |
| path_assignments: RefCell::new(Vec::new()), | |
| var_assignments: RefCell::new(Vec::new()), | |
| variant_matches: RefCell::new(Vec::new()), | |
| assignee_ids: RefCell::new(NodeSet()), | |
| fragments: RefCell::new(fragments::FragmentSets::new()), | |
| } | |
| } | |
| pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> { | |
| (*self.paths.borrow())[index.get()].loan_path.clone() | |
| } | |
| fn path_parent(&self, index: MovePathIndex) -> MovePathIndex { | |
| (*self.paths.borrow())[index.get()].parent | |
| } | |
| fn path_first_move(&self, index: MovePathIndex) -> MoveIndex { | |
| (*self.paths.borrow())[index.get()].first_move | |
| } | |
| /// Returns the index of first child, or `InvalidMovePathIndex` if | |
| /// `index` is leaf. | |
| fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex { | |
| (*self.paths.borrow())[index.get()].first_child | |
| } | |
| fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex { | |
| (*self.paths.borrow())[index.get()].next_sibling | |
| } | |
| fn set_path_first_move(&self, | |
| index: MovePathIndex, | |
| first_move: MoveIndex) { | |
| (*self.paths.borrow_mut())[index.get()].first_move = first_move | |
| } | |
| fn set_path_first_child(&self, | |
| index: MovePathIndex, | |
| first_child: MovePathIndex) { | |
| (*self.paths.borrow_mut())[index.get()].first_child = first_child | |
| } | |
| fn move_next_move(&self, index: MoveIndex) -> MoveIndex { | |
| //! Type safe indexing operator | |
| (*self.moves.borrow())[index.get()].next_move | |
| } | |
| fn is_var_path(&self, index: MovePathIndex) -> bool { | |
| //! True if `index` refers to a variable | |
| self.path_parent(index) == InvalidMovePathIndex | |
| } | |
| /// 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. | |
| pub fn move_path(&self, | |
| tcx: &TyCtxt<'tcx>, | |
| lp: Rc<LoanPath<'tcx>>) -> MovePathIndex { | |
| match self.path_map.borrow().get(&lp) { | |
| Some(&index) => { | |
| return index; | |
| } | |
| None => {} | |
| } | |
| let index = match lp.kind { | |
| LpVar(..) | LpUpvar(..) => { | |
| let index = MovePathIndex(self.paths.borrow().len()); | |
| self.paths.borrow_mut().push(MovePath { | |
| loan_path: lp.clone(), | |
| parent: InvalidMovePathIndex, | |
| first_move: InvalidMoveIndex, | |
| first_child: InvalidMovePathIndex, | |
| next_sibling: InvalidMovePathIndex, | |
| }); | |
| index | |
| } | |
| LpDowncast(ref base, _) | | |
| LpExtend(ref base, _, _) => { | |
| let parent_index = self.move_path(tcx, base.clone()); | |
| let index = MovePathIndex(self.paths.borrow().len()); | |
| let next_sibling = self.path_first_child(parent_index); | |
| self.set_path_first_child(parent_index, index); | |
| self.paths.borrow_mut().push(MovePath { | |
| loan_path: lp.clone(), | |
| parent: parent_index, | |
| first_move: InvalidMoveIndex, | |
| first_child: InvalidMovePathIndex, | |
| next_sibling: next_sibling, | |
| }); | |
| index | |
| } | |
| }; | |
| debug!("move_path(lp={:?}, index={:?})", | |
| lp, | |
| index); | |
| assert_eq!(index.get(), self.paths.borrow().len() - 1); | |
| self.path_map.borrow_mut().insert(lp, index); | |
| return index; | |
| } | |
| fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>) | |
| -> Option<MovePathIndex> { | |
| self.path_map.borrow().get(lp).cloned() | |
| } | |
| fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>) | |
| -> Vec<MovePathIndex> { | |
| let mut result = vec!(); | |
| self.add_existing_base_paths(lp, &mut result); | |
| result | |
| } | |
| /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but | |
| /// does not add new move paths | |
| fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>, | |
| result: &mut Vec<MovePathIndex>) { | |
| match self.path_map.borrow().get(lp).cloned() { | |
| Some(index) => { | |
| self.each_base_path(index, |p| { | |
| result.push(p); | |
| true | |
| }); | |
| } | |
| None => { | |
| match lp.kind { | |
| LpVar(..) | LpUpvar(..) => { } | |
| LpDowncast(ref b, _) | | |
| LpExtend(ref b, _, _) => { | |
| self.add_existing_base_paths(b, result); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`. | |
| pub fn add_move(&self, | |
| tcx: &TyCtxt<'tcx>, | |
| lp: Rc<LoanPath<'tcx>>, | |
| id: ast::NodeId, | |
| kind: MoveKind) { | |
| debug!("add_move(lp={:?}, id={}, kind={:?})", | |
| lp, | |
| id, | |
| kind); | |
| let path_index = self.move_path(tcx, lp.clone()); | |
| let move_index = MoveIndex(self.moves.borrow().len()); | |
| self.fragments.borrow_mut().add_move(path_index); | |
| let next_move = self.path_first_move(path_index); | |
| self.set_path_first_move(path_index, move_index); | |
| self.moves.borrow_mut().push(Move { | |
| path: path_index, | |
| id: id, | |
| kind: kind, | |
| next_move: next_move | |
| }); | |
| } | |
| /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given | |
| /// `span`. | |
| pub fn add_assignment(&self, | |
| tcx: &TyCtxt<'tcx>, | |
| lp: Rc<LoanPath<'tcx>>, | |
| assign_id: ast::NodeId, | |
| span: Span, | |
| assignee_id: ast::NodeId, | |
| mode: euv::MutateMode) { | |
| debug!("add_assignment(lp={:?}, assign_id={}, assignee_id={}", | |
| lp, assign_id, assignee_id); | |
| let path_index = self.move_path(tcx, lp.clone()); | |
| self.fragments.borrow_mut().add_assignment(path_index); | |
| match mode { | |
| MutateMode::Init | MutateMode::JustWrite => { | |
| self.assignee_ids.borrow_mut().insert(assignee_id); | |
| } | |
| MutateMode::WriteAndRead => { } | |
| } | |
| let assignment = Assignment { | |
| path: path_index, | |
| id: assign_id, | |
| span: span, | |
| assignee_id: assignee_id, | |
| }; | |
| if self.is_var_path(path_index) { | |
| debug!("add_assignment[var](lp={:?}, assignment={}, path_index={:?})", | |
| lp, self.var_assignments.borrow().len(), path_index); | |
| self.var_assignments.borrow_mut().push(assignment); | |
| } else { | |
| debug!("add_assignment[path](lp={:?}, path_index={:?})", | |
| lp, path_index); | |
| self.path_assignments.borrow_mut().push(assignment); | |
| } | |
| } | |
| /// Adds a new record for a match of `base_lp`, downcast to | |
| /// variant `lp`, that occurs at location `pattern_id`. (One | |
| /// should be able to recover the span info from the | |
| /// `pattern_id` and the ast_map, I think.) | |
| pub fn add_variant_match(&self, | |
| tcx: &TyCtxt<'tcx>, | |
| lp: Rc<LoanPath<'tcx>>, | |
| pattern_id: ast::NodeId, | |
| base_lp: Rc<LoanPath<'tcx>>, | |
| mode: euv::MatchMode) { | |
| debug!("add_variant_match(lp={:?}, pattern_id={})", | |
| lp, pattern_id); | |
| let path_index = self.move_path(tcx, lp.clone()); | |
| let base_path_index = self.move_path(tcx, base_lp.clone()); | |
| self.fragments.borrow_mut().add_assignment(path_index); | |
| let variant_match = VariantMatch { | |
| path: path_index, | |
| base_path: base_path_index, | |
| id: pattern_id, | |
| mode: mode, | |
| }; | |
| self.variant_matches.borrow_mut().push(variant_match); | |
| } | |
| fn fixup_fragment_sets(&self, tcx: &TyCtxt<'tcx>) { | |
| fragments::fixup_fragment_sets(self, tcx) | |
| } | |
| /// 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 `README.md` for more details. | |
| fn add_gen_kills(&self, | |
| tcx: &TyCtxt<'tcx>, | |
| dfcx_moves: &mut MoveDataFlow, | |
| dfcx_assign: &mut AssignDataFlow) { | |
| for (i, the_move) in self.moves.borrow().iter().enumerate() { | |
| dfcx_moves.add_gen(the_move.id, i); | |
| } | |
| for (i, assignment) in self.var_assignments.borrow().iter().enumerate() { | |
| dfcx_assign.add_gen(assignment.id, i); | |
| self.kill_moves(assignment.path, assignment.id, | |
| KillFrom::Execution, dfcx_moves); | |
| } | |
| for assignment in self.path_assignments.borrow().iter() { | |
| self.kill_moves(assignment.path, assignment.id, | |
| KillFrom::Execution, dfcx_moves); | |
| } | |
| // Kill all moves related to a variable `x` when | |
| // it goes out of scope: | |
| for path in self.paths.borrow().iter() { | |
| match path.loan_path.kind { | |
| LpVar(..) | LpUpvar(..) | LpDowncast(..) => { | |
| let kill_scope = path.loan_path.kill_scope(tcx); | |
| let path = *self.path_map.borrow().get(&path.loan_path).unwrap(); | |
| self.kill_moves(path, kill_scope.node_id(&tcx.region_maps), | |
| KillFrom::ScopeEnd, dfcx_moves); | |
| } | |
| LpExtend(..) => {} | |
| } | |
| } | |
| // Kill all assignments when the variable goes out of scope: | |
| for (assignment_index, assignment) in | |
| self.var_assignments.borrow().iter().enumerate() { | |
| let lp = self.path_loan_path(assignment.path); | |
| match lp.kind { | |
| LpVar(..) | LpUpvar(..) | LpDowncast(..) => { | |
| let kill_scope = lp.kill_scope(tcx); | |
| dfcx_assign.add_kill(KillFrom::ScopeEnd, | |
| kill_scope.node_id(&tcx.region_maps), | |
| assignment_index); | |
| } | |
| LpExtend(..) => { | |
| bug!("var assignment for non var path"); | |
| } | |
| } | |
| } | |
| } | |
| fn each_base_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where | |
| F: FnMut(MovePathIndex) -> bool, | |
| { | |
| let mut p = index; | |
| while p != InvalidMovePathIndex { | |
| if !f(p) { | |
| return false; | |
| } | |
| p = self.path_parent(p); | |
| } | |
| return true; | |
| } | |
| // FIXME(#19596) This is a workaround, but there should be better way to do this | |
| fn each_extending_path_<F>(&self, index: MovePathIndex, f: &mut F) -> bool where | |
| F: FnMut(MovePathIndex) -> bool, | |
| { | |
| if !(*f)(index) { | |
| return false; | |
| } | |
| let mut p = self.path_first_child(index); | |
| while p != InvalidMovePathIndex { | |
| if !self.each_extending_path_(p, f) { | |
| return false; | |
| } | |
| p = self.path_next_sibling(p); | |
| } | |
| return true; | |
| } | |
| fn each_extending_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where | |
| F: FnMut(MovePathIndex) -> bool, | |
| { | |
| self.each_extending_path_(index, &mut f) | |
| } | |
| fn each_applicable_move<F>(&self, index0: MovePathIndex, mut f: F) -> bool where | |
| F: FnMut(MoveIndex) -> 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, | |
| kill_kind: KillFrom, | |
| dfcx_moves: &mut MoveDataFlow) { | |
| // We can only perform kills for paths that refer to a unique location, | |
| // since otherwise we may kill a move from one location with an | |
| // assignment referring to another location. | |
| let loan_path = self.path_loan_path(path); | |
| if loan_path_is_precise(&loan_path) { | |
| self.each_applicable_move(path, |move_index| { | |
| debug!("kill_moves add_kill {:?} kill_id={} move_index={}", | |
| kill_kind, kill_id, move_index.get()); | |
| dfcx_moves.add_kill(kill_kind, kill_id, move_index.get()); | |
| true | |
| }); | |
| } | |
| } | |
| } | |
| impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> { | |
| pub fn new(move_data: MoveData<'tcx>, | |
| tcx: &'a TyCtxt<'tcx>, | |
| cfg: &cfg::CFG, | |
| id_range: IdRange, | |
| decl: &hir::FnDecl, | |
| body: &hir::Block) | |
| -> FlowedMoveData<'a, 'tcx> { | |
| let mut dfcx_moves = | |
| DataFlowContext::new(tcx, | |
| "flowed_move_data_moves", | |
| Some(decl), | |
| cfg, | |
| MoveDataFlowOperator, | |
| id_range, | |
| move_data.moves.borrow().len()); | |
| let mut dfcx_assign = | |
| DataFlowContext::new(tcx, | |
| "flowed_move_data_assigns", | |
| Some(decl), | |
| cfg, | |
| AssignDataFlowOperator, | |
| id_range, | |
| move_data.var_assignments.borrow().len()); | |
| move_data.fixup_fragment_sets(tcx); | |
| move_data.add_gen_kills(tcx, | |
| &mut dfcx_moves, | |
| &mut dfcx_assign); | |
| dfcx_moves.add_kills_from_flow_exits(cfg); | |
| dfcx_assign.add_kills_from_flow_exits(cfg); | |
| dfcx_moves.propagate(cfg, body); | |
| dfcx_assign.propagate(cfg, body); | |
| FlowedMoveData { | |
| move_data: move_data, | |
| dfcx_moves: dfcx_moves, | |
| dfcx_assign: dfcx_assign, | |
| } | |
| } | |
| pub fn kind_of_move_of_path(&self, | |
| id: ast::NodeId, | |
| loan_path: &Rc<LoanPath<'tcx>>) | |
| -> Option<MoveKind> { | |
| //! Returns the kind of a move of `loan_path` by `id`, if one exists. | |
| let mut ret = None; | |
| if let Some(loan_path_index) = self.move_data.path_map.borrow().get(&*loan_path) { | |
| self.dfcx_moves.each_gen_bit(id, |move_index| { | |
| let the_move = self.move_data.moves.borrow(); | |
| let the_move = (*the_move)[move_index]; | |
| if the_move.path == *loan_path_index { | |
| ret = Some(the_move.kind); | |
| false | |
| } else { | |
| true | |
| } | |
| }); | |
| } | |
| ret | |
| } | |
| /// 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`. | |
| pub fn each_move_of<F>(&self, | |
| id: ast::NodeId, | |
| loan_path: &Rc<LoanPath<'tcx>>, | |
| mut f: F) | |
| -> bool where | |
| F: FnMut(&Move, &LoanPath<'tcx>) -> bool, | |
| { | |
| // 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(id, |index| { | |
| let the_move = self.move_data.moves.borrow(); | |
| let the_move = &(*the_move)[index]; | |
| let moved_path = the_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(the_move, &self.move_data.path_loan_path(moved_path)) { | |
| ret = false; | |
| } | |
| } else { | |
| if let Some(loan_path_index) = opt_loan_path_index { | |
| 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(the_move, | |
| &self.move_data.path_loan_path(moved_path)) | |
| } else { | |
| true | |
| } | |
| }); | |
| if !cont { ret = false; } | |
| } | |
| } | |
| ret | |
| }) | |
| } | |
| /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`. | |
| /// `loan_path` must be a single variable. | |
| pub fn each_assignment_of<F>(&self, | |
| id: ast::NodeId, | |
| loan_path: &Rc<LoanPath<'tcx>>, | |
| mut f: F) | |
| -> bool where | |
| F: FnMut(&Assignment) -> bool, | |
| { | |
| 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(id, |index| { | |
| let assignment = self.move_data.var_assignments.borrow(); | |
| let assignment = &(*assignment)[index]; | |
| if assignment.path == loan_path_index && !f(assignment) { | |
| false | |
| } else { | |
| true | |
| } | |
| }) | |
| } | |
| } | |
| impl BitwiseOperator for MoveDataFlowOperator { | |
| #[inline] | |
| fn join(&self, succ: usize, pred: usize) -> usize { | |
| succ | pred // moves from both preds are in scope | |
| } | |
| } | |
| impl DataFlowOperator for MoveDataFlowOperator { | |
| #[inline] | |
| fn initial_value(&self) -> bool { | |
| false // no loans in scope by default | |
| } | |
| } | |
| impl BitwiseOperator for AssignDataFlowOperator { | |
| #[inline] | |
| fn join(&self, succ: usize, pred: usize) -> usize { | |
| succ | pred // moves from both preds are in scope | |
| } | |
| } | |
| impl DataFlowOperator for AssignDataFlowOperator { | |
| #[inline] | |
| fn initial_value(&self) -> bool { | |
| false // no assignments in scope by default | |
| } | |
| } |