1use 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,
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
97enum MovePathResult {
98 Path(MovePathIndex),
99 Union(MovePathIndex),
100 Error,
101}
102
103impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
104 fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult {
112 let data = &mut self.data;
113
114 debug!("lookup({:?})", place);
115 let Some(mut base) = data.rev_lookup.find_local(place.local) else {
116 return MovePathResult::Error;
117 };
118
119 let mut union_path = None;
125
126 for (place_ref, elem) in data.rev_lookup.un_derefer.iter_projections(place.as_ref()) {
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 MovePathResult::Error;
132 }
133 match elem {
134 ProjectionElem::Deref => match place_ty.kind() {
135 ty::Ref(..) | ty::RawPtr(..) => {
136 return MovePathResult::Error;
137 }
138 ty::Adt(adt, _) => {
139 if !adt.is_box() {
140 bug!("Adt should be a box type when Place is deref");
141 }
142 }
143 ty::Bool
144 | ty::Char
145 | ty::Int(_)
146 | ty::Uint(_)
147 | ty::Float(_)
148 | ty::Foreign(_)
149 | ty::Str
150 | ty::Array(_, _)
151 | ty::Pat(_, _)
152 | ty::Slice(_)
153 | ty::FnDef(_, _)
154 | ty::FnPtr(..)
155 | ty::Dynamic(_, _, _)
156 | ty::Closure(..)
157 | ty::CoroutineClosure(..)
158 | ty::Coroutine(_, _)
159 | ty::CoroutineWitness(..)
160 | ty::Never
161 | ty::Tuple(_)
162 | ty::UnsafeBinder(_)
163 | ty::Alias(_, _)
164 | ty::Param(_)
165 | ty::Bound(_, _)
166 | ty::Infer(_)
167 | ty::Error(_)
168 | ty::Placeholder(_) => {
169 bug!("When Place is Deref it's type shouldn't be {place_ty:#?}")
170 }
171 },
172 ProjectionElem::Field(_, _) => match place_ty.kind() {
173 ty::Adt(adt, _) => {
174 if adt.has_dtor(tcx) {
175 return MovePathResult::Error;
176 }
177 if adt.is_union() {
178 union_path.get_or_insert(base);
179 }
180 }
181 ty::Closure(..)
182 | ty::CoroutineClosure(..)
183 | ty::Coroutine(_, _)
184 | ty::Tuple(_) => (),
185 ty::Bool
186 | ty::Char
187 | ty::Int(_)
188 | ty::Uint(_)
189 | ty::Float(_)
190 | ty::Foreign(_)
191 | ty::Str
192 | ty::Array(_, _)
193 | ty::Pat(_, _)
194 | ty::Slice(_)
195 | ty::RawPtr(_, _)
196 | ty::Ref(_, _, _)
197 | ty::FnDef(_, _)
198 | ty::FnPtr(..)
199 | ty::Dynamic(_, _, _)
200 | ty::CoroutineWitness(..)
201 | ty::Never
202 | ty::UnsafeBinder(_)
203 | ty::Alias(_, _)
204 | ty::Param(_)
205 | ty::Bound(_, _)
206 | ty::Infer(_)
207 | ty::Error(_)
208 | ty::Placeholder(_) => bug!(
209 "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}"
210 ),
211 },
212 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
213 match place_ty.kind() {
214 ty::Slice(_) => {
215 return MovePathResult::Error;
216 }
217 ty::Array(_, _) => (),
218 _ => bug!("Unexpected type {:#?}", place_ty.is_array()),
219 }
220 }
221 ProjectionElem::Index(_) => match place_ty.kind() {
222 ty::Array(..) | ty::Slice(_) => {
223 return MovePathResult::Error;
224 }
225 _ => bug!("Unexpected type {place_ty:#?}"),
226 },
227 ProjectionElem::UnwrapUnsafeBinder(_) => {}
228 ProjectionElem::OpaqueCast(_)
233 | ProjectionElem::Subtype(_)
234 | ProjectionElem::Downcast(_, _) => (),
235 }
236 let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty;
237 if !(self.filter)(elem_ty) {
238 return MovePathResult::Error;
239 }
240 if union_path.is_none() {
241 base =
243 *data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| {
244 new_move_path(
245 &mut data.move_paths,
246 &mut data.path_map,
247 &mut data.init_path_map,
248 Some(base),
249 place_ref.project_deeper(&[elem], tcx),
250 )
251 })
252 }
253 }
254
255 if let Some(base) = union_path {
256 MovePathResult::Union(base)
258 } else {
259 MovePathResult::Path(base)
260 }
261 }
262
263 fn add_move_path(
264 &mut self,
265 base: MovePathIndex,
266 elem: PlaceElem<'tcx>,
267 mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>,
268 ) -> MovePathIndex {
269 let MoveDataBuilder {
270 data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. },
271 tcx,
272 ..
273 } = self;
274 *rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || {
275 new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
276 })
277 }
278
279 fn create_move_path(&mut self, place: Place<'tcx>) {
280 let _ = self.move_path_for(place);
283 }
284
285 fn finalize(self) -> MoveData<'tcx> {
286 debug!("{}", {
287 debug!("moves for {:?}:", self.body.span);
288 for (j, mo) in self.data.moves.iter_enumerated() {
289 debug!(" {:?} = {:?}", j, mo);
290 }
291 debug!("move paths for {:?}:", self.body.span);
292 for (j, path) in self.data.move_paths.iter_enumerated() {
293 debug!(" {:?} = {:?}", j, path);
294 }
295 "done dumping moves"
296 });
297
298 self.data
299 }
300}
301
302pub(super) fn gather_moves<'tcx>(
303 body: &Body<'tcx>,
304 tcx: TyCtxt<'tcx>,
305 filter: impl Fn(Ty<'tcx>) -> bool,
306) -> MoveData<'tcx> {
307 let mut builder = MoveDataBuilder::new(body, tcx, filter);
308
309 builder.gather_args();
310
311 for (bb, block) in body.basic_blocks.iter_enumerated() {
312 for (i, stmt) in block.statements.iter().enumerate() {
313 builder.loc = Location { block: bb, statement_index: i };
314 builder.gather_statement(stmt);
315 }
316
317 builder.loc = Location { block: bb, statement_index: block.statements.len() };
318 builder.gather_terminator(block.terminator());
319 }
320
321 builder.finalize()
322}
323
324impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
325 fn gather_args(&mut self) {
326 for arg in self.body.args_iter() {
327 if let Some(path) = self.data.rev_lookup.find_local(arg) {
328 let init = self.data.inits.push(Init {
329 path,
330 kind: InitKind::Deep,
331 location: InitLocation::Argument(arg),
332 });
333
334 debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
335
336 self.data.init_path_map[path].push(init);
337 }
338 }
339 }
340
341 fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
342 debug!("gather_statement({:?}, {:?})", self.loc, stmt);
343 match &stmt.kind {
344 StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
345 let local = place.as_local().unwrap();
346 assert!(self.body.local_decls[local].is_deref_temp());
347
348 let rev_lookup = &mut self.data.rev_lookup;
349
350 rev_lookup.un_derefer.insert(local, reffed.as_ref());
351 let base_local = rev_lookup.un_derefer.deref_chain(local).first().unwrap().local;
352 rev_lookup.locals[local] = rev_lookup.locals[base_local];
353 }
354 StatementKind::Assign(box (place, rval)) => {
355 self.create_move_path(*place);
356 if let RvalueInitializationState::Shallow = rval.initialization_state() {
357 self.create_move_path(self.tcx.mk_place_deref(*place));
361 self.gather_init(place.as_ref(), InitKind::Shallow);
362 } else {
363 self.gather_init(place.as_ref(), InitKind::Deep);
364 }
365 self.gather_rvalue(rval);
366 }
367 StatementKind::FakeRead(box (_, place)) => {
368 self.create_move_path(*place);
369 }
370 StatementKind::StorageLive(_) => {}
371 StatementKind::StorageDead(local) => {
372 if !self.body.local_decls[*local].is_deref_temp() {
374 self.gather_move(Place::from(*local));
375 }
376 }
377 StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) => {
378 span_bug!(
379 stmt.source_info.span,
380 "SetDiscriminant/Deinit should not exist during borrowck"
381 );
382 }
383 StatementKind::Retag { .. }
384 | StatementKind::AscribeUserType(..)
385 | StatementKind::PlaceMention(..)
386 | StatementKind::Coverage(..)
387 | StatementKind::Intrinsic(..)
388 | StatementKind::ConstEvalCounter
389 | StatementKind::BackwardIncompatibleDropHint { .. }
390 | StatementKind::Nop => {}
391 }
392 }
393
394 fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
395 match *rvalue {
396 Rvalue::ThreadLocalRef(_) => {} Rvalue::Use(ref operand)
398 | Rvalue::Repeat(ref operand, _)
399 | Rvalue::Cast(_, ref operand, _)
400 | Rvalue::ShallowInitBox(ref operand, _)
401 | Rvalue::UnaryOp(_, ref operand)
402 | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand),
403 Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => {
404 self.gather_operand(lhs);
405 self.gather_operand(rhs);
406 }
407 Rvalue::Aggregate(ref _kind, ref operands) => {
408 for operand in operands {
409 self.gather_operand(operand);
410 }
411 }
412 Rvalue::CopyForDeref(..) => unreachable!(),
413 Rvalue::Ref(..)
414 | Rvalue::RawPtr(..)
415 | Rvalue::Discriminant(..)
416 | Rvalue::Len(..)
417 | Rvalue::NullaryOp(
418 NullOp::SizeOf
419 | NullOp::AlignOf
420 | NullOp::OffsetOf(..)
421 | NullOp::UbChecks
422 | NullOp::ContractChecks,
423 _,
424 ) => {}
425 }
426 }
427
428 fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
429 debug!("gather_terminator({:?}, {:?})", self.loc, term);
430 match term.kind {
431 TerminatorKind::Goto { target: _ }
432 | TerminatorKind::FalseEdge { .. }
433 | TerminatorKind::FalseUnwind { .. }
434 | TerminatorKind::Return
439 | TerminatorKind::UnwindResume
440 | TerminatorKind::UnwindTerminate(_)
441 | TerminatorKind::CoroutineDrop
442 | TerminatorKind::Unreachable
443 | TerminatorKind::Drop { .. } => {}
444
445 TerminatorKind::Assert { ref cond, .. } => {
446 self.gather_operand(cond);
447 }
448
449 TerminatorKind::SwitchInt { ref discr, .. } => {
450 self.gather_operand(discr);
451 }
452
453 TerminatorKind::Yield { ref value, resume_arg: place, .. } => {
454 self.gather_operand(value);
455 self.create_move_path(place);
456 self.gather_init(place.as_ref(), InitKind::Deep);
457 }
458 TerminatorKind::Call {
459 ref func,
460 ref args,
461 destination,
462 target,
463 unwind: _,
464 call_source: _,
465 fn_span: _,
466 } => {
467 self.gather_operand(func);
468 for arg in args {
469 self.gather_operand(&arg.node);
470 }
471 if let Some(_bb) = target {
472 self.create_move_path(destination);
473 self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
474 }
475 }
476 TerminatorKind::TailCall { ref func, ref args, .. } => {
477 self.gather_operand(func);
478 for arg in args {
479 self.gather_operand(&arg.node);
480 }
481 }
482 TerminatorKind::InlineAsm {
483 asm_macro: _,
484 template: _,
485 ref operands,
486 options: _,
487 line_spans: _,
488 targets: _,
489 unwind: _,
490 } => {
491 for op in operands {
492 match *op {
493 InlineAsmOperand::In { reg: _, ref value }
494 => {
495 self.gather_operand(value);
496 }
497 InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
498 if let Some(place) = place {
499 self.create_move_path(place);
500 self.gather_init(place.as_ref(), InitKind::Deep);
501 }
502 }
503 InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
504 self.gather_operand(in_value);
505 if let Some(out_place) = out_place {
506 self.create_move_path(out_place);
507 self.gather_init(out_place.as_ref(), InitKind::Deep);
508 }
509 }
510 InlineAsmOperand::Const { value: _ }
511 | InlineAsmOperand::SymFn { value: _ }
512 | InlineAsmOperand::SymStatic { def_id: _ }
513 | InlineAsmOperand::Label { target_index: _ } => {}
514 }
515 }
516 }
517 }
518 }
519
520 fn gather_operand(&mut self, operand: &Operand<'tcx>) {
521 match *operand {
522 Operand::Constant(..) | Operand::Copy(..) => {} Operand::Move(place) => {
524 self.gather_move(place);
526 }
527 }
528 }
529
530 fn gather_move(&mut self, place: Place<'tcx>) {
531 debug!("gather_move({:?}, {:?})", self.loc, place);
532 if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
533 **place.projection
534 {
535 let base_place =
539 Place { local: place.local, projection: self.tcx.mk_place_elems(base) };
540 let base_path = match self.move_path_for(base_place) {
541 MovePathResult::Path(path) => path,
542 MovePathResult::Union(path) => {
543 self.record_move(place, path);
544 return;
545 }
546 MovePathResult::Error => {
547 return;
548 }
549 };
550 let base_ty = base_place.ty(self.body, self.tcx).ty;
551 let len: u64 = match base_ty.kind() {
552 ty::Array(_, size) => size
553 .try_to_target_usize(self.tcx)
554 .expect("expected subslice projection on fixed-size array"),
555 _ => bug!("from_end: false slice pattern of non-array type"),
556 };
557 for offset in from..to {
558 let elem =
559 ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false };
560 let path =
561 self.add_move_path(base_path, elem, |tcx| tcx.mk_place_elem(base_place, elem));
562 self.record_move(place, path);
563 }
564 } else {
565 match self.move_path_for(place) {
566 MovePathResult::Path(path) | MovePathResult::Union(path) => {
567 self.record_move(place, path)
568 }
569 MovePathResult::Error => {}
570 };
571 }
572 }
573
574 fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) {
575 let move_out = self.data.moves.push(MoveOut { path, source: self.loc });
576 debug!(
577 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
578 self.loc, place, move_out, path
579 );
580 self.data.path_map[path].push(move_out);
581 self.data.loc_map[self.loc].push(move_out);
582 }
583
584 fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) {
585 debug!("gather_init({:?}, {:?})", self.loc, place);
586
587 let mut place = place;
588
589 if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() {
592 if place_base.ty(self.body, self.tcx).ty.is_union() {
593 place = place_base;
594 }
595 }
596
597 if let LookupResult::Exact(path) = self.data.rev_lookup.find(place) {
598 let init = self.data.inits.push(Init {
599 location: InitLocation::Statement(self.loc),
600 path,
601 kind,
602 });
603
604 debug!(
605 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
606 self.loc, place, init, path
607 );
608
609 self.data.init_path_map[path].push(init);
610 self.data.init_loc_map[self.loc].push(init);
611 }
612 }
613}