1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc::ty::{self, TyCtxt};
13 use rustc::mir::tcx::RvalueInitializationState;
14 use rustc::util::nodemap::FxHashMap;
15 use rustc_data_structures::indexed_vec::{IndexVec};
17 use syntax::codemap::DUMMY_SP;
19 use std::collections::hash_map::Entry;
22 use super::abs_domain::Lift;
24 use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
26 pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
28 tcx: TyCtxt<'a, 'tcx, 'tcx>,
29 param_env: ty::ParamEnv<'tcx>,
33 pub enum MovePathError {
35 UnionMove { path: MovePathIndex },
38 impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
39 fn new(mir: &'a Mir<'tcx>,
40 tcx: TyCtxt<'a, 'tcx, 'tcx>,
41 param_env: ty::ParamEnv<'tcx>)
43 let mut move_paths = IndexVec::new();
44 let mut path_map = IndexVec::new();
51 moves: IndexVec::new(),
52 loc_map: LocationMap::new(mir),
53 rev_lookup: MovePathLookup {
54 locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
55 Self::new_move_path(&mut move_paths, &mut path_map, None, v)
57 projections: FxHashMap(),
65 fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
66 path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
67 parent: Option<MovePathIndex>,
71 let move_path = move_paths.push(MovePath {
78 if let Some(parent) = parent {
80 mem::replace(&mut move_paths[parent].first_child, Some(move_path));
81 move_paths[move_path].next_sibling = next_sibling;
84 let path_map_ent = path_map.push(vec![]);
85 assert_eq!(path_map_ent, move_path);
89 /// This creates a MovePath for a given lvalue, returning an `MovePathError`
90 /// if that lvalue can't be moved from.
92 /// NOTE: lvalues 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, lval: &Lvalue<'tcx>)
97 -> Result<MovePathIndex, MovePathError>
99 debug!("lookup({:?})", lval);
101 Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
102 // error: can't move out of a static
103 Lvalue::Static(..) => Err(MovePathError::IllegalMove),
104 Lvalue::Projection(ref proj) => {
105 self.move_path_for_projection(lval, proj)
110 fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
111 // This is an assignment, not a move, so this not being a valid
113 let _ = self.move_path_for(lval);
116 fn move_path_for_projection(&mut self,
118 proj: &LvalueProjection<'tcx>)
119 -> Result<MovePathIndex, MovePathError>
121 let base = try!(self.move_path_for(&proj.base));
122 let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
124 // error: can't move out of borrowed content
125 ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
126 // error: can't move out of struct with destructor
127 ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
128 return Err(MovePathError::IllegalMove),
129 // move out of union - always move the entire union
130 ty::TyAdt(adt, _) if adt.is_union() =>
131 return Err(MovePathError::UnionMove { path: base }),
132 // error: can't move out of a slice
134 return Err(MovePathError::IllegalMove),
135 ty::TyArray(..) => match proj.elem {
136 // error: can't move out of an array
137 ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
139 // FIXME: still badly broken
144 match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
145 Entry::Occupied(ent) => Ok(*ent.get()),
146 Entry::Vacant(ent) => {
147 let path = Self::new_move_path(
148 &mut self.data.move_paths,
149 &mut self.data.path_map,
159 fn finalize(self) -> MoveData<'tcx> {
161 debug!("moves for {:?}:", self.mir.span);
162 for (j, mo) in self.data.moves.iter_enumerated() {
163 debug!(" {:?} = {:?}", j, mo);
165 debug!("move paths for {:?}:", self.mir.span);
166 for (j, path) in self.data.move_paths.iter_enumerated() {
167 debug!(" {:?} = {:?}", j, path);
175 pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
176 tcx: TyCtxt<'a, 'tcx, 'tcx>,
177 param_env: ty::ParamEnv<'tcx>)
179 let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
181 for (bb, block) in mir.basic_blocks().iter_enumerated() {
182 for (i, stmt) in block.statements.iter().enumerate() {
183 let source = Location { block: bb, statement_index: i };
184 builder.gather_statement(source, stmt);
187 let terminator_loc = Location {
189 statement_index: block.statements.len()
191 builder.gather_terminator(terminator_loc, block.terminator());
197 impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
198 fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
199 debug!("gather_statement({:?}, {:?})", loc, stmt);
201 StatementKind::Assign(ref lval, ref rval) => {
202 self.create_move_path(lval);
203 if let RvalueInitializationState::Shallow = rval.initialization_state() {
204 // Box starts out uninitialized - need to create a separate
205 // move-path for the interior so it will be separate from
207 self.create_move_path(&lval.clone().deref());
209 self.gather_rvalue(loc, rval);
211 StatementKind::StorageLive(_) |
212 StatementKind::StorageDead(_) => {}
213 StatementKind::SetDiscriminant{ .. } => {
214 span_bug!(stmt.source_info.span,
215 "SetDiscriminant should not exist during borrowck");
217 StatementKind::InlineAsm { .. } |
218 StatementKind::EndRegion(_) |
219 StatementKind::Validate(..) |
220 StatementKind::Nop => {}
224 fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
226 Rvalue::Use(ref operand) |
227 Rvalue::Repeat(ref operand, _) |
228 Rvalue::Cast(_, ref operand, _) |
229 Rvalue::UnaryOp(_, ref operand) => {
230 self.gather_operand(loc, operand)
232 Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
233 Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
234 self.gather_operand(loc, lhs);
235 self.gather_operand(loc, rhs);
237 Rvalue::Aggregate(ref _kind, ref operands) => {
238 for operand in operands {
239 self.gather_operand(loc, operand);
243 Rvalue::Discriminant(..) |
245 Rvalue::NullaryOp(NullOp::SizeOf, _) |
246 Rvalue::NullaryOp(NullOp::Box, _) => {
247 // This returns an rvalue with uninitialized contents. We can't
248 // move out of it here because it is an rvalue - assignments always
249 // completely initialize their lvalue.
251 // However, this does not matter - MIR building is careful to
252 // only emit a shallow free for the partially-initialized
255 // In any case, if we want to fix this, we have to register a
256 // special move and change the `statement_effect` functions.
261 fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
262 debug!("gather_terminator({:?}, {:?})", loc, term);
264 TerminatorKind::Goto { target: _ } |
265 TerminatorKind::Resume |
266 TerminatorKind::Unreachable => { }
268 TerminatorKind::Return => {
269 self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
272 TerminatorKind::Assert { .. } |
273 TerminatorKind::SwitchInt { .. } => {
274 // branching terminators - these don't move anything
277 TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
278 self.gather_move(loc, location);
280 TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
281 self.create_move_path(location);
282 self.gather_operand(loc, value);
284 TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
285 self.gather_operand(loc, func);
287 self.gather_operand(loc, arg);
289 if let Some((ref destination, _bb)) = *destination {
290 self.create_move_path(destination);
296 fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
298 Operand::Constant(..) => {} // not-a-move
299 Operand::Consume(ref lval) => { // a move
300 self.gather_move(loc, lval);
305 fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
306 debug!("gather_move({:?}, {:?})", loc, lval);
308 let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
309 if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
310 debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
314 let path = match self.move_path_for(lval) {
315 Ok(path) | Err(MovePathError::UnionMove { path }) => path,
316 Err(MovePathError::IllegalMove) => {
317 // Moving out of a bad path. Eventually, this should be a MIR
318 // borrowck error instead of a bug.
319 span_bug!(self.mir.span,
320 "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
324 let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
326 debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
327 loc, lval, move_out, path);
329 self.data.path_map[path].push(move_out);
330 self.data.loc_map[loc].push(move_out);