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