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