]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | //! Data structures used for tracking moves. Please see the extensive |
c34b1796 | 2 | //! comments in the section "Moves and initialization" in `README.md`. |
1a4d82fc | 3 | |
9fa01778 | 4 | use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom}; |
b7449926 | 5 | |
9fa01778 | 6 | use crate::borrowck::*; |
e1599b0c | 7 | use crate::cfg; |
9e0c209e | 8 | use rustc::ty::{self, TyCtxt}; |
b7449926 | 9 | use rustc::util::nodemap::FxHashMap; |
62682a34 | 10 | |
1a4d82fc JJ |
11 | use std::cell::RefCell; |
12 | use std::rc::Rc; | |
85aaf69f | 13 | use std::usize; |
3157f602 | 14 | use syntax_pos::Span; |
54a0048b | 15 | use rustc::hir; |
9fa01778 | 16 | use log::debug; |
1a4d82fc | 17 | |
ea8adc8c | 18 | #[derive(Default)] |
1a4d82fc | 19 | pub struct MoveData<'tcx> { |
c34b1796 | 20 | /// Move paths. See section "Move paths" in `README.md`. |
1a4d82fc JJ |
21 | pub paths: RefCell<Vec<MovePath<'tcx>>>, |
22 | ||
23 | /// Cache of loan path to move path index, for easy lookup. | |
476ff2be | 24 | pub path_map: RefCell<FxHashMap<Rc<LoanPath<'tcx>>, MovePathIndex>>, |
1a4d82fc JJ |
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>>, | |
1a4d82fc JJ |
38 | } |
39 | ||
dc9dc135 | 40 | pub struct FlowedMoveData<'tcx> { |
1a4d82fc JJ |
41 | pub move_data: MoveData<'tcx>, |
42 | ||
dc9dc135 | 43 | pub dfcx_moves: MoveDataFlow<'tcx>, |
1a4d82fc JJ |
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. | |
dc9dc135 | 48 | pub dfcx_assign: AssignDataFlow<'tcx>, |
1a4d82fc JJ |
49 | } |
50 | ||
51 | /// Index into `MoveData.paths`, used like a pointer | |
85aaf69f | 52 | #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] |
c34b1796 | 53 | pub struct MovePathIndex(usize); |
1a4d82fc JJ |
54 | |
55 | impl MovePathIndex { | |
c34b1796 | 56 | fn get(&self) -> usize { |
1a4d82fc JJ |
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)] | |
c34b1796 | 68 | const InvalidMovePathIndex: MovePathIndex = MovePathIndex(usize::MAX); |
1a4d82fc JJ |
69 | |
70 | /// Index into `MoveData.moves`, used like a pointer | |
c34b1796 AL |
71 | #[derive(Copy, Clone, PartialEq)] |
72 | pub struct MoveIndex(usize); | |
1a4d82fc JJ |
73 | |
74 | impl MoveIndex { | |
c34b1796 | 75 | fn get(&self) -> usize { |
1a4d82fc JJ |
76 | let MoveIndex(v) = *self; v |
77 | } | |
78 | } | |
79 | ||
80 | #[allow(non_upper_case_globals)] | |
c34b1796 | 81 | const InvalidMoveIndex: MoveIndex = MoveIndex(usize::MAX); |
1a4d82fc JJ |
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 | ||
1a4d82fc | 102 | |
c34b1796 | 103 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
104 | pub struct Move { |
105 | /// Path being moved. | |
106 | pub path: MovePathIndex, | |
107 | ||
9fa01778 | 108 | /// ID of node that is doing the move. |
ea8adc8c | 109 | pub id: hir::ItemLocalId, |
1a4d82fc | 110 | |
1a4d82fc JJ |
111 | /// Next node in linked list of moves from `path`, or `InvalidMoveIndex` |
112 | pub next_move: MoveIndex | |
113 | } | |
114 | ||
c34b1796 | 115 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
116 | pub struct Assignment { |
117 | /// Path being assigned. | |
118 | pub path: MovePathIndex, | |
119 | ||
9fa01778 | 120 | /// ID where assignment occurs |
ea8adc8c | 121 | pub id: hir::ItemLocalId, |
1a4d82fc JJ |
122 | |
123 | /// span of node where assignment occurs | |
124 | pub span: Span, | |
125 | } | |
126 | ||
1a4d82fc JJ |
127 | #[derive(Clone, Copy)] |
128 | pub struct MoveDataFlowOperator; | |
129 | ||
dc9dc135 | 130 | pub type MoveDataFlow<'tcx> = DataFlowContext<'tcx, MoveDataFlowOperator>; |
1a4d82fc JJ |
131 | |
132 | #[derive(Clone, Copy)] | |
133 | pub struct AssignDataFlowOperator; | |
134 | ||
dc9dc135 | 135 | pub type AssignDataFlow<'tcx> = DataFlowContext<'tcx, AssignDataFlowOperator>; |
1a4d82fc | 136 | |
9fa01778 | 137 | fn loan_path_is_precise(loan_path: &LoanPath<'_>) -> bool { |
1a4d82fc JJ |
138 | match loan_path.kind { |
139 | LpVar(_) | LpUpvar(_) => { | |
140 | true | |
141 | } | |
7cac9316 | 142 | LpExtend(.., LpInterior(_, InteriorKind::InteriorElement)) => { |
85aaf69f | 143 | // Paths involving element accesses a[i] do not refer to a unique |
1a4d82fc | 144 | // location, as there is no accurate tracking of the indices. |
85aaf69f SL |
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.) | |
1a4d82fc JJ |
149 | false |
150 | } | |
151 | LpDowncast(ref lp_base, _) | | |
9e0c209e | 152 | LpExtend(ref lp_base, ..) => { |
7453a54e | 153 | loan_path_is_precise(&lp_base) |
1a4d82fc JJ |
154 | } |
155 | } | |
156 | } | |
157 | ||
dc9dc135 | 158 | impl MoveData<'tcx> { |
9fa01778 XL |
159 | /// Returns `true` if there are no trackable assignments or moves |
160 | /// in this move data -- that means that there is nothing that | |
3b2f2976 XL |
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 | ||
1a4d82fc JJ |
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. | |
dc9dc135 | 214 | pub fn move_path(&self, tcx: TyCtxt<'tcx>, lp: Rc<LoanPath<'tcx>>) -> MovePathIndex { |
3157f602 XL |
215 | if let Some(&index) = self.path_map.borrow().get(&lp) { |
216 | return index; | |
1a4d82fc JJ |
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, _) | | |
9e0c209e | 235 | LpExtend(ref base, ..) => { |
1a4d82fc JJ |
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, | |
3b2f2976 | 248 | next_sibling, |
1a4d82fc JJ |
249 | }); |
250 | ||
251 | index | |
252 | } | |
253 | }; | |
254 | ||
62682a34 SL |
255 | debug!("move_path(lp={:?}, index={:?})", |
256 | lp, | |
1a4d82fc JJ |
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> { | |
c30ab7b3 | 271 | let mut result = vec![]; |
1a4d82fc JJ |
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, _) | | |
9e0c209e | 291 | LpExtend(ref b, ..) => { |
1a4d82fc JJ |
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`. | |
dc9dc135 XL |
301 | pub fn add_move( |
302 | &self, | |
303 | tcx: TyCtxt<'tcx>, | |
304 | orig_lp: Rc<LoanPath<'tcx>>, | |
305 | id: hir::ItemLocalId, | |
dc9dc135 | 306 | ) { |
cc61c64b XL |
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 { | |
b7449926 | 311 | if let (&ty::Adt(adt_def, _), LpInterior(opt_variant_id, interior)) |
cc61c64b | 312 | = (&base_lp.ty.sty, lp_elem) { |
9e0c209e | 313 | if adt_def.is_union() { |
83c7162d | 314 | for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() { |
94b46f34 XL |
315 | let field = |
316 | InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name)); | |
cc61c64b XL |
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)); | |
416331ca | 321 | self.add_move_helper(tcx, sibling_lp, id); |
cc61c64b | 322 | } |
9e0c209e | 323 | } |
9e0c209e SL |
324 | } |
325 | } | |
cc61c64b | 326 | lp = base_lp.clone(); |
9e0c209e SL |
327 | } |
328 | ||
416331ca | 329 | self.add_move_helper(tcx, orig_lp, id); |
9e0c209e SL |
330 | } |
331 | ||
dc9dc135 XL |
332 | fn add_move_helper( |
333 | &self, | |
334 | tcx: TyCtxt<'tcx>, | |
335 | lp: Rc<LoanPath<'tcx>>, | |
336 | id: hir::ItemLocalId, | |
dc9dc135 | 337 | ) { |
416331ca | 338 | debug!("add_move(lp={:?}, id={:?})", lp, id); |
1a4d82fc | 339 | |
0bf4aa26 | 340 | let path_index = self.move_path(tcx, lp); |
1a4d82fc JJ |
341 | let move_index = MoveIndex(self.moves.borrow().len()); |
342 | ||
1a4d82fc JJ |
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, | |
3b2f2976 | 348 | id, |
3b2f2976 | 349 | next_move, |
1a4d82fc JJ |
350 | }); |
351 | } | |
352 | ||
353 | /// Adds a new record for an assignment to `lp` that occurs at location `id` with the given | |
354 | /// `span`. | |
dc9dc135 XL |
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 | ) { | |
9e0c209e SL |
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 { | |
b7449926 | 364 | if let ty::Adt(adt_def, _) = base_lp.ty.sty { |
9e0c209e | 365 | if adt_def.is_union() { |
83c7162d | 366 | for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() { |
94b46f34 XL |
367 | let field = |
368 | InteriorKind::InteriorField(mc::FieldIndex(i, field.ident.name)); | |
9e0c209e SL |
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, | |
b7449926 | 378 | span); |
9e0c209e SL |
379 | } |
380 | return; | |
381 | } | |
382 | } | |
383 | } | |
384 | ||
0bf4aa26 | 385 | self.add_assignment_helper(tcx, lp, assign_id, span); |
9e0c209e SL |
386 | } |
387 | ||
dc9dc135 XL |
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 | ) { | |
b7449926 | 395 | debug!("add_assignment(lp={:?}, assign_id={:?}", lp, assign_id); |
1a4d82fc JJ |
396 | |
397 | let path_index = self.move_path(tcx, lp.clone()); | |
398 | ||
1a4d82fc JJ |
399 | let assignment = Assignment { |
400 | path: path_index, | |
401 | id: assign_id, | |
3b2f2976 | 402 | span, |
1a4d82fc JJ |
403 | }; |
404 | ||
405 | if self.is_var_path(path_index) { | |
62682a34 SL |
406 | debug!("add_assignment[var](lp={:?}, assignment={}, path_index={:?})", |
407 | lp, self.var_assignments.borrow().len(), path_index); | |
1a4d82fc JJ |
408 | |
409 | self.var_assignments.borrow_mut().push(assignment); | |
410 | } else { | |
62682a34 SL |
411 | debug!("add_assignment[path](lp={:?}, path_index={:?})", |
412 | lp, path_index); | |
1a4d82fc JJ |
413 | |
414 | self.path_assignments.borrow_mut().push(assignment); | |
415 | } | |
416 | } | |
417 | ||
1a4d82fc JJ |
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 | |
c34b1796 | 422 | /// killed by scoping. See `README.md` for more details. |
dc9dc135 XL |
423 | fn add_gen_kills( |
424 | &self, | |
425 | bccx: &BorrowckCtxt<'_, 'tcx>, | |
426 | dfcx_moves: &mut MoveDataFlow<'_>, | |
427 | dfcx_assign: &mut AssignDataFlow<'_>, | |
428 | ) { | |
1a4d82fc JJ |
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); | |
9346a6ac AL |
435 | self.kill_moves(assignment.path, assignment.id, |
436 | KillFrom::Execution, dfcx_moves); | |
1a4d82fc JJ |
437 | } |
438 | ||
62682a34 | 439 | for assignment in self.path_assignments.borrow().iter() { |
9346a6ac AL |
440 | self.kill_moves(assignment.path, assignment.id, |
441 | KillFrom::Execution, dfcx_moves); | |
1a4d82fc JJ |
442 | } |
443 | ||
444 | // Kill all moves related to a variable `x` when | |
445 | // it goes out of scope: | |
62682a34 | 446 | for path in self.paths.borrow().iter() { |
1a4d82fc JJ |
447 | match path.loan_path.kind { |
448 | LpVar(..) | LpUpvar(..) | LpDowncast(..) => { | |
7cac9316 | 449 | let kill_scope = path.loan_path.kill_scope(bccx); |
c34b1796 | 450 | let path = *self.path_map.borrow().get(&path.loan_path).unwrap(); |
ea8adc8c | 451 | self.kill_moves(path, kill_scope.item_local_id(), |
9346a6ac | 452 | KillFrom::ScopeEnd, dfcx_moves); |
1a4d82fc JJ |
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(..) => { | |
7cac9316 | 464 | let kill_scope = lp.kill_scope(bccx); |
9346a6ac | 465 | dfcx_assign.add_kill(KillFrom::ScopeEnd, |
ea8adc8c | 466 | kill_scope.item_local_id(), |
9346a6ac | 467 | assignment_index); |
1a4d82fc JJ |
468 | } |
469 | LpExtend(..) => { | |
54a0048b | 470 | bug!("var assignment for non var path"); |
1a4d82fc JJ |
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 | ||
dc9dc135 XL |
532 | fn kill_moves( |
533 | &self, | |
534 | path: MovePathIndex, | |
535 | kill_id: hir::ItemLocalId, | |
536 | kill_kind: KillFrom, | |
537 | dfcx_moves: &mut MoveDataFlow<'_>, | |
538 | ) { | |
1a4d82fc JJ |
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); | |
7453a54e | 544 | if loan_path_is_precise(&loan_path) { |
1a4d82fc | 545 | self.each_applicable_move(path, |move_index| { |
ea8adc8c | 546 | debug!("kill_moves add_kill {:?} kill_id={:?} move_index={}", |
9346a6ac AL |
547 | kill_kind, kill_id, move_index.get()); |
548 | dfcx_moves.add_kill(kill_kind, kill_id, move_index.get()); | |
1a4d82fc JJ |
549 | true |
550 | }); | |
551 | } | |
552 | } | |
553 | } | |
554 | ||
dc9dc135 XL |
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> { | |
7cac9316 XL |
562 | let tcx = bccx.tcx; |
563 | ||
1a4d82fc JJ |
564 | let mut dfcx_moves = |
565 | DataFlowContext::new(tcx, | |
566 | "flowed_move_data_moves", | |
32a655c1 | 567 | Some(body), |
1a4d82fc JJ |
568 | cfg, |
569 | MoveDataFlowOperator, | |
1a4d82fc JJ |
570 | move_data.moves.borrow().len()); |
571 | let mut dfcx_assign = | |
572 | DataFlowContext::new(tcx, | |
573 | "flowed_move_data_assigns", | |
32a655c1 | 574 | Some(body), |
1a4d82fc JJ |
575 | cfg, |
576 | AssignDataFlowOperator, | |
1a4d82fc JJ |
577 | move_data.var_assignments.borrow().len()); |
578 | ||
7cac9316 | 579 | move_data.add_gen_kills(bccx, |
1a4d82fc JJ |
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 { | |
3b2f2976 XL |
590 | move_data, |
591 | dfcx_moves, | |
592 | dfcx_assign, | |
1a4d82fc JJ |
593 | } |
594 | } | |
595 | ||
416331ca | 596 | pub fn is_move_path(&self, id: hir::ItemLocalId, loan_path: &Rc<LoanPath<'tcx>>) -> bool { |
1a4d82fc JJ |
597 | //! Returns the kind of a move of `loan_path` by `id`, if one exists. |
598 | ||
416331ca | 599 | let mut ret = false; |
85aaf69f | 600 | if let Some(loan_path_index) = self.move_data.path_map.borrow().get(&*loan_path) { |
1a4d82fc JJ |
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]; | |
85aaf69f | 604 | if the_move.path == *loan_path_index { |
416331ca | 605 | ret = true; |
1a4d82fc JJ |
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, | |
ea8adc8c | 619 | id: hir::ItemLocalId, |
1a4d82fc JJ |
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. | |
7453a54e | 651 | if !f(the_move, &self.move_data.path_loan_path(moved_path)) { |
1a4d82fc JJ |
652 | ret = false; |
653 | } | |
654 | } else { | |
85aaf69f | 655 | if let Some(loan_path_index) = opt_loan_path_index { |
1a4d82fc JJ |
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, | |
7453a54e | 661 | &self.move_data.path_loan_path(moved_path)) |
1a4d82fc JJ |
662 | } else { |
663 | true | |
664 | } | |
665 | }); | |
85aaf69f | 666 | if !cont { ret = false; } |
1a4d82fc JJ |
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, | |
ea8adc8c | 676 | id: hir::ItemLocalId, |
1a4d82fc JJ |
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] | |
c34b1796 | 706 | fn join(&self, succ: usize, pred: usize) -> usize { |
1a4d82fc JJ |
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] | |
c34b1796 | 720 | fn join(&self, succ: usize, pred: usize) -> usize { |
1a4d82fc JJ |
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 | } |