1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Data structures used for tracking moves. Please see the extensive
12 //! comments in the section "Moves and initialization" in `doc.rs`.
14 pub use self::MoveKind::*;
16 use std::cell::RefCell;
19 use middle::borrowck::*;
20 use middle::borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
21 use middle::borrowck::LoanPathElem::{LpInterior};
23 use middle::dataflow::DataFlowContext;
24 use middle::dataflow::BitwiseOperator;
25 use middle::dataflow::DataFlowOperator;
26 use middle::expr_use_visitor as euv;
27 use middle::mem_categorization as mc;
31 use syntax::codemap::Span;
32 use util::nodemap::{FnvHashMap, NodeSet};
33 use util::ppaux::Repr;
35 #[path="fragments.rs"]
38 pub struct MoveData<'tcx> {
39 /// Move paths. See section "Move paths" in `doc.rs`.
40 pub paths: RefCell<Vec<MovePath<'tcx>>>,
42 /// Cache of loan path to move path index, for easy lookup.
43 pub path_map: RefCell<FnvHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>,
45 /// Each move or uninitialized variable gets an entry here.
46 pub moves: RefCell<Vec<Move>>,
48 /// Assignments to a variable, like `x = foo`. These are assigned
49 /// bits for dataflow, since we must track them to ensure that
50 /// immutable variables are assigned at most once along each path.
51 pub var_assignments: RefCell<Vec<Assignment>>,
53 /// Assignments to a path, like `x.f = foo`. These are not
54 /// assigned dataflow bits, but we track them because they still
56 pub path_assignments: RefCell<Vec<Assignment>>,
58 /// Enum variant matched within a pattern on some match arm, like
59 /// `SomeStruct{ f: Variant1(x, y) } => ...`
60 pub variant_matches: RefCell<Vec<VariantMatch>>,
62 /// Assignments to a variable or path, like `x = foo`, but not `x += foo`.
63 pub assignee_ids: RefCell<NodeSet>,
65 /// Path-fragments from moves in to or out of parts of structured data.
66 pub fragments: RefCell<fragments::FragmentSets>,
69 pub struct FlowedMoveData<'a, 'tcx: 'a> {
70 pub move_data: MoveData<'tcx>,
72 pub dfcx_moves: MoveDataFlow<'a, 'tcx>,
74 // We could (and maybe should, for efficiency) combine both move
75 // and assign data flow into one, but this way it's easier to
76 // distinguish the bits that correspond to moves and assignments.
77 pub dfcx_assign: AssignDataFlow<'a, 'tcx>
80 /// Index into `MoveData.paths`, used like a pointer
81 #[deriving(PartialEq, Eq, PartialOrd, Ord, Show)]
82 pub struct MovePathIndex(uint);
85 fn get(&self) -> uint {
86 let MovePathIndex(v) = *self; v
90 impl Clone for MovePathIndex {
91 fn clone(&self) -> MovePathIndex {
92 MovePathIndex(self.get())
96 #[allow(non_upper_case_globals)]
97 static InvalidMovePathIndex: MovePathIndex =
98 MovePathIndex(uint::MAX);
100 /// Index into `MoveData.moves`, used like a pointer
101 #[deriving(PartialEq)]
102 pub struct MoveIndex(uint);
105 fn get(&self) -> uint {
106 let MoveIndex(v) = *self; v
110 #[allow(non_upper_case_globals)]
111 static InvalidMoveIndex: MoveIndex =
112 MoveIndex(uint::MAX);
114 pub struct MovePath<'tcx> {
115 /// Loan path corresponding to this move path
116 pub loan_path: Rc<LoanPath<'tcx>>,
118 /// Parent pointer, `InvalidMovePathIndex` if root
119 pub parent: MovePathIndex,
121 /// Head of linked list of moves to this path,
122 /// `InvalidMoveIndex` if not moved
123 pub first_move: MoveIndex,
125 /// First node in linked list of children, `InvalidMovePathIndex` if leaf
126 pub first_child: MovePathIndex,
128 /// Next node in linked list of parent's children (siblings),
129 /// `InvalidMovePathIndex` if none.
130 pub next_sibling: MovePathIndex,
133 #[deriving(PartialEq, Show)]
135 Declared, // When declared, variables start out "moved".
136 MoveExpr, // Expression or binding that moves a variable
137 MovePat, // By-move binding
138 Captured // Closure creation that moves a value
142 /// Path being moved.
143 pub path: MovePathIndex,
145 /// id of node that is doing the move.
148 /// Kind of move, for error messages.
151 /// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
152 pub next_move: MoveIndex
155 pub struct Assignment {
156 /// Path being assigned.
157 pub path: MovePathIndex,
159 /// id where assignment occurs
162 /// span of node where assignment occurs
166 pub struct VariantMatch {
167 /// downcast to the variant.
168 pub path: MovePathIndex,
170 /// path being downcast to the variant.
171 pub base_path: MovePathIndex,
173 /// id where variant's pattern occurs
176 /// says if variant established by move (and why), by copy, or by borrow.
177 pub mode: euv::MatchMode
181 pub struct MoveDataFlowOperator;
183 pub type MoveDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, MoveDataFlowOperator>;
186 pub struct AssignDataFlowOperator;
188 pub type AssignDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, AssignDataFlowOperator>;
190 fn loan_path_is_precise(loan_path: &LoanPath) -> bool {
191 match loan_path.kind {
192 LpVar(_) | LpUpvar(_) => {
195 LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
196 // Paths involving element accesses do not refer to a unique
197 // location, as there is no accurate tracking of the indices.
200 LpDowncast(ref lp_base, _) |
201 LpExtend(ref lp_base, _, _) => {
202 loan_path_is_precise(&**lp_base)
208 pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
209 format!("Move{} path: {}, id: {}, kind: {} {}",
211 move_data.path_loan_path(self.path).repr(tcx),
219 pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
220 format!("Assignment{} path: {}, id: {} {}",
222 move_data.path_loan_path(self.path).repr(tcx),
229 pub fn to_string<'tcx>(&self, move_data: &MoveData<'tcx>, tcx: &ty::ctxt<'tcx>) -> String {
230 format!("VariantMatch{} path: {}, id: {} {}",
232 move_data.path_loan_path(self.path).repr(tcx),
238 impl<'tcx> MoveData<'tcx> {
239 pub fn new() -> MoveData<'tcx> {
241 paths: RefCell::new(Vec::new()),
242 path_map: RefCell::new(FnvHashMap::new()),
243 moves: RefCell::new(Vec::new()),
244 path_assignments: RefCell::new(Vec::new()),
245 var_assignments: RefCell::new(Vec::new()),
246 variant_matches: RefCell::new(Vec::new()),
247 assignee_ids: RefCell::new(NodeSet::new()),
248 fragments: RefCell::new(fragments::FragmentSets::new()),
252 pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> {
253 (*self.paths.borrow())[index.get()].loan_path.clone()
256 fn path_parent(&self, index: MovePathIndex) -> MovePathIndex {
257 (*self.paths.borrow())[index.get()].parent
260 fn path_first_move(&self, index: MovePathIndex) -> MoveIndex {
261 (*self.paths.borrow())[index.get()].first_move
264 /// Returns the index of first child, or `InvalidMovePathIndex` if
266 fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex {
267 (*self.paths.borrow())[index.get()].first_child
270 fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex {
271 (*self.paths.borrow())[index.get()].next_sibling
274 fn set_path_first_move(&self,
275 index: MovePathIndex,
276 first_move: MoveIndex) {
277 (*self.paths.borrow_mut())[index.get()].first_move = first_move
280 fn set_path_first_child(&self,
281 index: MovePathIndex,
282 first_child: MovePathIndex) {
283 (*self.paths.borrow_mut())[index.get()].first_child = first_child
286 fn move_next_move(&self, index: MoveIndex) -> MoveIndex {
287 //! Type safe indexing operator
288 (*self.moves.borrow())[index.get()].next_move
291 fn is_var_path(&self, index: MovePathIndex) -> bool {
292 //! True if `index` refers to a variable
293 self.path_parent(index) == InvalidMovePathIndex
296 /// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for
297 /// `lp` and any of its base paths that do not yet have an index.
298 pub fn move_path(&self,
299 tcx: &ty::ctxt<'tcx>,
300 lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
301 match self.path_map.borrow().get(&lp) {
308 let index = match lp.kind {
309 LpVar(..) | LpUpvar(..) => {
310 let index = MovePathIndex(self.paths.borrow().len());
312 self.paths.borrow_mut().push(MovePath {
313 loan_path: lp.clone(),
314 parent: InvalidMovePathIndex,
315 first_move: InvalidMoveIndex,
316 first_child: InvalidMovePathIndex,
317 next_sibling: InvalidMovePathIndex,
323 LpDowncast(ref base, _) |
324 LpExtend(ref base, _, _) => {
325 let parent_index = self.move_path(tcx, base.clone());
327 let index = MovePathIndex(self.paths.borrow().len());
329 let next_sibling = self.path_first_child(parent_index);
330 self.set_path_first_child(parent_index, index);
332 self.paths.borrow_mut().push(MovePath {
333 loan_path: lp.clone(),
334 parent: parent_index,
335 first_move: InvalidMoveIndex,
336 first_child: InvalidMovePathIndex,
337 next_sibling: next_sibling,
344 debug!("move_path(lp={}, index={})",
348 assert_eq!(index.get(), self.paths.borrow().len() - 1);
349 self.path_map.borrow_mut().insert(lp, index);
353 fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>)
354 -> Option<MovePathIndex> {
355 self.path_map.borrow().get(lp).cloned()
358 fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>)
359 -> Vec<MovePathIndex> {
360 let mut result = vec!();
361 self.add_existing_base_paths(lp, &mut result);
365 /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but
366 /// does not add new move paths
367 fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>,
368 result: &mut Vec<MovePathIndex>) {
369 match self.path_map.borrow().get(lp).cloned() {
371 self.each_base_path(index, |p| {
378 LpVar(..) | LpUpvar(..) => { }
379 LpDowncast(ref b, _) |
380 LpExtend(ref b, _, _) => {
381 self.add_existing_base_paths(b, result);
389 /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`.
390 pub fn add_move(&self,
391 tcx: &ty::ctxt<'tcx>,
392 lp: Rc<LoanPath<'tcx>>,
395 debug!("add_move(lp={}, id={}, kind={})",
400 let path_index = self.move_path(tcx, lp.clone());
401 let move_index = MoveIndex(self.moves.borrow().len());
403 self.fragments.borrow_mut().add_move(path_index);
405 let next_move = self.path_first_move(path_index);
406 self.set_path_first_move(path_index, move_index);
408 self.moves.borrow_mut().push(Move {
416 /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given
418 pub fn add_assignment(&self,
419 tcx: &ty::ctxt<'tcx>,
420 lp: Rc<LoanPath<'tcx>>,
421 assign_id: ast::NodeId,
423 assignee_id: ast::NodeId,
424 mode: euv::MutateMode) {
425 debug!("add_assignment(lp={}, assign_id={}, assignee_id={}",
426 lp.repr(tcx), assign_id, assignee_id);
428 let path_index = self.move_path(tcx, lp.clone());
430 self.fragments.borrow_mut().add_assignment(path_index);
433 euv::Init | euv::JustWrite => {
434 self.assignee_ids.borrow_mut().insert(assignee_id);
436 euv::WriteAndRead => { }
439 let assignment = Assignment {
445 if self.is_var_path(path_index) {
446 debug!("add_assignment[var](lp={}, assignment={}, path_index={})",
447 lp.repr(tcx), self.var_assignments.borrow().len(), path_index);
449 self.var_assignments.borrow_mut().push(assignment);
451 debug!("add_assignment[path](lp={}, path_index={})",
452 lp.repr(tcx), path_index);
454 self.path_assignments.borrow_mut().push(assignment);
458 /// Adds a new record for a match of `base_lp`, downcast to
459 /// variant `lp`, that occurs at location `pattern_id`. (One
460 /// should be able to recover the span info from the
461 /// `pattern_id` and the ast_map, I think.)
462 pub fn add_variant_match(&self,
463 tcx: &ty::ctxt<'tcx>,
464 lp: Rc<LoanPath<'tcx>>,
465 pattern_id: ast::NodeId,
466 base_lp: Rc<LoanPath<'tcx>>,
467 mode: euv::MatchMode) {
468 debug!("add_variant_match(lp={}, pattern_id={})",
469 lp.repr(tcx), pattern_id);
471 let path_index = self.move_path(tcx, lp.clone());
472 let base_path_index = self.move_path(tcx, base_lp.clone());
474 self.fragments.borrow_mut().add_assignment(path_index);
476 let variant_match = VariantMatch {
478 base_path: base_path_index,
483 self.variant_matches.borrow_mut().push(variant_match);
486 fn fixup_fragment_sets(&self, tcx: &ty::ctxt<'tcx>) {
487 fragments::fixup_fragment_sets(self, tcx)
490 /// Adds the gen/kills for the various moves and
491 /// assignments into the provided data flow contexts.
492 /// Moves are generated by moves and killed by assignments and
493 /// scoping. Assignments are generated by assignment to variables and
494 /// killed by scoping. See `doc.rs` for more details.
495 fn add_gen_kills(&self,
496 tcx: &ty::ctxt<'tcx>,
497 dfcx_moves: &mut MoveDataFlow,
498 dfcx_assign: &mut AssignDataFlow) {
499 for (i, the_move) in self.moves.borrow().iter().enumerate() {
500 dfcx_moves.add_gen(the_move.id, i);
503 for (i, assignment) in self.var_assignments.borrow().iter().enumerate() {
504 dfcx_assign.add_gen(assignment.id, i);
505 self.kill_moves(assignment.path, assignment.id, dfcx_moves);
508 for assignment in self.path_assignments.borrow().iter() {
509 self.kill_moves(assignment.path, assignment.id, dfcx_moves);
512 // Kill all moves related to a variable `x` when
513 // it goes out of scope:
514 for path in self.paths.borrow().iter() {
515 match path.loan_path.kind {
516 LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
517 let kill_scope = path.loan_path.kill_scope(tcx);
518 let path = self.path_map.borrow()[path.loan_path];
519 self.kill_moves(path, kill_scope.node_id(), dfcx_moves);
525 // Kill all assignments when the variable goes out of scope:
526 for (assignment_index, assignment) in
527 self.var_assignments.borrow().iter().enumerate() {
528 let lp = self.path_loan_path(assignment.path);
530 LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
531 let kill_scope = lp.kill_scope(tcx);
532 dfcx_assign.add_kill(kill_scope.node_id(), assignment_index);
535 tcx.sess.bug("var assignment for non var path");
541 fn each_base_path(&self, index: MovePathIndex, f: |MovePathIndex| -> bool)
544 while p != InvalidMovePathIndex {
548 p = self.path_parent(p);
553 fn each_extending_path(&self,
554 index: MovePathIndex,
555 f: |MovePathIndex| -> bool)
561 let mut p = self.path_first_child(index);
562 while p != InvalidMovePathIndex {
563 if !self.each_extending_path(p, |x| f(x)) {
566 p = self.path_next_sibling(p);
572 fn each_applicable_move(&self,
573 index0: MovePathIndex,
574 f: |MoveIndex| -> bool)
577 self.each_extending_path(index0, |index| {
578 let mut p = self.path_first_move(index);
579 while p != InvalidMoveIndex {
584 p = self.move_next_move(p);
593 kill_id: ast::NodeId,
594 dfcx_moves: &mut MoveDataFlow) {
595 // We can only perform kills for paths that refer to a unique location,
596 // since otherwise we may kill a move from one location with an
597 // assignment referring to another location.
599 let loan_path = self.path_loan_path(path);
600 if loan_path_is_precise(&*loan_path) {
601 self.each_applicable_move(path, |move_index| {
602 dfcx_moves.add_kill(kill_id, move_index.get());
609 impl<'a, 'tcx> FlowedMoveData<'a, 'tcx> {
610 pub fn new(move_data: MoveData<'tcx>,
611 tcx: &'a ty::ctxt<'tcx>,
613 id_range: ast_util::IdRange,
616 -> FlowedMoveData<'a, 'tcx> {
618 DataFlowContext::new(tcx,
619 "flowed_move_data_moves",
622 MoveDataFlowOperator,
624 move_data.moves.borrow().len());
625 let mut dfcx_assign =
626 DataFlowContext::new(tcx,
627 "flowed_move_data_assigns",
630 AssignDataFlowOperator,
632 move_data.var_assignments.borrow().len());
634 move_data.fixup_fragment_sets(tcx);
636 move_data.add_gen_kills(tcx,
640 dfcx_moves.add_kills_from_flow_exits(cfg);
641 dfcx_assign.add_kills_from_flow_exits(cfg);
643 dfcx_moves.propagate(cfg, body);
644 dfcx_assign.propagate(cfg, body);
647 move_data: move_data,
648 dfcx_moves: dfcx_moves,
649 dfcx_assign: dfcx_assign,
653 pub fn kind_of_move_of_path(&self,
655 loan_path: &Rc<LoanPath<'tcx>>)
656 -> Option<MoveKind> {
657 //! Returns the kind of a move of `loan_path` by `id`, if one exists.
660 for loan_path_index in self.move_data.path_map.borrow().get(&*loan_path).iter() {
661 self.dfcx_moves.each_gen_bit(id, |move_index| {
662 let the_move = self.move_data.moves.borrow();
663 let the_move = (*the_move)[move_index];
664 if the_move.path == **loan_path_index {
665 ret = Some(the_move.kind);
675 /// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may*
676 /// have occurred on entry to `id` without an intervening assignment. In other words, any moves
677 /// that would invalidate a reference to `loan_path` at location `id`.
678 pub fn each_move_of(&self,
680 loan_path: &Rc<LoanPath<'tcx>>,
681 f: |&Move, &LoanPath<'tcx>| -> bool)
685 // 1. Move of `a.b.c`, use of `a.b.c`
686 // 2. Move of `a.b.c`, use of `a.b.c.d`
687 // 3. Move of `a.b.c`, use of `a` or `a.b`
691 // 4. move of `a.b.c`, use of `a.b.d`
693 let base_indices = self.move_data.existing_base_paths(loan_path);
694 if base_indices.is_empty() {
698 let opt_loan_path_index = self.move_data.existing_move_path(loan_path);
702 self.dfcx_moves.each_bit_on_entry(id, |index| {
703 let the_move = self.move_data.moves.borrow();
704 let the_move = &(*the_move)[index];
705 let moved_path = the_move.path;
706 if base_indices.iter().any(|x| x == &moved_path) {
707 // Scenario 1 or 2: `loan_path` or some base path of
708 // `loan_path` was moved.
709 if !f(the_move, &*self.move_data.path_loan_path(moved_path)) {
713 for &loan_path_index in opt_loan_path_index.iter() {
714 let cont = self.move_data.each_base_path(moved_path, |p| {
715 if p == loan_path_index {
716 // Scenario 3: some extension of `loan_path`
719 &*self.move_data.path_loan_path(moved_path))
724 if !cont { ret = false; break }
731 /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`.
732 /// `loan_path` must be a single variable.
733 pub fn each_assignment_of(&self,
735 loan_path: &Rc<LoanPath<'tcx>>,
736 f: |&Assignment| -> bool)
738 let loan_path_index = {
739 match self.move_data.existing_move_path(loan_path) {
742 // if there were any assignments, it'd have an index
748 self.dfcx_assign.each_bit_on_entry(id, |index| {
749 let assignment = self.move_data.var_assignments.borrow();
750 let assignment = &(*assignment)[index];
751 if assignment.path == loan_path_index && !f(assignment) {
760 impl BitwiseOperator for MoveDataFlowOperator {
762 fn join(&self, succ: uint, pred: uint) -> uint {
763 succ | pred // moves from both preds are in scope
767 impl DataFlowOperator for MoveDataFlowOperator {
769 fn initial_value(&self) -> bool {
770 false // no loans in scope by default
774 impl BitwiseOperator for AssignDataFlowOperator {
776 fn join(&self, succ: uint, pred: uint) -> uint {
777 succ | pred // moves from both preds are in scope
781 impl DataFlowOperator for AssignDataFlowOperator {
783 fn initial_value(&self) -> bool {
784 false // no assignments in scope by default