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,
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(_) | ProjectionElem::Downcast(_, _) => (),
232 }
233 let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty;
234 if !(self.filter)(elem_ty) {
235 return MovePathResult::Error;
236 }
237 if union_path.is_none() {
238 base =
240 *data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| {
241 new_move_path(
242 &mut data.move_paths,
243 &mut data.path_map,
244 &mut data.init_path_map,
245 Some(base),
246 place_ref.project_deeper(&[elem], tcx),
247 )
248 })
249 }
250 }
251
252 if let Some(base) = union_path {
253 MovePathResult::Union(base)
255 } else {
256 MovePathResult::Path(base)
257 }
258 }
259
260 fn add_move_path(
261 &mut self,
262 base: MovePathIndex,
263 elem: PlaceElem<'tcx>,
264 mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>,
265 ) -> MovePathIndex {
266 let MoveDataBuilder {
267 data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. },
268 tcx,
269 ..
270 } = self;
271 *rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || {
272 new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
273 })
274 }
275
276 fn create_move_path(&mut self, place: Place<'tcx>) {
277 let _ = self.move_path_for(place);
280 }
281
282 fn finalize(self) -> MoveData<'tcx> {
283 debug!("{}", {
284 debug!("moves for {:?}:", self.body.span);
285 for (j, mo) in self.data.moves.iter_enumerated() {
286 debug!(" {:?} = {:?}", j, mo);
287 }
288 debug!("move paths for {:?}:", self.body.span);
289 for (j, path) in self.data.move_paths.iter_enumerated() {
290 debug!(" {:?} = {:?}", j, path);
291 }
292 "done dumping moves"
293 });
294
295 self.data
296 }
297}
298
299pub(super) fn gather_moves<'tcx>(
300 body: &Body<'tcx>,
301 tcx: TyCtxt<'tcx>,
302 filter: impl Fn(Ty<'tcx>) -> bool,
303) -> MoveData<'tcx> {
304 let mut builder = MoveDataBuilder::new(body, tcx, filter);
305
306 builder.gather_args();
307
308 for (bb, block) in body.basic_blocks.iter_enumerated() {
309 for (i, stmt) in block.statements.iter().enumerate() {
310 builder.loc = Location { block: bb, statement_index: i };
311 builder.gather_statement(stmt);
312 }
313
314 builder.loc = Location { block: bb, statement_index: block.statements.len() };
315 builder.gather_terminator(block.terminator());
316 }
317
318 builder.finalize()
319}
320
321impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
322 fn gather_args(&mut self) {
323 for arg in self.body.args_iter() {
324 if let Some(path) = self.data.rev_lookup.find_local(arg) {
325 let init = self.data.inits.push(Init {
326 path,
327 kind: InitKind::Deep,
328 location: InitLocation::Argument(arg),
329 });
330
331 debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg);
332
333 self.data.init_path_map[path].push(init);
334 }
335 }
336 }
337
338 fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
339 debug!("gather_statement({:?}, {:?})", self.loc, stmt);
340 match &stmt.kind {
341 StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
342 let local = place.as_local().unwrap();
343 assert!(self.body.local_decls[local].is_deref_temp());
344
345 let rev_lookup = &mut self.data.rev_lookup;
346
347 rev_lookup.un_derefer.insert(local, reffed.as_ref());
348 let base_local = rev_lookup.un_derefer.deref_chain(local).first().unwrap().local;
349 rev_lookup.locals[local] = rev_lookup.locals[base_local];
350 }
351 StatementKind::Assign(box (place, rval)) => {
352 self.create_move_path(*place);
353 if let RvalueInitializationState::Shallow = rval.initialization_state() {
354 self.create_move_path(self.tcx.mk_place_deref(*place));
358 self.gather_init(place.as_ref(), InitKind::Shallow);
359 } else {
360 self.gather_init(place.as_ref(), InitKind::Deep);
361 }
362 self.gather_rvalue(rval);
363 }
364 StatementKind::FakeRead(box (_, place)) => {
365 self.create_move_path(*place);
366 }
367 StatementKind::StorageLive(_) => {}
368 StatementKind::StorageDead(local) => {
369 if !self.body.local_decls[*local].is_deref_temp() {
371 self.gather_move(Place::from(*local));
372 }
373 }
374 StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) => {
375 span_bug!(
376 stmt.source_info.span,
377 "SetDiscriminant/Deinit should not exist during borrowck"
378 );
379 }
380 StatementKind::Retag { .. }
381 | StatementKind::AscribeUserType(..)
382 | StatementKind::PlaceMention(..)
383 | StatementKind::Coverage(..)
384 | StatementKind::Intrinsic(..)
385 | StatementKind::ConstEvalCounter
386 | StatementKind::BackwardIncompatibleDropHint { .. }
387 | StatementKind::Nop => {}
388 }
389 }
390
391 fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
392 match *rvalue {
393 Rvalue::ThreadLocalRef(_) => {} Rvalue::Use(ref operand)
395 | Rvalue::Repeat(ref operand, _)
396 | Rvalue::Cast(_, ref operand, _)
397 | Rvalue::ShallowInitBox(ref operand, _)
398 | Rvalue::UnaryOp(_, ref operand)
399 | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand),
400 Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => {
401 self.gather_operand(lhs);
402 self.gather_operand(rhs);
403 }
404 Rvalue::Aggregate(ref _kind, ref operands) => {
405 for operand in operands {
406 self.gather_operand(operand);
407 }
408 }
409 Rvalue::CopyForDeref(..) => unreachable!(),
410 Rvalue::Ref(..)
411 | Rvalue::RawPtr(..)
412 | Rvalue::Discriminant(..)
413 | Rvalue::NullaryOp(
414 NullOp::SizeOf
415 | NullOp::AlignOf
416 | NullOp::OffsetOf(..)
417 | NullOp::UbChecks
418 | NullOp::ContractChecks,
419 _,
420 ) => {}
421 }
422 }
423
424 fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
425 debug!("gather_terminator({:?}, {:?})", self.loc, term);
426 match term.kind {
427 TerminatorKind::Goto { target: _ }
428 | TerminatorKind::FalseEdge { .. }
429 | TerminatorKind::FalseUnwind { .. }
430 | TerminatorKind::Return
435 | TerminatorKind::UnwindResume
436 | TerminatorKind::UnwindTerminate(_)
437 | TerminatorKind::CoroutineDrop
438 | TerminatorKind::Unreachable
439 | TerminatorKind::Drop { .. } => {}
440
441 TerminatorKind::Assert { ref cond, .. } => {
442 self.gather_operand(cond);
443 }
444
445 TerminatorKind::SwitchInt { ref discr, .. } => {
446 self.gather_operand(discr);
447 }
448
449 TerminatorKind::Yield { ref value, resume_arg: place, .. } => {
450 self.gather_operand(value);
451 self.create_move_path(place);
452 self.gather_init(place.as_ref(), InitKind::Deep);
453 }
454 TerminatorKind::Call {
455 ref func,
456 ref args,
457 destination,
458 target,
459 unwind: _,
460 call_source: _,
461 fn_span: _,
462 } => {
463 self.gather_operand(func);
464 for arg in args {
465 self.gather_operand(&arg.node);
466 }
467 if let Some(_bb) = target {
468 self.create_move_path(destination);
469 self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
470 }
471 }
472 TerminatorKind::TailCall { ref func, ref args, .. } => {
473 self.gather_operand(func);
474 for arg in args {
475 self.gather_operand(&arg.node);
476 }
477 }
478 TerminatorKind::InlineAsm {
479 asm_macro: _,
480 template: _,
481 ref operands,
482 options: _,
483 line_spans: _,
484 targets: _,
485 unwind: _,
486 } => {
487 for op in operands {
488 match *op {
489 InlineAsmOperand::In { reg: _, ref value }
490 => {
491 self.gather_operand(value);
492 }
493 InlineAsmOperand::Out { reg: _, late: _, place, .. } => {
494 if let Some(place) = place {
495 self.create_move_path(place);
496 self.gather_init(place.as_ref(), InitKind::Deep);
497 }
498 }
499 InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => {
500 self.gather_operand(in_value);
501 if let Some(out_place) = out_place {
502 self.create_move_path(out_place);
503 self.gather_init(out_place.as_ref(), InitKind::Deep);
504 }
505 }
506 InlineAsmOperand::Const { value: _ }
507 | InlineAsmOperand::SymFn { value: _ }
508 | InlineAsmOperand::SymStatic { def_id: _ }
509 | InlineAsmOperand::Label { target_index: _ } => {}
510 }
511 }
512 }
513 }
514 }
515
516 fn gather_operand(&mut self, operand: &Operand<'tcx>) {
517 match *operand {
518 Operand::Constant(..) | Operand::Copy(..) => {} Operand::Move(place) => {
520 self.gather_move(place);
522 }
523 }
524 }
525
526 fn gather_move(&mut self, place: Place<'tcx>) {
527 debug!("gather_move({:?}, {:?})", self.loc, place);
528 if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
529 **place.projection
530 {
531 let base_place =
535 Place { local: place.local, projection: self.tcx.mk_place_elems(base) };
536 let base_path = match self.move_path_for(base_place) {
537 MovePathResult::Path(path) => path,
538 MovePathResult::Union(path) => {
539 self.record_move(place, path);
540 return;
541 }
542 MovePathResult::Error => {
543 return;
544 }
545 };
546 let base_ty = base_place.ty(self.body, self.tcx).ty;
547 let len: u64 = match base_ty.kind() {
548 ty::Array(_, size) => size
549 .try_to_target_usize(self.tcx)
550 .expect("expected subslice projection on fixed-size array"),
551 _ => bug!("from_end: false slice pattern of non-array type"),
552 };
553 for offset in from..to {
554 let elem =
555 ProjectionElem::ConstantIndex { offset, min_length: len, from_end: false };
556 let path =
557 self.add_move_path(base_path, elem, |tcx| tcx.mk_place_elem(base_place, elem));
558 self.record_move(place, path);
559 }
560 } else {
561 match self.move_path_for(place) {
562 MovePathResult::Path(path) | MovePathResult::Union(path) => {
563 self.record_move(place, path)
564 }
565 MovePathResult::Error => {}
566 };
567 }
568 }
569
570 fn record_move(&mut self, place: Place<'tcx>, path: MovePathIndex) {
571 let move_out = self.data.moves.push(MoveOut { path, source: self.loc });
572 debug!(
573 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
574 self.loc, place, move_out, path
575 );
576 self.data.path_map[path].push(move_out);
577 self.data.loc_map[self.loc].push(move_out);
578 }
579
580 fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) {
581 debug!("gather_init({:?}, {:?})", self.loc, place);
582
583 let mut place = place;
584
585 if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() {
588 if place_base.ty(self.body, self.tcx).ty.is_union() {
589 place = place_base;
590 }
591 }
592
593 if let LookupResult::Exact(path) = self.data.rev_lookup.find(place) {
594 let init = self.data.inits.push(Init {
595 location: InitLocation::Statement(self.loc),
596 path,
597 kind,
598 });
599
600 debug!(
601 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
602 self.loc, place, init, path
603 );
604
605 self.data.init_path_map[path].push(init);
606 self.data.init_loc_map[self.loc].push(init);
607 }
608 }
609}