rustc_mir_dataflow/move_paths/
builder.rs1use std::mem;
2
3use rustc_index::IndexVec;
4use rustc_middle::mir::*;
5use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
6use rustc_middle::{bug, span_bug};
7use smallvec::{SmallVec, smallvec};
8use tracing::debug;
9
10use super::{
11 Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut,
12 MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, MoveSubPath, MoveSubPathResult,
13};
14
15struct MoveDataBuilder<'a, 'tcx, F> {
16 body: &'a Body<'tcx>,
17 loc: Location,
18 tcx: TyCtxt<'tcx>,
19 data: MoveData<'tcx>,
20 filter: F,
21}
22
23impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
24 fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, filter: F) -> Self {
25 let mut move_paths = IndexVec::new();
26 let mut path_map = IndexVec::new();
27 let mut init_path_map = IndexVec::new();
28
29 let locals = body
30 .local_decls
31 .iter_enumerated()
32 .map(|(i, l)| {
33 if l.is_deref_temp() {
34 return None;
35 }
36 if filter(l.ty) {
37 Some(new_move_path(
38 &mut move_paths,
39 &mut path_map,
40 &mut init_path_map,
41 None,
42 Place::from(i),
43 ))
44 } else {
45 None
46 }
47 })
48 .collect();
49
50 MoveDataBuilder {
51 body,
52 loc: Location::START,
53 tcx,
54 data: MoveData {
55 moves: IndexVec::new(),
56 loc_map: LocationMap::new(body),
57 rev_lookup: MovePathLookup {
58 locals,
59 projections: Default::default(),
60 un_derefer: Default::default(),
61 },
62 move_paths,
63 path_map,
64 inits: IndexVec::new(),
65 init_loc_map: LocationMap::new(body),
66 init_path_map,
67 },
68 filter,
69 }
70 }
71}
72
73fn new_move_path<'tcx>(
74 move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
75 path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
76 init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
77 parent: Option<MovePathIndex>,
78 place: Place<'tcx>,
79) -> MovePathIndex {
80 let move_path =
81 move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place });
82
83 if let Some(parent) = parent {
84 let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path));
85 move_paths[move_path].next_sibling = next_sibling;
86 }
87
88 let path_map_ent = path_map.push(smallvec![]);
89 assert_eq!(path_map_ent, move_path);
90
91 let init_path_map_ent = init_path_map.push(smallvec![]);
92 assert_eq!(init_path_map_ent, move_path);
93
94 move_path
95}
96
97impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
98 fn move_path_for<G>(&mut self, place: Place<'tcx>, mut on_move: G)
108 where
109 G: FnMut(&mut Self, MovePathIndex),
110 {
111 let data = &mut self.data;
112
113 debug!("lookup({:?})", place);
114 let Some(mut base) = data.rev_lookup.find_local(place.local) else {
115 return;
116 };
117
118 let mut union_path = None;
124
125 let mut iter = data.rev_lookup.un_derefer.iter_projections(place.as_ref());
126 while let Some((place_ref, elem)) = iter.next() {
127 let body = self.body;
128 let tcx = self.tcx;
129 let place_ty = place_ref.ty(body, tcx).ty;
130 if place_ty.references_error() {
131 return;
132 }
133
134 let res = MoveSubPath::of(elem.kind());
135
136 let move_elem = match res {
137 MoveSubPathResult::One(move_elem) => {
138 match move_elem {
139 MoveSubPath::Deref => match place_ty.kind() {
140 ty::Ref(..) | ty::RawPtr(..) => {
141 return;
142 }
143 ty::Adt(adt, _) => {
144 if !adt.is_box() {
145 bug!("Adt should be a box type when Place is deref");
146 }
147 }
148 ty::Bool
149 | ty::Char
150 | ty::Int(_)
151 | ty::Uint(_)
152 | ty::Float(_)
153 | ty::Foreign(_)
154 | ty::Str
155 | ty::Array(_, _)
156 | ty::Pat(_, _)
157 | ty::Slice(_)
158 | ty::FnDef(_, _)
159 | ty::FnPtr(..)
160 | ty::Dynamic(_, _)
161 | ty::Closure(..)
162 | ty::CoroutineClosure(..)
163 | ty::Coroutine(_, _)
164 | ty::CoroutineWitness(..)
165 | ty::Never
166 | ty::Tuple(_)
167 | ty::UnsafeBinder(_)
168 | ty::Alias(_, _)
169 | ty::Param(_)
170 | ty::Bound(_, _)
171 | ty::Infer(_)
172 | ty::Error(_)
173 | ty::Placeholder(_) => {
174 bug!("When Place is Deref it's type shouldn't be {place_ty:#?}")
175 }
176 },
177 MoveSubPath::Field(_) => match place_ty.kind() {
178 ty::Adt(adt, _) => {
179 if adt.has_dtor(tcx) {
180 return;
181 }
182 if adt.is_union() {
183 union_path.get_or_insert(base);
184 }
185 }
186 ty::Closure(..)
187 | ty::CoroutineClosure(..)
188 | ty::Coroutine(_, _)
189 | ty::Tuple(_) => (),
190 ty::Bool
191 | ty::Char
192 | ty::Int(_)
193 | ty::Uint(_)
194 | ty::Float(_)
195 | ty::Foreign(_)
196 | ty::Str
197 | ty::Array(_, _)
198 | ty::Pat(_, _)
199 | ty::Slice(_)
200 | ty::RawPtr(_, _)
201 | ty::Ref(_, _, _)
202 | ty::FnDef(_, _)
203 | ty::FnPtr(..)
204 | ty::Dynamic(_, _)
205 | ty::CoroutineWitness(..)
206 | ty::Never
207 | ty::UnsafeBinder(_)
208 | ty::Alias(_, _)
209 | ty::Param(_)
210 | ty::Bound(_, _)
211 | ty::Infer(_)
212 | ty::Error(_)
213 | ty::Placeholder(_) => bug!(
214 "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}"
215 ),
216 },
217 MoveSubPath::ConstantIndex(_) => match place_ty.kind() {
218 ty::Slice(_) => {
219 return;
220 }
221 ty::Array(_, _) => (),
222 _ => bug!("Unexpected type {:#?}", place_ty.is_array()),
223 },
224 MoveSubPath::Downcast(_) => (),
225 MoveSubPath::UnwrapUnsafeBinder => (),
226 };
227
228 move_elem
229 }
230
231 MoveSubPathResult::Subslice { from, to } => {
235 assert!(
236 iter.all(
237 |(_, elem)| MoveSubPath::of(elem.kind()) == MoveSubPathResult::Skip
238 )
239 );
240 drop(iter); let (&elem_ty, len) = match place_ty.kind() {
243 ty::Array(ty, size) => (
244 ty,
245 size.try_to_target_usize(self.tcx)
246 .expect("expected subslice projection on fixed-size array"),
247 ),
248 _ => bug!("from_end: false slice pattern of non-array type"),
249 };
250
251 if !(self.filter)(elem_ty) {
252 return;
253 }
254
255 for offset in from..to {
256 let place_elem =
257 PlaceElem::ConstantIndex { offset, min_length: len, from_end: false };
258 let subpath_elem = MoveSubPath::ConstantIndex(offset);
259
260 let mpi = self.add_move_path(base, subpath_elem, |tcx| {
261 place_ref.project_deeper(&[place_elem], tcx)
262 });
263 on_move(self, mpi);
264 }
265
266 return;
267 }
268
269 MoveSubPathResult::Skip => continue,
270 MoveSubPathResult::Stop => return,
271 };
272
273 let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty;
274 if !(self.filter)(elem_ty) {
275 return;
276 }
277 if union_path.is_none() {
278 base = *data.rev_lookup.projections.entry((base, move_elem)).or_insert_with(|| {
280 new_move_path(
281 &mut data.move_paths,
282 &mut data.path_map,
283 &mut data.init_path_map,
284 Some(base),
285 place_ref.project_deeper(&[elem], tcx),
286 )
287 })
288 }
289 }
290
291 drop(iter); if let Some(base) = union_path {
294 on_move(self, base);
296 } else {
297 on_move(self, base);
298 }
299 }
300
301 fn add_move_path(
302 &mut self,
303 base: MovePathIndex,
304 elem: MoveSubPath,
305 mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>,
306 ) -> MovePathIndex {
307 let MoveDataBuilder {
308 data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. },
309 tcx,
310 ..
311 } = self;
312 *rev_lookup.projections.entry((base, elem)).or_insert_with(move || {
313 new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
314 })
315 }
316
317 fn create_move_path(&mut self, place: Place<'tcx>) {
318 self.move_path_for(place, |_, _| ());
321 }
322
323 fn finalize(self) -> MoveData<'tcx> {
324 debug!("{}", {
325 debug!("moves for {:?}:", self.body.span);
326 for (j, mo) in self.data.moves.iter_enumerated() {
327 debug!(" {:?} = {:?}", j, mo);
328 }
329 debug!("move paths for {:?}:", self.body.span);
330 for (j, path) in self.data.move_paths.iter_enumerated() {
331 debug!(" {:?} = {:?}", j, path);
332 }
333 "done dumping moves"
334 });
335
336 self.data
337 }
338}
339
340pub(super) fn gather_moves<'tcx>(
341 body: &Body<'tcx>,
342 tcx: TyCtxt<'tcx>,
343 filter: impl Fn(Ty<'tcx>) -> bool,
344) -> MoveData<'tcx> {
345 let mut builder = MoveDataBuilder::new(body, tcx, filter);
346
347 builder.gather_args();
348
349 for (bb, block) in body.basic_blocks.iter_enumerated() {
350 for (i, stmt) in block.statements.iter().enumerate() {
351 builder.loc = Location { block: bb, statement_index: i };
352 builder.gather_statement(stmt);
353 }
354
355 builder.loc = Location { block: bb, statement_index: block.statements.len() };
356 builder.gather_terminator(block.terminator());
357 }
358
359 builder.finalize()
360}
361
362impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
363 fn gather_args(&mut self) {
364 for arg in self.body.args_iter() {
365 if let Some(path) = self.data.rev_lookup.find_local(arg) {
366 let init = self.data.inits.push(Init {
367 path,
368 kind: InitKind::Deep,
369 location: InitLocation::Argument(arg),
370 });
371
372 debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
373
374 self.data.init_path_map[path].push(init);
375 }
376 }
377 }
378
379 fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
380 debug!("gather_statement({:?}, {:?})", self.loc, stmt);
381 match &stmt.kind {
382 StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
383 let local = place.as_local().unwrap();
384 assert!(self.body.local_decls[local].is_deref_temp());
385
386 let rev_lookup = &mut self.data.rev_lookup;
387
388 rev_lookup.un_derefer.insert(local, reffed.as_ref());
389 let base_local = rev_lookup.un_derefer.deref_chain(local).first().unwrap().local;
390 rev_lookup.locals[local] = rev_lookup.locals[base_local];
391 }
392 StatementKind::Assign(box (place, rval)) => {
393 self.create_move_path(*place);
394 if let RvalueInitializationState::Shallow = rval.initialization_state() {
395 self.create_move_path(self.tcx.mk_place_deref(*place));
399 self.gather_init(place.as_ref(), InitKind::Shallow);
400 } else {
401 self.gather_init(place.as_ref(), InitKind::Deep);
402 }
403 self.gather_rvalue(rval);
404 }
405 StatementKind::FakeRead(box (_, place)) => {
406 self.create_move_path(*place);
407 }
408 StatementKind::StorageLive(_) => {}
409 StatementKind::StorageDead(local) => {
410 if !self.body.local_decls[*local].is_deref_temp() {
412 self.gather_move(Place::from(*local));
413 }
414 }
415 StatementKind::SetDiscriminant { .. } => {
416 span_bug!(
417 stmt.source_info.span,
418 "SetDiscriminant/Deinit should not exist during borrowck"
419 );
420 }
421 StatementKind::Retag { .. }
422 | StatementKind::AscribeUserType(..)
423 | StatementKind::PlaceMention(..)
424 | StatementKind::Coverage(..)
425 | StatementKind::Intrinsic(..)
426 | StatementKind::ConstEvalCounter
427 | StatementKind::BackwardIncompatibleDropHint { .. }
428 | StatementKind::Nop => {}
429 }
430 }
431
432 fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
433 match *rvalue {
434 Rvalue::ThreadLocalRef(_) => {} Rvalue::Use(ref operand)
436 | Rvalue::Repeat(ref operand, _)
437 | Rvalue::Cast(_, ref operand, _)
438 | Rvalue::ShallowInitBox(ref operand, _)
439 | Rvalue::UnaryOp(_, ref operand)
440 | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand),
441 Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => {
442 self.gather_operand(lhs);
443 self.gather_operand(rhs);
444 }
445 Rvalue::Aggregate(ref _kind, ref operands) => {
446 for operand in operands {
447 self.gather_operand(operand);
448 }
449 }
450 Rvalue::CopyForDeref(..) => unreachable!(),
451 Rvalue::Ref(..)
452 | Rvalue::RawPtr(..)
453 | Rvalue::Discriminant(..)
454 | Rvalue::NullaryOp(
455 NullOp::OffsetOf(..) | NullOp::UbChecks | NullOp::ContractChecks,
456 _,
457 ) => {}
458 }
459 }
460
461 fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
462 debug!("gather_terminator({:?}, {:?})", self.loc, term);
463 match term.kind {
464 TerminatorKind::Goto { target: _ }
465 | TerminatorKind::FalseEdge { .. }
466 | TerminatorKind::FalseUnwind { .. }
467 | TerminatorKind::Return
472 | TerminatorKind::UnwindResume
473 | TerminatorKind::UnwindTerminate(_)
474 | TerminatorKind::CoroutineDrop
475 | TerminatorKind::Unreachable
476 | TerminatorKind::Drop { .. } => {}
477
478 TerminatorKind::Assert { ref cond, .. } => {
479 self.gather_operand(cond);
480 }
481
482 TerminatorKind::SwitchInt { ref discr, .. } => {
483 self.gather_operand(discr);
484 }
485
486 TerminatorKind::Yield { ref value, resume_arg: place, .. } => {
487 self.gather_operand(value);
488 self.create_move_path(place);
489 self.gather_init(place.as_ref(), InitKind::Deep);
490 }
491 TerminatorKind::Call {
492 ref func,
493 ref args,
494 destination,
495 target,
496 unwind: _,
497 call_source: _,
498 fn_span: _,
499 } => {
500 self.gather_operand(func);
501 for arg in args {
502 self.gather_operand(&arg.node);
503 }
504 if let Some(_bb) = target {
505 self.create_move_path(destination);
506 self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
507 }
508 }
509 TerminatorKind::TailCall { ref func, ref args, .. } => {
510 self.gather_operand(func);
511 for arg in args {
512 self.gather_operand(&arg.node);
513 }
514 }
515 TerminatorKind::InlineAsm {
516 asm_macro: _,
517 template: _,
518 ref operands,
519 options: _,
520 line_spans: _,
521 targets: _,
522 unwind: _,
523 } => {
524 for op in operands {
525 match *op {
526 InlineAsmOperand::In { reg: _, ref value }
527 => {
528 self.gather_operand(value);
529 }
530 InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
531 if let Some(place) = place {
532 self.create_move_path(place);
533 self.gather_init(place.as_ref(), InitKind::Deep);
534 }
535 }
536 InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
537 self.gather_operand(in_value);
538 if let Some(out_place) = out_place {
539 self.create_move_path(out_place);
540 self.gather_init(out_place.as_ref(), InitKind::Deep);
541 }
542 }
543 InlineAsmOperand::Const { value: _ }
544 | InlineAsmOperand::SymFn { value: _ }
545 | InlineAsmOperand::SymStatic { def_id: _ }
546 | InlineAsmOperand::Label { target_index: _ } => {}
547 }
548 }
549 }
550 }
551 }
552
553 fn gather_operand(&mut self, operand: &Operand<'tcx>) {
554 match *operand {
555 Operand::Constant(..) | Operand::Copy(..) => {} Operand::Move(place) => {
557 self.gather_move(place);
559 }
560 }
561 }
562
563 fn gather_move(&mut self, place: Place<'tcx>) {
564 debug!("gather_move({:?}, {:?})", self.loc, place);
565 self.move_path_for(place, |this, mpi| this.record_move(place, mpi));
566 }
567
568 fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) {
569 let move_out = self.data.moves.push(MoveOut { path, source: self.loc });
570 debug!(
571 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
572 self.loc, place, move_out, path
573 );
574 self.data.path_map[path].push(move_out);
575 self.data.loc_map[self.loc].push(move_out);
576 }
577
578 fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) {
579 debug!("gather_init({:?}, {:?})", self.loc, place);
580
581 let mut place = place;
582
583 if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() {
586 if place_base.ty(self.body, self.tcx).ty.is_union() {
587 place = place_base;
588 }
589 }
590
591 if let LookupResult::Exact(path) = self.data.rev_lookup.find(place) {
592 let init = self.data.inits.push(Init {
593 location: InitLocation::Statement(self.loc),
594 path,
595 kind,
596 });
597
598 debug!(
599 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
600 self.loc, place, init, path
601 );
602
603 self.data.init_path_map[path].push(init);
604 self.data.init_loc_map[self.loc].push(init);
605 }
606 }
607}