1 use rustc
::mir
::tcx
::RvalueInitializationState
;
3 use rustc
::ty
::{self, TyCtxt}
;
4 use rustc_index
::vec
::IndexVec
;
5 use smallvec
::{smallvec, SmallVec}
;
7 use std
::convert
::TryInto
;
10 use super::abs_domain
::Lift
;
11 use super::IllegalMoveOriginKind
::*;
12 use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}
;
14 LocationMap
, MoveData
, MoveOut
, MoveOutIndex
, MovePath
, MovePathIndex
, MovePathLookup
,
17 struct MoveDataBuilder
<'a
, 'tcx
> {
20 param_env
: ty
::ParamEnv
<'tcx
>,
22 errors
: Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>,
25 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
26 fn new(body
: &'a Body
<'tcx
>, tcx
: TyCtxt
<'tcx
>, param_env
: ty
::ParamEnv
<'tcx
>) -> Self {
27 let mut move_paths
= IndexVec
::new();
28 let mut path_map
= IndexVec
::new();
29 let mut init_path_map
= IndexVec
::new();
37 moves
: IndexVec
::new(),
38 loc_map
: LocationMap
::new(body
),
39 rev_lookup
: MovePathLookup
{
53 projections
: Default
::default(),
57 inits
: IndexVec
::new(),
58 init_loc_map
: LocationMap
::new(body
),
65 move_paths
: &mut IndexVec
<MovePathIndex
, MovePath
<'tcx
>>,
66 path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[MoveOutIndex
; 4]>>,
67 init_path_map
: &mut IndexVec
<MovePathIndex
, SmallVec
<[InitIndex
; 4]>>,
68 parent
: Option
<MovePathIndex
>,
72 move_paths
.push(MovePath { next_sibling: None, first_child: None, parent, place }
);
74 if let Some(parent
) = parent
{
75 let next_sibling
= mem
::replace(&mut move_paths
[parent
].first_child
, Some(move_path
));
76 move_paths
[move_path
].next_sibling
= next_sibling
;
79 let path_map_ent
= path_map
.push(smallvec
![]);
80 assert_eq
!(path_map_ent
, move_path
);
82 let init_path_map_ent
= init_path_map
.push(smallvec
![]);
83 assert_eq
!(init_path_map_ent
, move_path
);
89 impl<'b
, 'a
, 'tcx
> Gatherer
<'b
, 'a
, 'tcx
> {
90 /// This creates a MovePath for a given place, returning an `MovePathError`
91 /// if that place can't be moved from.
93 /// NOTE: places behind references *do not* get a move path, which is
94 /// problematic for borrowck.
96 /// Maybe we should have separate "borrowck" and "moveck" modes.
97 fn move_path_for(&mut self, place
: &Place
<'tcx
>) -> Result
<MovePathIndex
, MoveError
<'tcx
>> {
98 debug
!("lookup({:?})", place
);
99 let mut base
= match place
.base
{
100 PlaceBase
::Local(local
) => self.builder
.data
.rev_lookup
.locals
[local
],
101 PlaceBase
::Static(..) => {
102 return Err(MoveError
::cannot_move_out_of(self.loc
, Static
));
106 // The move path index of the first union that we find. Once this is
107 // some we stop creating child move paths, since moves from unions
108 // move the whole thing.
109 // We continue looking for other move errors though so that moving
110 // from `*(u.f: &_)` isn't allowed.
111 let mut union_path
= None
;
113 for (i
, elem
) in place
.projection
.iter().enumerate() {
114 let proj_base
= &place
.projection
[..i
];
115 let body
= self.builder
.body
;
116 let tcx
= self.builder
.tcx
;
117 let place_ty
= Place
::ty_from(&place
.base
, proj_base
, body
, tcx
).ty
;
118 match place_ty
.kind
{
119 ty
::Ref(..) | ty
::RawPtr(..) => {
120 let proj
= &place
.projection
[..i
+1];
121 return Err(MoveError
::cannot_move_out_of(
124 target_place
: Place
{
125 base
: place
.base
.clone(),
126 projection
: tcx
.intern_place_elems(proj
),
131 ty
::Adt(adt
, _
) if adt
.has_dtor(tcx
) && !adt
.is_box() => {
132 return Err(MoveError
::cannot_move_out_of(
134 InteriorOfTypeWithDestructor { container_ty: place_ty }
,
137 ty
::Adt(adt
, _
) if adt
.is_union() => {
138 union_path
.get_or_insert(base
);
141 return Err(MoveError
::cannot_move_out_of(
143 InteriorOfSliceOrArray
{
145 is_index
: match elem
{
146 ProjectionElem
::Index(..) => true,
152 ty
::Array(..) => match elem
{
153 ProjectionElem
::Index(..) => {
154 return Err(MoveError
::cannot_move_out_of(
156 InteriorOfSliceOrArray { ty: place_ty, is_index: true }
,
164 if union_path
.is_none() {
165 base
= self.add_move_path(base
, elem
, |tcx
| {
167 base
: place
.base
.clone(),
168 projection
: tcx
.intern_place_elems(&place
.projection
[..i
+1]),
174 if let Some(base
) = union_path
{
175 // Move out of union - always move the entire union.
176 Err(MoveError
::UnionMove { path: base }
)
185 elem
: &PlaceElem
<'tcx
>,
186 mk_place
: impl FnOnce(TyCtxt
<'tcx
>) -> Place
<'tcx
>,
188 let MoveDataBuilder
{
189 data
: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }
,
193 *rev_lookup
.projections
194 .entry((base
, elem
.lift()))
195 .or_insert_with(move || {
196 let path
= MoveDataBuilder
::new_move_path(
207 fn create_move_path(&mut self, place
: &Place
<'tcx
>) {
208 // This is an non-moving access (such as an overwrite or
209 // drop), so this not being a valid move path is OK.
210 let _
= self.move_path_for(place
);
214 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
217 ) -> Result
<MoveData
<'tcx
>, (MoveData
<'tcx
>, Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>)> {
219 debug
!("moves for {:?}:", self.body
.span
);
220 for (j
, mo
) in self.data
.moves
.iter_enumerated() {
221 debug
!(" {:?} = {:?}", j
, mo
);
223 debug
!("move paths for {:?}:", self.body
.span
);
224 for (j
, path
) in self.data
.move_paths
.iter_enumerated() {
225 debug
!(" {:?} = {:?}", j
, path
);
230 if !self.errors
.is_empty() { Err((self.data, self.errors)) }
else { Ok(self.data) }
234 pub(super) fn gather_moves
<'tcx
>(
237 param_env
: ty
::ParamEnv
<'tcx
>,
238 ) -> Result
<MoveData
<'tcx
>, (MoveData
<'tcx
>, Vec
<(Place
<'tcx
>, MoveError
<'tcx
>)>)> {
239 let mut builder
= MoveDataBuilder
::new(body
, tcx
, param_env
);
241 builder
.gather_args();
243 for (bb
, block
) in body
.basic_blocks().iter_enumerated() {
244 for (i
, stmt
) in block
.statements
.iter().enumerate() {
245 let source
= Location { block: bb, statement_index: i }
;
246 builder
.gather_statement(source
, stmt
);
249 let terminator_loc
= Location { block: bb, statement_index: block.statements.len() }
;
250 builder
.gather_terminator(terminator_loc
, block
.terminator());
256 impl<'a
, 'tcx
> MoveDataBuilder
<'a
, 'tcx
> {
257 fn gather_args(&mut self) {
258 for arg
in self.body
.args_iter() {
259 let path
= self.data
.rev_lookup
.locals
[arg
];
261 let init
= self.data
.inits
.push(Init
{
263 kind
: InitKind
::Deep
,
264 location
: InitLocation
::Argument(arg
),
267 debug
!("gather_args: adding init {:?} of {:?} for argument {:?}", init
, path
, arg
);
269 self.data
.init_path_map
[path
].push(init
);
273 fn gather_statement(&mut self, loc
: Location
, stmt
: &Statement
<'tcx
>) {
274 debug
!("gather_statement({:?}, {:?})", loc
, stmt
);
275 (Gatherer { builder: self, loc }
).gather_statement(stmt
);
278 fn gather_terminator(&mut self, loc
: Location
, term
: &Terminator
<'tcx
>) {
279 debug
!("gather_terminator({:?}, {:?})", loc
, term
);
280 (Gatherer { builder: self, loc }
).gather_terminator(term
);
284 struct Gatherer
<'b
, 'a
, 'tcx
> {
285 builder
: &'b
mut MoveDataBuilder
<'a
, 'tcx
>,
289 impl<'b
, 'a
, 'tcx
> Gatherer
<'b
, 'a
, 'tcx
> {
290 fn gather_statement(&mut self, stmt
: &Statement
<'tcx
>) {
292 StatementKind
::Assign(box(ref place
, ref rval
)) => {
293 self.create_move_path(place
);
294 if let RvalueInitializationState
::Shallow
= rval
.initialization_state() {
295 // Box starts out uninitialized - need to create a separate
296 // move-path for the interior so it will be separate from
298 self.create_move_path(&self.builder
.tcx
.mk_place_deref(place
.clone()));
299 self.gather_init(place
.as_ref(), InitKind
::Shallow
);
301 self.gather_init(place
.as_ref(), InitKind
::Deep
);
303 self.gather_rvalue(rval
);
305 StatementKind
::FakeRead(_
, ref place
) => {
306 self.create_move_path(place
);
308 StatementKind
::InlineAsm(ref asm
) => {
309 for (output
, kind
) in asm
.outputs
.iter().zip(&asm
.asm
.outputs
) {
310 if !kind
.is_indirect
{
311 self.gather_init(output
.as_ref(), InitKind
::Deep
);
314 for (_
, input
) in asm
.inputs
.iter() {
315 self.gather_operand(input
);
318 StatementKind
::StorageLive(_
) => {}
319 StatementKind
::StorageDead(local
) => {
320 self.gather_move(&Place
::from(local
));
322 StatementKind
::SetDiscriminant { .. }
=> {
324 stmt
.source_info
.span
,
325 "SetDiscriminant should not exist during borrowck"
328 StatementKind
::Retag { .. }
329 | StatementKind
::AscribeUserType(..)
330 | StatementKind
::Nop
=> {}
334 fn gather_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>) {
336 Rvalue
::Use(ref operand
)
337 | Rvalue
::Repeat(ref operand
, _
)
338 | Rvalue
::Cast(_
, ref operand
, _
)
339 | Rvalue
::UnaryOp(_
, ref operand
) => self.gather_operand(operand
),
340 Rvalue
::BinaryOp(ref _binop
, ref lhs
, ref rhs
)
341 | Rvalue
::CheckedBinaryOp(ref _binop
, ref lhs
, ref rhs
) => {
342 self.gather_operand(lhs
);
343 self.gather_operand(rhs
);
345 Rvalue
::Aggregate(ref _kind
, ref operands
) => {
346 for operand
in operands
{
347 self.gather_operand(operand
);
351 | Rvalue
::Discriminant(..)
353 | Rvalue
::NullaryOp(NullOp
::SizeOf
, _
)
354 | Rvalue
::NullaryOp(NullOp
::Box
, _
) => {
355 // This returns an rvalue with uninitialized contents. We can't
356 // move out of it here because it is an rvalue - assignments always
357 // completely initialize their place.
359 // However, this does not matter - MIR building is careful to
360 // only emit a shallow free for the partially-initialized
363 // In any case, if we want to fix this, we have to register a
364 // special move and change the `statement_effect` functions.
369 fn gather_terminator(&mut self, term
: &Terminator
<'tcx
>) {
371 TerminatorKind
::Goto { target: _ }
372 | TerminatorKind
::Resume
373 | TerminatorKind
::Abort
374 | TerminatorKind
::GeneratorDrop
375 | TerminatorKind
::FalseEdges { .. }
376 | TerminatorKind
::FalseUnwind { .. }
377 | TerminatorKind
::Unreachable
=> {}
379 TerminatorKind
::Return
=> {
380 self.gather_move(&Place
::return_place());
383 TerminatorKind
::Assert { ref cond, .. }
=> {
384 self.gather_operand(cond
);
387 TerminatorKind
::SwitchInt { ref discr, .. }
=> {
388 self.gather_operand(discr
);
391 TerminatorKind
::Yield { ref value, .. }
=> {
392 self.gather_operand(value
);
395 TerminatorKind
::Drop { ref location, target: _, unwind: _ }
=> {
396 self.gather_move(location
);
398 TerminatorKind
::DropAndReplace { ref location, ref value, .. }
=> {
399 self.create_move_path(location
);
400 self.gather_operand(value
);
401 self.gather_init(location
.as_ref(), InitKind
::Deep
);
403 TerminatorKind
::Call
{
410 self.gather_operand(func
);
412 self.gather_operand(arg
);
414 if let Some((ref destination
, _bb
)) = *destination
{
415 self.create_move_path(destination
);
416 self.gather_init(destination
.as_ref(), InitKind
::NonPanicPathOnly
);
422 fn gather_operand(&mut self, operand
: &Operand
<'tcx
>) {
424 Operand
::Constant(..) | Operand
::Copy(..) => {}
// not-a-move
425 Operand
::Move(ref place
) => {
427 self.gather_move(place
);
432 fn gather_move(&mut self, place
: &Place
<'tcx
>) {
433 debug
!("gather_move({:?}, {:?})", self.loc
, place
);
437 ProjectionElem
::Subslice { from, to, from_end: false }
,
438 ] = **place
.projection
{
439 // Split `Subslice` patterns into the corresponding list of
440 // `ConstIndex` patterns. This is done to ensure that all move paths
441 // are disjoint, which is expected by drop elaboration.
442 let base_place
= Place
{
443 base
: place
.base
.clone(),
444 projection
: self.builder
.tcx
.intern_place_elems(base
),
446 let base_path
= match self.move_path_for(&base_place
) {
448 Err(MoveError
::UnionMove { path }
) => {
449 self.record_move(place
, path
);
452 Err(error @ MoveError
::IllegalMove { .. }
) => {
453 self.builder
.errors
.push((base_place
, error
));
457 let base_ty
= base_place
.ty(self.builder
.body
, self.builder
.tcx
).ty
;
458 let len
: u32 = match base_ty
.kind
{
459 ty
::Array(_
, size
) => {
460 let length
= size
.eval_usize(self.builder
.tcx
, self.builder
.param_env
);
461 length
.try_into().expect(
462 "slice pattern of array with more than u32::MAX elements"
465 _
=> bug
!("from_end: false slice pattern of non-array type"),
467 for offset
in from
..to
{
468 let elem
= ProjectionElem
::ConstantIndex
{
473 let path
= self.add_move_path(
476 |tcx
| tcx
.mk_place_elem(base_place
.clone(), elem
),
478 self.record_move(place
, path
);
481 match self.move_path_for(place
) {
482 Ok(path
) | Err(MoveError
::UnionMove { path }
) => self.record_move(place
, path
),
483 Err(error @ MoveError
::IllegalMove { .. }
) => {
484 self.builder
.errors
.push((place
.clone(), error
));
490 fn record_move(&mut self, place
: &Place
<'tcx
>, path
: MovePathIndex
) {
491 let move_out
= self.builder
.data
.moves
.push(MoveOut { path: path, source: self.loc }
);
493 "gather_move({:?}, {:?}): adding move {:?} of {:?}",
494 self.loc
, place
, move_out
, path
496 self.builder
.data
.path_map
[path
].push(move_out
);
497 self.builder
.data
.loc_map
[self.loc
].push(move_out
);
500 fn gather_init(&mut self, place
: PlaceRef
<'cx
, 'tcx
>, kind
: InitKind
) {
501 debug
!("gather_init({:?}, {:?})", self.loc
, place
);
503 let mut place
= place
;
505 // Check if we are assigning into a field of a union, if so, lookup the place
506 // of the union so it is marked as initialized again.
507 if let [proj_base @
.., ProjectionElem
::Field(_
, _
)] = place
.projection
{
508 if let ty
::Adt(def
, _
) =
509 Place
::ty_from(place
.base
, proj_base
, self.builder
.body
, self.builder
.tcx
).ty
.kind
512 place
= PlaceRef { base: place.base, projection: proj_base }
517 if let LookupResult
::Exact(path
) = self.builder
.data
.rev_lookup
.find(place
) {
518 let init
= self.builder
.data
.inits
.push(Init
{
519 location
: InitLocation
::Statement(self.loc
),
525 "gather_init({:?}, {:?}): adding init {:?} of {:?}",
526 self.loc
, place
, init
, path
529 self.builder
.data
.init_path_map
[path
].push(init
);
530 self.builder
.data
.init_loc_map
[self.loc
].push(init
);