]> git.lizzy.rs Git - rust.git/blob - src/librustc_ast_borrowck/borrowck/move_data.rs
887a0e2f20e16544adbcedb3d40b3a631061de5b
[rust.git] / src / librustc_ast_borrowck / borrowck / move_data.rs
1 //! Data structures used for tracking moves. Please see the extensive
2 //! comments in the section "Moves and initialization" in `README.md`.
3
4 use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
5
6 use crate::borrowck::*;
7 use rustc::cfg;
8 use rustc::ty::{self, TyCtxt};
9 use rustc::util::nodemap::FxHashMap;
10
11 use std::cell::RefCell;
12 use std::rc::Rc;
13 use std::usize;
14 use syntax_pos::Span;
15 use rustc::hir;
16 use log::debug;
17
18 #[derive(Default)]
19 pub struct MoveData<'tcx> {
20     /// Move paths. See section "Move paths" in `README.md`.
21     pub paths: RefCell<Vec<MovePath<'tcx>>>,
22
23     /// Cache of loan path to move path index, for easy lookup.
24     pub path_map: RefCell<FxHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>,
25
26     /// Each move or uninitialized variable gets an entry here.
27     pub moves: RefCell<Vec<Move>>,
28
29     /// Assignments to a variable, like `x = foo`. These are assigned
30     /// bits for dataflow, since we must track them to ensure that
31     /// immutable variables are assigned at most once along each path.
32     pub var_assignments: RefCell<Vec<Assignment>>,
33
34     /// Assignments to a path, like `x.f = foo`. These are not
35     /// assigned dataflow bits, but we track them because they still
36     /// kill move bits.
37     pub path_assignments: RefCell<Vec<Assignment>>,
38 }
39
40 pub struct FlowedMoveData<'tcx> {
41     pub move_data: MoveData<'tcx>,
42
43     pub dfcx_moves: MoveDataFlow<'tcx>,
44
45     // We could (and maybe should, for efficiency) combine both move
46     // and assign data flow into one, but this way it's easier to
47     // distinguish the bits that correspond to moves and assignments.
48     pub dfcx_assign: AssignDataFlow<'tcx>,
49 }
50
51 /// Index into `MoveData.paths`, used like a pointer
52 #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
53 pub struct MovePathIndex(usize);
54
55 impl MovePathIndex {
56     fn get(&self) -> usize {
57         let MovePathIndex(v) = *self; v
58     }
59 }
60
61 impl Clone for MovePathIndex {
62     fn clone(&self) -> MovePathIndex {
63         MovePathIndex(self.get())
64     }
65 }
66
67 #[allow(non_upper_case_globals)]
68 const InvalidMovePathIndex: MovePathIndex = MovePathIndex(usize::MAX);
69
70 /// Index into `MoveData.moves`, used like a pointer
71 #[derive(Copy, Clone, PartialEq)]
72 pub struct MoveIndex(usize);
73
74 impl MoveIndex {
75     fn get(&self) -> usize {
76         let MoveIndex(v) = *self; v
77     }
78 }
79
80 #[allow(non_upper_case_globals)]
81 const InvalidMoveIndex: MoveIndex = MoveIndex(usize::MAX);
82
83 pub struct MovePath<'tcx> {
84     /// Loan path corresponding to this move path
85     pub loan_path: Rc<LoanPath<'tcx>>,
86
87     /// Parent pointer, `InvalidMovePathIndex` if root
88     pub parent: MovePathIndex,
89
90     /// Head of linked list of moves to this path,
91     /// `InvalidMoveIndex` if not moved
92     pub first_move: MoveIndex,
93
94     /// First node in linked list of children, `InvalidMovePathIndex` if leaf
95     pub first_child: MovePathIndex,
96
97     /// Next node in linked list of parent's children (siblings),
98     /// `InvalidMovePathIndex` if none.
99     pub next_sibling: MovePathIndex,
100 }
101
102
103 #[derive(Copy, Clone)]
104 pub struct Move {
105     /// Path being moved.
106     pub path: MovePathIndex,
107
108     /// ID of node that is doing the move.
109     pub id: hir::ItemLocalId,
110
111     /// Next node in linked list of moves from `path`, or `InvalidMoveIndex`
112     pub next_move: MoveIndex
113 }
114
115 #[derive(Copy, Clone)]
116 pub struct Assignment {
117     /// Path being assigned.
118     pub path: MovePathIndex,
119
120     /// ID where assignment occurs
121     pub id: hir::ItemLocalId,
122
123     /// span of node where assignment occurs
124     pub span: Span,
125 }
126
127 #[derive(Clone, Copy)]
128 pub struct MoveDataFlowOperator;
129
130 pub type MoveDataFlow<'tcx> = DataFlowContext<'tcx, MoveDataFlowOperator>;
131
132 #[derive(Clone, Copy)]
133 pub struct AssignDataFlowOperator;
134
135 pub type AssignDataFlow<'tcx> = DataFlowContext<'tcx, AssignDataFlowOperator>;
136
137 fn loan_path_is_precise(loan_path: &LoanPath<'_>) -> bool {
138     match loan_path.kind {
139         LpVar(_) | LpUpvar(_) => {
140             true
141         }
142         LpExtend(.., LpInterior(_, InteriorKind::InteriorElement)) => {
143             // Paths involving element accesses a[i] do not refer to a unique
144             // location, as there is no accurate tracking of the indices.
145             //
146             // (Paths involving element accesses via slice pattern bindings
147             // can in principle be tracked precisely, but that is future
148             // work. For now, continue claiming that they are imprecise.)
149             false
150         }
151         LpDowncast(ref lp_base, _) |
152         LpExtend(ref lp_base, ..) => {
153             loan_path_is_precise(&lp_base)
154         }
155     }
156 }
157
158 impl MoveData<'tcx> {
159     /// Returns `true` if there are no trackable assignments or moves
160     /// in this move data -- that means that there is nothing that
161     /// could cause a borrow error.
162     pub fn is_empty(&self) -> bool {
163         self.moves.borrow().is_empty() &&
164             self.path_assignments.borrow().is_empty() &&
165             self.var_assignments.borrow().is_empty()
166     }
167
168     pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> {
169         (*self.paths.borrow())[index.get()].loan_path.clone()
170     }
171
172     fn path_parent(&self, index: MovePathIndex) -> MovePathIndex {
173         (*self.paths.borrow())[index.get()].parent
174     }
175
176     fn path_first_move(&self, index: MovePathIndex) -> MoveIndex {
177         (*self.paths.borrow())[index.get()].first_move
178     }
179
180     /// Returns the index of first child, or `InvalidMovePathIndex` if
181     /// `index` is leaf.
182     fn path_first_child(&self, index: MovePathIndex) -> MovePathIndex {
183         (*self.paths.borrow())[index.get()].first_child
184     }
185
186     fn path_next_sibling(&self, index: MovePathIndex) -> MovePathIndex {
187         (*self.paths.borrow())[index.get()].next_sibling
188     }
189
190     fn set_path_first_move(&self,
191                            index: MovePathIndex,
192                            first_move: MoveIndex) {
193         (*self.paths.borrow_mut())[index.get()].first_move = first_move
194     }
195
196     fn set_path_first_child(&self,
197                             index: MovePathIndex,
198                             first_child: MovePathIndex) {
199         (*self.paths.borrow_mut())[index.get()].first_child = first_child
200     }
201
202     fn move_next_move(&self, index: MoveIndex) -> MoveIndex {
203         //! Type safe indexing operator
204         (*self.moves.borrow())[index.get()].next_move
205     }
206
207     fn is_var_path(&self, index: MovePathIndex) -> bool {
208         //! True if `index` refers to a variable
209         self.path_parent(index) == InvalidMovePathIndex
210     }
211
212     /// Returns the existing move path index for `lp`, if any, and otherwise adds a new index for
213     /// `lp` and any of its base paths that do not yet have an index.
214     pub fn move_path(&self, tcx: TyCtxt<'tcx>, lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
215         if let Some(&index) = self.path_map.borrow().get(&lp) {
216             return index;
217         }
218
219         let index = match lp.kind {
220             LpVar(..) | LpUpvar(..) => {
221                 let index = MovePathIndex(self.paths.borrow().len());
222
223                 self.paths.borrow_mut().push(MovePath {
224                     loan_path: lp.clone(),
225                     parent: InvalidMovePathIndex,
226                     first_move: InvalidMoveIndex,
227                     first_child: InvalidMovePathIndex,
228                     next_sibling: InvalidMovePathIndex,
229                 });
230
231                 index
232             }
233
234             LpDowncast(ref base, _) |
235             LpExtend(ref base, ..) => {
236                 let parent_index = self.move_path(tcx, base.clone());
237
238                 let index = MovePathIndex(self.paths.borrow().len());
239
240                 let next_sibling = self.path_first_child(parent_index);
241                 self.set_path_first_child(parent_index, index);
242
243                 self.paths.borrow_mut().push(MovePath {
244                     loan_path: lp.clone(),
245                     parent: parent_index,
246                     first_move: InvalidMoveIndex,
247                     first_child: InvalidMovePathIndex,
248                     next_sibling,
249                 });
250
251                 index
252             }
253         };
254
255         debug!("move_path(lp={:?}, index={:?})",
256                lp,
257                index);
258
259         assert_eq!(index.get(), self.paths.borrow().len() - 1);
260         self.path_map.borrow_mut().insert(lp, index);
261         return index;
262     }
263
264     fn existing_move_path(&self, lp: &Rc<LoanPath<'tcx>>)
265                           -> Option<MovePathIndex> {
266         self.path_map.borrow().get(lp).cloned()
267     }
268
269     fn existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>)
270                            -> Vec<MovePathIndex> {
271         let mut result = vec![];
272         self.add_existing_base_paths(lp, &mut result);
273         result
274     }
275
276     /// Adds any existing move path indices for `lp` and any base paths of `lp` to `result`, but
277     /// does not add new move paths
278     fn add_existing_base_paths(&self, lp: &Rc<LoanPath<'tcx>>,
279                                result: &mut Vec<MovePathIndex>) {
280         match self.path_map.borrow().get(lp).cloned() {
281             Some(index) => {
282                 self.each_base_path(index, |p| {
283                     result.push(p);
284                     true
285                 });
286             }
287             None => {
288                 match lp.kind {
289                     LpVar(..) | LpUpvar(..) => { }
290                     LpDowncast(ref b, _) |
291                     LpExtend(ref b, ..) => {
292                         self.add_existing_base_paths(b, result);
293                     }
294                 }
295             }
296         }
297
298     }
299
300     /// Adds a new move entry for a move of `lp` that occurs at location `id` with kind `kind`.
301     pub fn add_move(
302         &self,
303         tcx: TyCtxt<'tcx>,
304         orig_lp: Rc<LoanPath<'tcx>>,
305         id: hir::ItemLocalId,
306     ) {
307         // Moving one union field automatically moves all its fields. Also move siblings of
308         // all parent union fields, moves do not propagate upwards automatically.
309         let mut lp = orig_lp.clone();
310         while let LpExtend(ref base_lp, mutbl, lp_elem) = lp.clone().kind {
311             if let (&ty::Adt(adt_def, _), LpInterior(opt_variant_id, interior))
312                     = (&base_lp.ty.sty, lp_elem) {
313                 if adt_def.is_union() {
314                     for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() {
315                         let field =
316                             InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name));
317                         if field != interior {
318                             let sibling_lp_kind =
319                                 LpExtend(base_lp.clone(), mutbl, LpInterior(opt_variant_id, field));
320                             let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, tcx.types.err));
321                             self.add_move_helper(tcx, sibling_lp, id);
322                         }
323                     }
324                 }
325             }
326             lp = base_lp.clone();
327         }
328
329         self.add_move_helper(tcx, orig_lp, id);
330     }
331
332     fn add_move_helper(
333         &self,
334         tcx: TyCtxt<'tcx>,
335         lp: Rc<LoanPath<'tcx>>,
336         id: hir::ItemLocalId,
337     ) {
338         debug!("add_move(lp={:?}, id={:?})", lp, id);
339
340         let path_index = self.move_path(tcx, lp);
341         let move_index = MoveIndex(self.moves.borrow().len());
342
343         let next_move = self.path_first_move(path_index);
344         self.set_path_first_move(path_index, move_index);
345
346         self.moves.borrow_mut().push(Move {
347             path: path_index,
348             id,
349             next_move,
350         });
351     }
352
353     /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given
354     /// `span`.
355     pub fn add_assignment(
356         &self,
357         tcx: TyCtxt<'tcx>,
358         lp: Rc<LoanPath<'tcx>>,
359         assign_id: hir::ItemLocalId,
360         span: Span,
361     ) {
362         // Assigning to one union field automatically assigns to all its fields.
363         if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
364             if let ty::Adt(adt_def, _) = base_lp.ty.sty {
365                 if adt_def.is_union() {
366                     for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() {
367                         let field =
368                             InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name));
369                         let field_ty = if field == interior {
370                             lp.ty
371                         } else {
372                             tcx.types.err // Doesn't matter
373                         };
374                         let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
375                                                     LpInterior(opt_variant_id, field));
376                         let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
377                         self.add_assignment_helper(tcx, sibling_lp, assign_id,
378                                                    span);
379                     }
380                     return;
381                 }
382             }
383         }
384
385         self.add_assignment_helper(tcx, lp, assign_id, span);
386     }
387
388     fn add_assignment_helper(
389         &self,
390         tcx: TyCtxt<'tcx>,
391         lp: Rc<LoanPath<'tcx>>,
392         assign_id: hir::ItemLocalId,
393         span: Span,
394     ) {
395         debug!("add_assignment(lp={:?}, assign_id={:?}", lp, assign_id);
396
397         let path_index = self.move_path(tcx, lp.clone());
398
399         let assignment = Assignment {
400             path: path_index,
401             id: assign_id,
402             span,
403         };
404
405         if self.is_var_path(path_index) {
406             debug!("add_assignment[var](lp={:?}, assignment={}, path_index={:?})",
407                    lp, self.var_assignments.borrow().len(), path_index);
408
409             self.var_assignments.borrow_mut().push(assignment);
410         } else {
411             debug!("add_assignment[path](lp={:?}, path_index={:?})",
412                    lp, path_index);
413
414             self.path_assignments.borrow_mut().push(assignment);
415         }
416     }
417
418     /// Adds the gen/kills for the various moves and
419     /// assignments into the provided data flow contexts.
420     /// Moves are generated by moves and killed by assignments and
421     /// scoping. Assignments are generated by assignment to variables and
422     /// killed by scoping. See `README.md` for more details.
423     fn add_gen_kills(
424         &self,
425         bccx: &BorrowckCtxt<'_, 'tcx>,
426         dfcx_moves: &mut MoveDataFlow<'_>,
427         dfcx_assign: &mut AssignDataFlow<'_>,
428     ) {
429         for (i, the_move) in self.moves.borrow().iter().enumerate() {
430             dfcx_moves.add_gen(the_move.id, i);
431         }
432
433         for (i, assignment) in self.var_assignments.borrow().iter().enumerate() {
434             dfcx_assign.add_gen(assignment.id, i);
435             self.kill_moves(assignment.path, assignment.id,
436                             KillFrom::Execution, dfcx_moves);
437         }
438
439         for assignment in self.path_assignments.borrow().iter() {
440             self.kill_moves(assignment.path, assignment.id,
441                             KillFrom::Execution, dfcx_moves);
442         }
443
444         // Kill all moves related to a variable `x` when
445         // it goes out of scope:
446         for path in self.paths.borrow().iter() {
447             match path.loan_path.kind {
448                 LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
449                     let kill_scope = path.loan_path.kill_scope(bccx);
450                     let path = *self.path_map.borrow().get(&path.loan_path).unwrap();
451                     self.kill_moves(path, kill_scope.item_local_id(),
452                                     KillFrom::ScopeEnd, dfcx_moves);
453                 }
454                 LpExtend(..) => {}
455             }
456         }
457
458         // Kill all assignments when the variable goes out of scope:
459         for (assignment_index, assignment) in
460                 self.var_assignments.borrow().iter().enumerate() {
461             let lp = self.path_loan_path(assignment.path);
462             match lp.kind {
463                 LpVar(..) | LpUpvar(..) | LpDowncast(..) => {
464                     let kill_scope = lp.kill_scope(bccx);
465                     dfcx_assign.add_kill(KillFrom::ScopeEnd,
466                                          kill_scope.item_local_id(),
467                                          assignment_index);
468                 }
469                 LpExtend(..) => {
470                     bug!("var assignment for non var path");
471                 }
472             }
473         }
474     }
475
476     fn each_base_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where
477         F: FnMut(MovePathIndex) -> bool,
478     {
479         let mut p = index;
480         while p != InvalidMovePathIndex {
481             if !f(p) {
482                 return false;
483             }
484             p = self.path_parent(p);
485         }
486         return true;
487     }
488
489     // FIXME(#19596) This is a workaround, but there should be better way to do this
490     fn each_extending_path_<F>(&self, index: MovePathIndex, f: &mut F) -> bool where
491         F: FnMut(MovePathIndex) -> bool,
492     {
493         if !(*f)(index) {
494             return false;
495         }
496
497         let mut p = self.path_first_child(index);
498         while p != InvalidMovePathIndex {
499             if !self.each_extending_path_(p, f) {
500                 return false;
501             }
502             p = self.path_next_sibling(p);
503         }
504
505         return true;
506     }
507
508     fn each_extending_path<F>(&self, index: MovePathIndex, mut f: F) -> bool where
509         F: FnMut(MovePathIndex) -> bool,
510     {
511         self.each_extending_path_(index, &mut f)
512     }
513
514     fn each_applicable_move<F>(&self, index0: MovePathIndex, mut f: F) -> bool where
515         F: FnMut(MoveIndex) -> bool,
516     {
517         let mut ret = true;
518         self.each_extending_path(index0, |index| {
519             let mut p = self.path_first_move(index);
520             while p != InvalidMoveIndex {
521                 if !f(p) {
522                     ret = false;
523                     break;
524                 }
525                 p = self.move_next_move(p);
526             }
527             ret
528         });
529         ret
530     }
531
532     fn kill_moves(
533         &self,
534         path: MovePathIndex,
535         kill_id: hir::ItemLocalId,
536         kill_kind: KillFrom,
537         dfcx_moves: &mut MoveDataFlow<'_>,
538     ) {
539         // We can only perform kills for paths that refer to a unique location,
540         // since otherwise we may kill a move from one location with an
541         // assignment referring to another location.
542
543         let loan_path = self.path_loan_path(path);
544         if loan_path_is_precise(&loan_path) {
545             self.each_applicable_move(path, |move_index| {
546                 debug!("kill_moves add_kill {:?} kill_id={:?} move_index={}",
547                        kill_kind, kill_id, move_index.get());
548                 dfcx_moves.add_kill(kill_kind, kill_id, move_index.get());
549                 true
550             });
551         }
552     }
553 }
554
555 impl<'tcx> FlowedMoveData<'tcx> {
556     pub fn new(
557         move_data: MoveData<'tcx>,
558         bccx: &BorrowckCtxt<'_, 'tcx>,
559         cfg: &cfg::CFG,
560         body: &hir::Body,
561     ) -> FlowedMoveData<'tcx> {
562         let tcx = bccx.tcx;
563
564         let mut dfcx_moves =
565             DataFlowContext::new(tcx,
566                                  "flowed_move_data_moves",
567                                  Some(body),
568                                  cfg,
569                                  MoveDataFlowOperator,
570                                  move_data.moves.borrow().len());
571         let mut dfcx_assign =
572             DataFlowContext::new(tcx,
573                                  "flowed_move_data_assigns",
574                                  Some(body),
575                                  cfg,
576                                  AssignDataFlowOperator,
577                                  move_data.var_assignments.borrow().len());
578
579         move_data.add_gen_kills(bccx,
580                                 &mut dfcx_moves,
581                                 &mut dfcx_assign);
582
583         dfcx_moves.add_kills_from_flow_exits(cfg);
584         dfcx_assign.add_kills_from_flow_exits(cfg);
585
586         dfcx_moves.propagate(cfg, body);
587         dfcx_assign.propagate(cfg, body);
588
589         FlowedMoveData {
590             move_data,
591             dfcx_moves,
592             dfcx_assign,
593         }
594     }
595
596     pub fn is_move_path(&self, id: hir::ItemLocalId, loan_path: &Rc<LoanPath<'tcx>>) -> bool {
597         //! Returns the kind of a move of `loan_path` by `id`, if one exists.
598
599         let mut ret = false;
600         if let Some(loan_path_index) = self.move_data.path_map.borrow().get(&*loan_path) {
601             self.dfcx_moves.each_gen_bit(id, |move_index| {
602                 let the_move = self.move_data.moves.borrow();
603                 let the_move = (*the_move)[move_index];
604                 if the_move.path == *loan_path_index {
605                     ret = true;
606                     false
607                 } else {
608                     true
609                 }
610             });
611         }
612         ret
613     }
614
615     /// Iterates through each move of `loan_path` (or some base path of `loan_path`) that *may*
616     /// have occurred on entry to `id` without an intervening assignment. In other words, any moves
617     /// that would invalidate a reference to `loan_path` at location `id`.
618     pub fn each_move_of<F>(&self,
619                            id: hir::ItemLocalId,
620                            loan_path: &Rc<LoanPath<'tcx>>,
621                            mut f: F)
622                            -> bool where
623         F: FnMut(&Move, &LoanPath<'tcx>) -> bool,
624     {
625         // Bad scenarios:
626         //
627         // 1. Move of `a.b.c`, use of `a.b.c`
628         // 2. Move of `a.b.c`, use of `a.b.c.d`
629         // 3. Move of `a.b.c`, use of `a` or `a.b`
630         //
631         // OK scenario:
632         //
633         // 4. move of `a.b.c`, use of `a.b.d`
634
635         let base_indices = self.move_data.existing_base_paths(loan_path);
636         if base_indices.is_empty() {
637             return true;
638         }
639
640         let opt_loan_path_index = self.move_data.existing_move_path(loan_path);
641
642         let mut ret = true;
643
644         self.dfcx_moves.each_bit_on_entry(id, |index| {
645             let the_move = self.move_data.moves.borrow();
646             let the_move = &(*the_move)[index];
647             let moved_path = the_move.path;
648             if base_indices.iter().any(|x| x == &moved_path) {
649                 // Scenario 1 or 2: `loan_path` or some base path of
650                 // `loan_path` was moved.
651                 if !f(the_move, &self.move_data.path_loan_path(moved_path)) {
652                     ret = false;
653                 }
654             } else {
655                 if let Some(loan_path_index) = opt_loan_path_index {
656                     let cont = self.move_data.each_base_path(moved_path, |p| {
657                         if p == loan_path_index {
658                             // Scenario 3: some extension of `loan_path`
659                             // was moved
660                             f(the_move,
661                               &self.move_data.path_loan_path(moved_path))
662                         } else {
663                             true
664                         }
665                     });
666                     if !cont { ret = false; }
667                 }
668             }
669             ret
670         })
671     }
672
673     /// Iterates through every assignment to `loan_path` that may have occurred on entry to `id`.
674     /// `loan_path` must be a single variable.
675     pub fn each_assignment_of<F>(&self,
676                                  id: hir::ItemLocalId,
677                                  loan_path: &Rc<LoanPath<'tcx>>,
678                                  mut f: F)
679                                  -> bool where
680         F: FnMut(&Assignment) -> bool,
681     {
682         let loan_path_index = {
683             match self.move_data.existing_move_path(loan_path) {
684                 Some(i) => i,
685                 None => {
686                     // if there were any assignments, it'd have an index
687                     return true;
688                 }
689             }
690         };
691
692         self.dfcx_assign.each_bit_on_entry(id, |index| {
693             let assignment = self.move_data.var_assignments.borrow();
694             let assignment = &(*assignment)[index];
695             if assignment.path == loan_path_index && !f(assignment) {
696                 false
697             } else {
698                 true
699             }
700         })
701     }
702 }
703
704 impl BitwiseOperator for MoveDataFlowOperator {
705     #[inline]
706     fn join(&self, succ: usize, pred: usize) -> usize {
707         succ | pred // moves from both preds are in scope
708     }
709 }
710
711 impl DataFlowOperator for MoveDataFlowOperator {
712     #[inline]
713     fn initial_value(&self) -> bool {
714         false // no loans in scope by default
715     }
716 }
717
718 impl BitwiseOperator for AssignDataFlowOperator {
719     #[inline]
720     fn join(&self, succ: usize, pred: usize) -> usize {
721         succ | pred // moves from both preds are in scope
722     }
723 }
724
725 impl DataFlowOperator for AssignDataFlowOperator {
726     #[inline]
727     fn initial_value(&self) -> bool {
728         false // no assignments in scope by default
729     }
730 }