1 use rustc_index
::IndexVec
;
2 use rustc_middle
::mir
::tcx
::{PlaceTy, RvalueInitializationState}
;
3 use rustc_middle
::mir
::*;
4 use rustc_middle
::ty
::{self, Ty, TyCtxt, TypeVisitableExt}
;
5 use smallvec
::{smallvec, SmallVec}
;
9 use super::abs_domain
::Lift
;
10 use super::{Init, InitIndex, InitKind, InitLocation, LookupResult}
;
12 LocationMap
, MoveData
, MoveOut
, MoveOutIndex
, MovePath
, MovePathIndex
, MovePathLookup
,
15 struct MoveDataBuilder
<'a
, 'tcx
, F
> {
18 param_env
: ty
::ParamEnv
<'tcx
>,
23 impl<'a
, 'tcx
, F
: Fn(Ty
<'tcx
>) -> bool
> MoveDataBuilder
<'a
, 'tcx
, F
> {
27 param_env
: ty
::ParamEnv
<'tcx
>,
30 let mut move_paths
= IndexVec
::new();
31 let mut path_map
= IndexVec
::new();
32 let mut init_path_map
= IndexVec
::new();
38 if l
.is_deref_temp() {
60 moves
: IndexVec
::new(),
61 loc_map
: LocationMap
::new(body
),
62 rev_lookup
: MovePathLookup
{
64 projections
: Default
::default(),
65 un_derefer
: Default
::default(),
69 inits
: IndexVec
::new(),
70 init_loc_map
: LocationMap
::new(body
),
78 fn new_move_path
<'tcx
>(
79 move_paths
: &mut IndexVec
<MovePathIndex
, MovePath
<'tcx
>>,
80 path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[MoveOutIndex
; 4]>>,
81 init_path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[InitIndex
; 4]>>,
82 parent
: Option
<MovePathIndex
>,
86 move_paths
.push(MovePath { next_sibling: None, first_child: None, parent, place }
);
88 if let Some(parent
) = parent
{
89 let next_sibling
= mem
::replace(&mut move_paths
[parent
].first_child
, Some(move_path
));
90 move_paths
[move_path
].next_sibling
= next_sibling
;
93 let path_map_ent
= path_map
.push(smallvec
![]);
94 assert_eq
!(path_map_ent
, move_path
);
96 let init_path_map_ent
= init_path_map
.push(smallvec
![]);
97 assert_eq
!(init_path_map_ent
, move_path
);
102 enum MovePathResult
{
104 Union(MovePathIndex
),
108 impl<'b
, 'a
, 'tcx
, F
: Fn(Ty
<'tcx
>) -> bool
> Gatherer
<'b
, 'a
, 'tcx
, F
> {
109 /// This creates a MovePath for a given place, returning an `MovePathError`
110 /// if that place can't be moved from.
112 /// NOTE: places behind references *do not* get a move path, which is
113 /// problematic for borrowck.
115 /// Maybe we should have separate "borrowck" and "moveck" modes.
116 fn move_path_for(&mut self, place
: Place
<'tcx
>) -> MovePathResult
{
117 let data
= &mut self.builder
.data
;
119 debug
!("lookup({:?})", place
);
120 let Some(mut base
) = data
.rev_lookup
.find_local(place
.local
) else {
121 return MovePathResult
::Error
;
124 // The move path index of the first union that we find. Once this is
125 // some we stop creating child move paths, since moves from unions
126 // move the whole thing.
127 // We continue looking for other move errors though so that moving
128 // from `*(u.f: &_)` isn't allowed.
129 let mut union_path
= None
;
131 for (place_ref
, elem
) in data
.rev_lookup
.un_derefer
.iter_projections(place
.as_ref()) {
132 let body
= self.builder
.body
;
133 let tcx
= self.builder
.tcx
;
134 let place_ty
= place_ref
.ty(body
, tcx
).ty
;
135 if place_ty
.references_error() {
136 return MovePathResult
::Error
;
139 ProjectionElem
::Deref
=> match place_ty
.kind() {
140 ty
::Ref(..) | ty
::RawPtr(..) => {
141 return MovePathResult
::Error
;
145 bug
!("Adt should be a box type when Place is deref");
160 | ty
::Dynamic(_
, _
, _
)
162 | ty
::CoroutineClosure(..)
163 | ty
::Coroutine(_
, _
)
164 | ty
::CoroutineWitness(..)
172 | ty
::Placeholder(_
) => {
173 bug
!("When Place is Deref it's type shouldn't be {place_ty:#?}")
176 ProjectionElem
::Field(_
, _
) => match place_ty
.kind() {
178 if adt
.has_dtor(tcx
) {
179 return MovePathResult
::Error
;
182 union_path
.get_or_insert(base
);
186 | ty
::CoroutineClosure(..)
187 | ty
::Coroutine(_
, _
)
188 | ty
::Tuple(_
) => (),
203 | ty
::Dynamic(_
, _
, _
)
204 | ty
::CoroutineWitness(..)
211 | ty
::Placeholder(_
) => bug
!(
212 "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}"
215 ProjectionElem
::ConstantIndex { .. }
| ProjectionElem
::Subslice { .. }
=> {
216 match place_ty
.kind() {
218 return MovePathResult
::Error
;
220 ty
::Array(_
, _
) => (),
221 _
=> bug
!("Unexpected type {:#?}", place_ty
.is_array()),
224 ProjectionElem
::Index(_
) => match place_ty
.kind() {
225 ty
::Array(..) | ty
::Slice(_
) => {
226 return MovePathResult
::Error
;
228 _
=> bug
!("Unexpected type {place_ty:#?}"),
230 // `OpaqueCast`:Only transmutes the type, so no moves there.
231 // `Downcast` :Only changes information about a `Place` without moving.
232 // `Subtype` :Only transmutes the type, so moves.
233 // So it's safe to skip these.
234 ProjectionElem
::OpaqueCast(_
)
235 | ProjectionElem
::Subtype(_
)
236 | ProjectionElem
::Downcast(_
, _
) => (),
238 let elem_ty
= PlaceTy
::from_ty(place_ty
).projection_ty(tcx
, elem
).ty
;
239 if !(self.builder
.filter
)(elem_ty
) {
240 return MovePathResult
::Error
;
242 if union_path
.is_none() {
243 // inlined from add_move_path because of a borrowck conflict with the iterator
245 *data
.rev_lookup
.projections
.entry((base
, elem
.lift())).or_insert_with(|| {
247 &mut data
.move_paths
,
249 &mut data
.init_path_map
,
251 place_ref
.project_deeper(&[elem
], tcx
),
257 if let Some(base
) = union_path
{
258 // Move out of union - always move the entire union.
259 MovePathResult
::Union(base
)
261 MovePathResult
::Path(base
)
268 elem
: PlaceElem
<'tcx
>,
269 mk_place
: impl FnOnce(TyCtxt
<'tcx
>) -> Place
<'tcx
>,
271 let MoveDataBuilder
{
272 data
: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }
,
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
))
281 fn create_move_path(&mut self, place
: Place
<'tcx
>) {
282 // This is an non-moving access (such as an overwrite or
283 // drop), so this not being a valid move path is OK.
284 let _
= self.move_path_for(place
);
288 impl<'a
, 'tcx
, F
> MoveDataBuilder
<'a
, 'tcx
, F
> {
289 fn finalize(self) -> MoveData
<'tcx
> {
291 debug
!("moves for {:?}:", self.body
.span
);
292 for (j
, mo
) in self.data
.moves
.iter_enumerated() {
293 debug
!(" {:?} = {:?}", j
, mo
);
295 debug
!("move paths for {:?}:", self.body
.span
);
296 for (j
, path
) in self.data
.move_paths
.iter_enumerated() {
297 debug
!(" {:?} = {:?}", j
, path
);
306 pub(super) fn gather_moves
<'tcx
>(
309 param_env
: ty
::ParamEnv
<'tcx
>,
310 filter
: impl Fn(Ty
<'tcx
>) -> bool
,
311 ) -> MoveData
<'tcx
> {
312 let mut builder
= MoveDataBuilder
::new(body
, tcx
, param_env
, filter
);
314 builder
.gather_args();
316 for (bb
, block
) in body
.basic_blocks
.iter_enumerated() {
317 for (i
, stmt
) in block
.statements
.iter().enumerate() {
318 let source
= Location { block: bb, statement_index: i }
;
319 builder
.gather_statement(source
, stmt
);
322 let terminator_loc
= Location { block: bb, statement_index: block.statements.len() }
;
323 builder
.gather_terminator(terminator_loc
, block
.terminator());
329 impl<'a
, 'tcx
, F
: Fn(Ty
<'tcx
>) -> bool
> MoveDataBuilder
<'a
, 'tcx
, F
> {
330 fn gather_args(&mut self) {
331 for arg
in self.body
.args_iter() {
332 if let Some(path
) = self.data
.rev_lookup
.find_local(arg
) {
333 let init
= self.data
.inits
.push(Init
{
335 kind
: InitKind
::Deep
,
336 location
: InitLocation
::Argument(arg
),
339 debug
!("gather_args: adding init {:?} of {:?} for argument {:?}", init
, path
, arg
);
341 self.data
.init_path_map
[path
].push(init
);
346 fn gather_statement(&mut self, loc
: Location
, stmt
: &Statement
<'tcx
>) {
347 debug
!("gather_statement({:?}, {:?})", loc
, stmt
);
348 (Gatherer { builder: self, loc }
).gather_statement(stmt
);
351 fn gather_terminator(&mut self, loc
: Location
, term
: &Terminator
<'tcx
>) {
352 debug
!("gather_terminator({:?}, {:?})", loc
, term
);
353 (Gatherer { builder: self, loc }
).gather_terminator(term
);
357 struct Gatherer
<'b
, 'a
, 'tcx
, F
> {
358 builder
: &'b
mut MoveDataBuilder
<'a
, 'tcx
, F
>,
362 impl<'b
, 'a
, 'tcx
, F
: Fn(Ty
<'tcx
>) -> bool
> Gatherer
<'b
, 'a
, 'tcx
, F
> {
363 fn gather_statement(&mut self, stmt
: &Statement
<'tcx
>) {
365 StatementKind
::Assign(box (place
, Rvalue
::CopyForDeref(reffed
))) => {
366 let local
= place
.as_local().unwrap();
367 assert
!(self.builder
.body
.local_decls
[local
].is_deref_temp());
369 let rev_lookup
= &mut self.builder
.data
.rev_lookup
;
371 rev_lookup
.un_derefer
.insert(local
, reffed
.as_ref());
372 let base_local
= rev_lookup
.un_derefer
.deref_chain(local
).first().unwrap().local
;
373 rev_lookup
.locals
[local
] = rev_lookup
.locals
[base_local
];
375 StatementKind
::Assign(box (place
, rval
)) => {
376 self.create_move_path(*place
);
377 if let RvalueInitializationState
::Shallow
= rval
.initialization_state() {
378 // Box starts out uninitialized - need to create a separate
379 // move-path for the interior so it will be separate from
381 self.create_move_path(self.builder
.tcx
.mk_place_deref(*place
));
382 self.gather_init(place
.as_ref(), InitKind
::Shallow
);
384 self.gather_init(place
.as_ref(), InitKind
::Deep
);
386 self.gather_rvalue(rval
);
388 StatementKind
::FakeRead(box (_
, place
)) => {
389 self.create_move_path(*place
);
391 StatementKind
::StorageLive(_
) => {}
392 StatementKind
::StorageDead(local
) => {
393 // DerefTemp locals (results of CopyForDeref) don't actually move anything.
394 if !self.builder
.body
.local_decls
[*local
].is_deref_temp() {
395 self.gather_move(Place
::from(*local
));
398 StatementKind
::SetDiscriminant { .. }
| StatementKind
::Deinit(..) => {
400 stmt
.source_info
.span
,
401 "SetDiscriminant/Deinit should not exist during borrowck"
404 StatementKind
::Retag { .. }
405 | StatementKind
::AscribeUserType(..)
406 | StatementKind
::PlaceMention(..)
407 | StatementKind
::Coverage(..)
408 | StatementKind
::Intrinsic(..)
409 | StatementKind
::ConstEvalCounter
410 | StatementKind
::Nop
=> {}
414 fn gather_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>) {
416 Rvalue
::ThreadLocalRef(_
) => {}
// not-a-move
417 Rvalue
::Use(ref operand
)
418 | Rvalue
::Repeat(ref operand
, _
)
419 | Rvalue
::Cast(_
, ref operand
, _
)
420 | Rvalue
::ShallowInitBox(ref operand
, _
)
421 | Rvalue
::UnaryOp(_
, ref operand
) => self.gather_operand(operand
),
422 Rvalue
::BinaryOp(ref _binop
, box (ref lhs
, ref rhs
))
423 | Rvalue
::CheckedBinaryOp(ref _binop
, box (ref lhs
, ref rhs
)) => {
424 self.gather_operand(lhs
);
425 self.gather_operand(rhs
);
427 Rvalue
::Aggregate(ref _kind
, ref operands
) => {
428 for operand
in operands
{
429 self.gather_operand(operand
);
432 Rvalue
::CopyForDeref(..) => unreachable
!(),
434 | Rvalue
::AddressOf(..)
435 | Rvalue
::Discriminant(..)
438 NullOp
::SizeOf
| NullOp
::AlignOf
| NullOp
::OffsetOf(..) | NullOp
::UbChecks
,
444 fn gather_terminator(&mut self, term
: &Terminator
<'tcx
>) {
446 TerminatorKind
::Goto { target: _ }
447 | TerminatorKind
::FalseEdge { .. }
448 | TerminatorKind
::FalseUnwind { .. }
449 // In some sense returning moves the return place into the current
450 // call's destination, however, since there are no statements after
451 // this that could possibly access the return place, this doesn't
453 | TerminatorKind
::Return
454 | TerminatorKind
::UnwindResume
455 | TerminatorKind
::UnwindTerminate(_
)
456 | TerminatorKind
::CoroutineDrop
457 | TerminatorKind
::Unreachable
458 | TerminatorKind
::Drop { .. }
=> {}
460 TerminatorKind
::Assert { ref cond, .. }
=> {
461 self.gather_operand(cond
);
464 TerminatorKind
::SwitchInt { ref discr, .. }
=> {
465 self.gather_operand(discr
);
468 TerminatorKind
::Yield { ref value, resume_arg: place, .. }
=> {
469 self.gather_operand(value
);
470 self.create_move_path(place
);
471 self.gather_init(place
.as_ref(), InitKind
::Deep
);
473 TerminatorKind
::Call
{
482 self.gather_operand(func
);
484 self.gather_operand(&arg
.node
);
486 if let Some(_bb
) = target
{
487 self.create_move_path(destination
);
488 self.gather_init(destination
.as_ref(), InitKind
::NonPanicPathOnly
);
491 TerminatorKind
::InlineAsm
{
501 InlineAsmOperand
::In { reg: _, ref value }
503 self.gather_operand(value
);
505 InlineAsmOperand
::Out { reg: _, late: _, place, .. }
=> {
506 if let Some(place
) = place
{
507 self.create_move_path(place
);
508 self.gather_init(place
.as_ref(), InitKind
::Deep
);
511 InlineAsmOperand
::InOut { reg: _, late: _, ref in_value, out_place }
=> {
512 self.gather_operand(in_value
);
513 if let Some(out_place
) = out_place
{
514 self.create_move_path(out_place
);
515 self.gather_init(out_place
.as_ref(), InitKind
::Deep
);
518 InlineAsmOperand
::Const { value: _ }
519 | InlineAsmOperand
::SymFn { value: _ }
520 | InlineAsmOperand
::SymStatic { def_id: _ }
521 | InlineAsmOperand
::Label { target_index: _ }
=> {}
528 fn gather_operand(&mut self, operand
: &Operand
<'tcx
>) {
530 Operand
::Constant(..) | Operand
::Copy(..) => {}
// not-a-move
531 Operand
::Move(place
) => {
533 self.gather_move(place
);
538 fn gather_move(&mut self, place
: Place
<'tcx
>) {
539 debug
!("gather_move({:?}, {:?})", self.loc
, place
);
540 if let [ref base @
.., ProjectionElem
::Subslice { from, to, from_end: false }
] =
543 // Split `Subslice` patterns into the corresponding list of
544 // `ConstIndex` patterns. This is done to ensure that all move paths
545 // are disjoint, which is expected by drop elaboration.
547 Place { local: place.local, projection: self.builder.tcx.mk_place_elems(base) }
;
548 let base_path
= match self.move_path_for(base_place
) {
549 MovePathResult
::Path(path
) => path
,
550 MovePathResult
::Union(path
) => {
551 self.record_move(place
, path
);
554 MovePathResult
::Error
=> {
558 let base_ty
= base_place
.ty(self.builder
.body
, self.builder
.tcx
).ty
;
559 let len
: u64 = match base_ty
.kind() {
560 ty
::Array(_
, size
) => {
561 size
.eval_target_usize(self.builder
.tcx
, self.builder
.param_env
)
563 _
=> bug
!("from_end: false slice pattern of non-array type"),
565 for offset
in from
..to
{
567 ProjectionElem
::ConstantIndex { offset, min_length: len, from_end: false }
;
569 self.add_move_path(base_path
, elem
, |tcx
| tcx
.mk_place_elem(base_place
, elem
));
570 self.record_move(place
, path
);
573 match self.move_path_for(place
) {
574 MovePathResult
::Path(path
) | MovePathResult
::Union(path
) => {
575 self.record_move(place
, path
)
577 MovePathResult
::Error
=> {}
582 fn record_move(&mut self, place
: Place
<'tcx
>, path
: MovePathIndex
) {
583 let move_out
= self.builder
.data
.moves
.push(MoveOut { path, source: self.loc }
);
585 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
586 self.loc
, place
, move_out
, path
588 self.builder
.data
.path_map
[path
].push(move_out
);
589 self.builder
.data
.loc_map
[self.loc
].push(move_out
);
592 fn gather_init(&mut self, place
: PlaceRef
<'tcx
>, kind
: InitKind
) {
593 debug
!("gather_init({:?}, {:?})", self.loc
, place
);
595 let mut place
= place
;
597 // Check if we are assigning into a field of a union, if so, lookup the place
598 // of the union so it is marked as initialized again.
599 if let Some((place_base
, ProjectionElem
::Field(_
, _
))) = place
.last_projection() {
600 if place_base
.ty(self.builder
.body
, self.builder
.tcx
).ty
.is_union() {
605 if let LookupResult
::Exact(path
) = self.builder
.data
.rev_lookup
.find(place
) {
606 let init
= self.builder
.data
.inits
.push(Init
{
607 location
: InitLocation
::Statement(self.loc
),
613 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
614 self.loc
, place
, init
, path
617 self.builder
.data
.init_path_map
[path
].push(init
);
618 self.builder
.data
.init_loc_map
[self.loc
].push(init
);