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