1 use rustc_index
::vec
::IndexVec
;
2 use rustc_middle
::mir
::tcx
::RvalueInitializationState
;
3 use rustc_middle
::mir
::*;
4 use rustc_middle
::ty
::{self, TyCtxt}
;
5 use smallvec
::{smallvec, SmallVec}
;
9 use super::abs_domain
::Lift
;
10 use super::IllegalMoveOriginKind
::*;
11 use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}
;
13 LocationMap
, MoveData
, MoveOut
, MoveOutIndex
, MovePath
, MovePathIndex
, MovePathLookup
,
16 struct MoveDataBuilder
<'a
, 'tcx
> {
19 param_env
: ty
::ParamEnv
<'tcx
>,
21 errors
: Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>,
24 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
25 fn new(body
: &'a Body
<'tcx
>, tcx
: TyCtxt
<'tcx
>, param_env
: ty
::ParamEnv
<'tcx
>) -> Self {
26 let mut move_paths
= IndexVec
::new();
27 let mut path_map
= IndexVec
::new();
28 let mut init_path_map
= IndexVec
::new();
36 moves
: IndexVec
::new(),
37 loc_map
: LocationMap
::new(body
),
38 rev_lookup
: MovePathLookup
{
52 projections
: Default
::default(),
56 inits
: IndexVec
::new(),
57 init_loc_map
: LocationMap
::new(body
),
64 move_paths
: &mut IndexVec
<MovePathIndex
, MovePath
<'tcx
>>,
65 path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[MoveOutIndex
; 4]>>,
66 init_path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[InitIndex
; 4]>>,
67 parent
: Option
<MovePathIndex
>,
71 move_paths
.push(MovePath { next_sibling: None, first_child: None, parent, place }
);
73 if let Some(parent
) = parent
{
74 let next_sibling
= mem
::replace(&mut move_paths
[parent
].first_child
, Some(move_path
));
75 move_paths
[move_path
].next_sibling
= next_sibling
;
78 let path_map_ent
= path_map
.push(smallvec
![]);
79 assert_eq
!(path_map_ent
, move_path
);
81 let init_path_map_ent
= init_path_map
.push(smallvec
![]);
82 assert_eq
!(init_path_map_ent
, move_path
);
88 impl<'b
, 'a
, 'tcx
> Gatherer
<'b
, 'a
, 'tcx
> {
89 /// This creates a MovePath for a given place, returning an `MovePathError`
90 /// if that place can't be moved from.
92 /// NOTE: places behind references *do not* get a move path, which is
93 /// problematic for borrowck.
95 /// Maybe we should have separate "borrowck" and "moveck" modes.
96 fn move_path_for(&mut self, place
: Place
<'tcx
>) -> Result
<MovePathIndex
, MoveError
<'tcx
>> {
97 debug
!("lookup({:?})", place
);
98 let mut base
= self.builder
.data
.rev_lookup
.locals
[place
.local
];
100 // The move path index of the first union that we find. Once this is
101 // some we stop creating child move paths, since moves from unions
102 // move the whole thing.
103 // We continue looking for other move errors though so that moving
104 // from `*(u.f: &_)` isn't allowed.
105 let mut union_path
= None
;
107 for (i
, elem
) in place
.projection
.iter().enumerate() {
108 let proj_base
= &place
.projection
[..i
];
109 let body
= self.builder
.body
;
110 let tcx
= self.builder
.tcx
;
111 let place_ty
= Place
::ty_from(place
.local
, proj_base
, body
, tcx
).ty
;
112 match place_ty
.kind() {
113 ty
::Ref(..) | ty
::RawPtr(..) => {
114 let proj
= &place
.projection
[..i
+ 1];
115 return Err(MoveError
::cannot_move_out_of(
118 target_place
: Place
{
120 projection
: tcx
.intern_place_elems(proj
),
125 ty
::Adt(adt
, _
) if adt
.has_dtor(tcx
) && !adt
.is_box() => {
126 return Err(MoveError
::cannot_move_out_of(
128 InteriorOfTypeWithDestructor { container_ty: place_ty }
,
131 ty
::Adt(adt
, _
) if adt
.is_union() => {
132 union_path
.get_or_insert(base
);
135 return Err(MoveError
::cannot_move_out_of(
137 InteriorOfSliceOrArray
{
139 is_index
: match elem
{
140 ProjectionElem
::Index(..) => true,
148 if let ProjectionElem
::Index(..) = elem
{
149 return Err(MoveError
::cannot_move_out_of(
151 InteriorOfSliceOrArray { ty: place_ty, is_index: true }
,
159 if union_path
.is_none() {
160 base
= self.add_move_path(base
, elem
, |tcx
| Place
{
162 projection
: tcx
.intern_place_elems(&place
.projection
[..i
+ 1]),
167 if let Some(base
) = union_path
{
168 // Move out of union - always move the entire union.
169 Err(MoveError
::UnionMove { path: base }
)
178 elem
: PlaceElem
<'tcx
>,
179 mk_place
: impl FnOnce(TyCtxt
<'tcx
>) -> Place
<'tcx
>,
181 let MoveDataBuilder
{
182 data
: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }
,
186 *rev_lookup
.projections
.entry((base
, elem
.lift())).or_insert_with(move || {
187 MoveDataBuilder
::new_move_path(
197 fn create_move_path(&mut self, place
: Place
<'tcx
>) {
198 // This is an non-moving access (such as an overwrite or
199 // drop), so this not being a valid move path is OK.
200 let _
= self.move_path_for(place
);
204 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
207 ) -> Result
<MoveData
<'tcx
>, (MoveData
<'tcx
>, Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>)> {
209 debug
!("moves for {:?}:", self.body
.span
);
210 for (j
, mo
) in self.data
.moves
.iter_enumerated() {
211 debug
!(" {:?} = {:?}", j
, mo
);
213 debug
!("move paths for {:?}:", self.body
.span
);
214 for (j
, path
) in self.data
.move_paths
.iter_enumerated() {
215 debug
!(" {:?} = {:?}", j
, path
);
220 if !self.errors
.is_empty() { Err((self.data, self.errors)) }
else { Ok(self.data) }
224 pub(super) fn gather_moves
<'tcx
>(
227 param_env
: ty
::ParamEnv
<'tcx
>,
228 ) -> Result
<MoveData
<'tcx
>, (MoveData
<'tcx
>, Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>)> {
229 let mut builder
= MoveDataBuilder
::new(body
, tcx
, param_env
);
231 builder
.gather_args();
233 for (bb
, block
) in body
.basic_blocks().iter_enumerated() {
234 for (i
, stmt
) in block
.statements
.iter().enumerate() {
235 let source
= Location { block: bb, statement_index: i }
;
236 builder
.gather_statement(source
, stmt
);
239 let terminator_loc
= Location { block: bb, statement_index: block.statements.len() }
;
240 builder
.gather_terminator(terminator_loc
, block
.terminator());
246 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
247 fn gather_args(&mut self) {
248 for arg
in self.body
.args_iter() {
249 let path
= self.data
.rev_lookup
.locals
[arg
];
251 let init
= self.data
.inits
.push(Init
{
253 kind
: InitKind
::Deep
,
254 location
: InitLocation
::Argument(arg
),
257 debug
!("gather_args: adding init {:?} of {:?} for argument {:?}", init
, path
, arg
);
259 self.data
.init_path_map
[path
].push(init
);
263 fn gather_statement(&mut self, loc
: Location
, stmt
: &Statement
<'tcx
>) {
264 debug
!("gather_statement({:?}, {:?})", loc
, stmt
);
265 (Gatherer { builder: self, loc }
).gather_statement(stmt
);
268 fn gather_terminator(&mut self, loc
: Location
, term
: &Terminator
<'tcx
>) {
269 debug
!("gather_terminator({:?}, {:?})", loc
, term
);
270 (Gatherer { builder: self, loc }
).gather_terminator(term
);
274 struct Gatherer
<'b
, 'a
, 'tcx
> {
275 builder
: &'b
mut MoveDataBuilder
<'a
, 'tcx
>,
279 impl<'b
, 'a
, 'tcx
> Gatherer
<'b
, 'a
, 'tcx
> {
280 fn gather_statement(&mut self, stmt
: &Statement
<'tcx
>) {
282 StatementKind
::Assign(box (place
, rval
)) => {
283 self.create_move_path(*place
);
284 if let RvalueInitializationState
::Shallow
= rval
.initialization_state() {
285 // Box starts out uninitialized - need to create a separate
286 // move-path for the interior so it will be separate from
288 self.create_move_path(self.builder
.tcx
.mk_place_deref(*place
));
289 self.gather_init(place
.as_ref(), InitKind
::Shallow
);
291 self.gather_init(place
.as_ref(), InitKind
::Deep
);
293 self.gather_rvalue(rval
);
295 StatementKind
::FakeRead(_
, place
) => {
296 self.create_move_path(**place
);
298 StatementKind
::LlvmInlineAsm(ref asm
) => {
299 for (output
, kind
) in asm
.outputs
.iter().zip(&asm
.asm
.outputs
) {
300 if !kind
.is_indirect
{
301 self.gather_init(output
.as_ref(), InitKind
::Deep
);
304 for (_
, input
) in asm
.inputs
.iter() {
305 self.gather_operand(input
);
308 StatementKind
::StorageLive(_
) => {}
309 StatementKind
::StorageDead(local
) => {
310 self.gather_move(Place
::from(*local
));
312 StatementKind
::SetDiscriminant { .. }
=> {
314 stmt
.source_info
.span
,
315 "SetDiscriminant should not exist during borrowck"
318 StatementKind
::Retag { .. }
319 | StatementKind
::AscribeUserType(..)
320 | StatementKind
::Coverage(..)
321 | StatementKind
::Nop
=> {}
325 fn gather_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>) {
327 Rvalue
::ThreadLocalRef(_
) => {}
// not-a-move
328 Rvalue
::Use(ref operand
)
329 | Rvalue
::Repeat(ref operand
, _
)
330 | Rvalue
::Cast(_
, ref operand
, _
)
331 | Rvalue
::UnaryOp(_
, ref operand
) => self.gather_operand(operand
),
332 Rvalue
::BinaryOp(ref _binop
, ref lhs
, ref rhs
)
333 | Rvalue
::CheckedBinaryOp(ref _binop
, ref lhs
, ref rhs
) => {
334 self.gather_operand(lhs
);
335 self.gather_operand(rhs
);
337 Rvalue
::Aggregate(ref _kind
, ref operands
) => {
338 for operand
in operands
{
339 self.gather_operand(operand
);
343 | Rvalue
::AddressOf(..)
344 | Rvalue
::Discriminant(..)
346 | Rvalue
::NullaryOp(NullOp
::SizeOf
, _
)
347 | Rvalue
::NullaryOp(NullOp
::Box
, _
) => {
348 // This returns an rvalue with uninitialized contents. We can't
349 // move out of it here because it is an rvalue - assignments always
350 // completely initialize their place.
352 // However, this does not matter - MIR building is careful to
353 // only emit a shallow free for the partially-initialized
356 // In any case, if we want to fix this, we have to register a
357 // special move and change the `statement_effect` functions.
362 fn gather_terminator(&mut self, term
: &Terminator
<'tcx
>) {
364 TerminatorKind
::Goto { target: _ }
365 | TerminatorKind
::Resume
366 | TerminatorKind
::Abort
367 | TerminatorKind
::GeneratorDrop
368 | TerminatorKind
::FalseEdge { .. }
369 | TerminatorKind
::FalseUnwind { .. }
370 | TerminatorKind
::Unreachable
=> {}
372 TerminatorKind
::Return
=> {
373 self.gather_move(Place
::return_place());
376 TerminatorKind
::Assert { ref cond, .. }
=> {
377 self.gather_operand(cond
);
380 TerminatorKind
::SwitchInt { ref discr, .. }
=> {
381 self.gather_operand(discr
);
384 TerminatorKind
::Yield { ref value, resume_arg: place, .. }
=> {
385 self.gather_operand(value
);
386 self.create_move_path(place
);
387 self.gather_init(place
.as_ref(), InitKind
::Deep
);
390 TerminatorKind
::Drop { place, target: _, unwind: _ }
=> {
391 self.gather_move(place
);
393 TerminatorKind
::DropAndReplace { place, ref value, .. }
=> {
394 self.create_move_path(place
);
395 self.gather_operand(value
);
396 self.gather_init(place
.as_ref(), InitKind
::Deep
);
398 TerminatorKind
::Call
{
406 self.gather_operand(func
);
408 self.gather_operand(arg
);
410 if let Some((destination
, _bb
)) = *destination
{
411 self.create_move_path(destination
);
412 self.gather_init(destination
.as_ref(), InitKind
::NonPanicPathOnly
);
415 TerminatorKind
::InlineAsm
{
424 InlineAsmOperand
::In { reg: _, ref value }
425 | InlineAsmOperand
::Const { ref value }
=> {
426 self.gather_operand(value
);
428 InlineAsmOperand
::Out { reg: _, late: _, place, .. }
=> {
429 if let Some(place
) = place
{
430 self.create_move_path(place
);
431 self.gather_init(place
.as_ref(), InitKind
::Deep
);
434 InlineAsmOperand
::InOut { reg: _, late: _, ref in_value, out_place }
=> {
435 self.gather_operand(in_value
);
436 if let Some(out_place
) = out_place
{
437 self.create_move_path(out_place
);
438 self.gather_init(out_place
.as_ref(), InitKind
::Deep
);
441 InlineAsmOperand
::SymFn { value: _ }
442 | InlineAsmOperand
::SymStatic { def_id: _ }
=> {}
449 fn gather_operand(&mut self, operand
: &Operand
<'tcx
>) {
451 Operand
::Constant(..) | Operand
::Copy(..) => {}
// not-a-move
452 Operand
::Move(place
) => {
454 self.gather_move(place
);
459 fn gather_move(&mut self, place
: Place
<'tcx
>) {
460 debug
!("gather_move({:?}, {:?})", self.loc
, place
);
462 if let [ref base @
.., ProjectionElem
::Subslice { from, to, from_end: false }
] =
465 // Split `Subslice` patterns into the corresponding list of
466 // `ConstIndex` patterns. This is done to ensure that all move paths
467 // are disjoint, which is expected by drop elaboration.
469 Place { local: place.local, projection: self.builder.tcx.intern_place_elems(base) }
;
470 let base_path
= match self.move_path_for(base_place
) {
472 Err(MoveError
::UnionMove { path }
) => {
473 self.record_move(place
, path
);
476 Err(error @ MoveError
::IllegalMove { .. }
) => {
477 self.builder
.errors
.push((base_place
, error
));
481 let base_ty
= base_place
.ty(self.builder
.body
, self.builder
.tcx
).ty
;
482 let len
: u64 = match base_ty
.kind() {
483 ty
::Array(_
, size
) => size
.eval_usize(self.builder
.tcx
, self.builder
.param_env
),
484 _
=> bug
!("from_end: false slice pattern of non-array type"),
486 for offset
in from
..to
{
488 ProjectionElem
::ConstantIndex { offset, min_length: len, from_end: false }
;
490 self.add_move_path(base_path
, elem
, |tcx
| tcx
.mk_place_elem(base_place
, elem
));
491 self.record_move(place
, path
);
494 match self.move_path_for(place
) {
495 Ok(path
) | Err(MoveError
::UnionMove { path }
) => self.record_move(place
, path
),
496 Err(error @ MoveError
::IllegalMove { .. }
) => {
497 self.builder
.errors
.push((place
, error
));
503 fn record_move(&mut self, place
: Place
<'tcx
>, path
: MovePathIndex
) {
504 let move_out
= self.builder
.data
.moves
.push(MoveOut { path, source: self.loc }
);
506 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
507 self.loc
, place
, move_out
, path
509 self.builder
.data
.path_map
[path
].push(move_out
);
510 self.builder
.data
.loc_map
[self.loc
].push(move_out
);
513 fn gather_init(&mut self, place
: PlaceRef
<'tcx
>, kind
: InitKind
) {
514 debug
!("gather_init({:?}, {:?})", self.loc
, place
);
516 let mut place
= place
;
518 // Check if we are assigning into a field of a union, if so, lookup the place
519 // of the union so it is marked as initialized again.
520 if let [proj_base @
.., ProjectionElem
::Field(_
, _
)] = place
.projection
{
521 if let ty
::Adt(def
, _
) =
522 Place
::ty_from(place
.local
, proj_base
, self.builder
.body
, self.builder
.tcx
)
527 place
= PlaceRef { local: place.local, projection: proj_base }
532 if let LookupResult
::Exact(path
) = self.builder
.data
.rev_lookup
.find(place
) {
533 let init
= self.builder
.data
.inits
.push(Init
{
534 location
: InitLocation
::Statement(self.loc
),
540 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
541 self.loc
, place
, init
, path
544 self.builder
.data
.init_path_map
[path
].push(init
);
545 self.builder
.data
.init_loc_map
[self.loc
].push(init
);