Skip to content

Commit 869f05a

Browse files
committed
Factored MoveData construction code into builder submodule.
1 parent 9b8b8c6 commit 869f05a

File tree

2 files changed

+334
-313
lines changed

2 files changed

+334
-313
lines changed
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
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.
4+
//
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.
10+
11+
use rustc::ty::{self, TyCtxt};
12+
use rustc::mir::*;
13+
use rustc::mir::tcx::RvalueInitializationState;
14+
use rustc::util::nodemap::FxHashMap;
15+
use rustc_data_structures::indexed_vec::{IndexVec};
16+
17+
use syntax::codemap::DUMMY_SP;
18+
19+
use std::collections::hash_map::Entry;
20+
use std::mem;
21+
22+
use super::abs_domain::Lift;
23+
24+
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
25+
26+
pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
27+
mir: &'a Mir<'tcx>,
28+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
29+
param_env: ty::ParamEnv<'tcx>,
30+
data: MoveData<'tcx>,
31+
}
32+
33+
pub enum MovePathError {
34+
IllegalMove,
35+
UnionMove { path: MovePathIndex },
36+
}
37+
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>)
42+
-> Self {
43+
let mut move_paths = IndexVec::new();
44+
let mut path_map = IndexVec::new();
45+
46+
MoveDataBuilder {
47+
mir,
48+
tcx,
49+
param_env,
50+
data: MoveData {
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)
56+
}).collect(),
57+
projections: FxHashMap(),
58+
},
59+
move_paths,
60+
path_map,
61+
}
62+
}
63+
}
64+
65+
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
66+
path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
67+
parent: Option<MovePathIndex>,
68+
lvalue: Lvalue<'tcx>)
69+
-> MovePathIndex
70+
{
71+
let move_path = move_paths.push(MovePath {
72+
next_sibling: None,
73+
first_child: None,
74+
parent,
75+
lvalue,
76+
});
77+
78+
if let Some(parent) = parent {
79+
let next_sibling =
80+
mem::replace(&mut move_paths[parent].first_child, Some(move_path));
81+
move_paths[move_path].next_sibling = next_sibling;
82+
}
83+
84+
let path_map_ent = path_map.push(vec![]);
85+
assert_eq!(path_map_ent, move_path);
86+
move_path
87+
}
88+
89+
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
90+
/// if that lvalue can't be moved from.
91+
///
92+
/// NOTE: lvalues behind references *do not* get a move path, which is
93+
/// problematic for borrowck.
94+
///
95+
/// Maybe we should have separate "borrowck" and "moveck" modes.
96+
fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
97+
-> Result<MovePathIndex, MovePathError>
98+
{
99+
debug!("lookup({:?})", lval);
100+
match *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)
106+
}
107+
}
108+
}
109+
110+
fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
111+
// This is an assignment, not a move, so this not being a valid
112+
// move path is OK.
113+
let _ = self.move_path_for(lval);
114+
}
115+
116+
fn move_path_for_projection(&mut self,
117+
lval: &Lvalue<'tcx>,
118+
proj: &LvalueProjection<'tcx>)
119+
-> Result<MovePathIndex, MovePathError>
120+
{
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);
123+
match lv_ty.sty {
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
133+
ty::TySlice(..) =>
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),
138+
_ => {
139+
// FIXME: still badly broken
140+
}
141+
},
142+
_ => {}
143+
};
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,
150+
Some(base),
151+
lval.clone()
152+
);
153+
ent.insert(path);
154+
Ok(path)
155+
}
156+
}
157+
}
158+
159+
fn finalize(self) -> MoveData<'tcx> {
160+
debug!("{}", {
161+
debug!("moves for {:?}:", self.mir.span);
162+
for (j, mo) in self.data.moves.iter_enumerated() {
163+
debug!(" {:?} = {:?}", j, mo);
164+
}
165+
debug!("move paths for {:?}:", self.mir.span);
166+
for (j, path) in self.data.move_paths.iter_enumerated() {
167+
debug!(" {:?} = {:?}", j, path);
168+
}
169+
"done dumping moves"
170+
});
171+
self.data
172+
}
173+
}
174+
175+
pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
176+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
177+
param_env: ty::ParamEnv<'tcx>)
178+
-> MoveData<'tcx> {
179+
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
180+
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);
185+
}
186+
187+
let terminator_loc = Location {
188+
block: bb,
189+
statement_index: block.statements.len()
190+
};
191+
builder.gather_terminator(terminator_loc, block.terminator());
192+
}
193+
194+
builder.finalize()
195+
}
196+
197+
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
198+
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
199+
debug!("gather_statement({:?}, {:?})", loc, stmt);
200+
match stmt.kind {
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
206+
// the exterior.
207+
self.create_move_path(&lval.clone().deref());
208+
}
209+
self.gather_rvalue(loc, rval);
210+
}
211+
StatementKind::StorageLive(_) |
212+
StatementKind::StorageDead(_) => {}
213+
StatementKind::SetDiscriminant{ .. } => {
214+
span_bug!(stmt.source_info.span,
215+
"SetDiscriminant should not exist during borrowck");
216+
}
217+
StatementKind::InlineAsm { .. } |
218+
StatementKind::EndRegion(_) |
219+
StatementKind::Validate(..) |
220+
StatementKind::Nop => {}
221+
}
222+
}
223+
224+
fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
225+
match *rvalue {
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)
231+
}
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);
236+
}
237+
Rvalue::Aggregate(ref _kind, ref operands) => {
238+
for operand in operands {
239+
self.gather_operand(loc, operand);
240+
}
241+
}
242+
Rvalue::Ref(..) |
243+
Rvalue::Discriminant(..) |
244+
Rvalue::Len(..) |
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.
250+
//
251+
// However, this does not matter - MIR building is careful to
252+
// only emit a shallow free for the partially-initialized
253+
// temporary.
254+
//
255+
// In any case, if we want to fix this, we have to register a
256+
// special move and change the `statement_effect` functions.
257+
}
258+
}
259+
}
260+
261+
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
262+
debug!("gather_terminator({:?}, {:?})", loc, term);
263+
match term.kind {
264+
TerminatorKind::Goto { target: _ } |
265+
TerminatorKind::Resume |
266+
TerminatorKind::Unreachable => { }
267+
268+
TerminatorKind::Return => {
269+
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
270+
}
271+
272+
TerminatorKind::Assert { .. } |
273+
TerminatorKind::SwitchInt { .. } => {
274+
// branching terminators - these don't move anything
275+
}
276+
277+
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
278+
self.gather_move(loc, location);
279+
}
280+
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
281+
self.create_move_path(location);
282+
self.gather_operand(loc, value);
283+
}
284+
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
285+
self.gather_operand(loc, func);
286+
for arg in args {
287+
self.gather_operand(loc, arg);
288+
}
289+
if let Some((ref destination, _bb)) = *destination {
290+
self.create_move_path(destination);
291+
}
292+
}
293+
}
294+
}
295+
296+
fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
297+
match *operand {
298+
Operand::Constant(..) => {} // not-a-move
299+
Operand::Consume(ref lval) => { // a move
300+
self.gather_move(loc, lval);
301+
}
302+
}
303+
}
304+
305+
fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
306+
debug!("gather_move({:?}, {:?})", loc, lval);
307+
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);
311+
return
312+
}
313+
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 {:?}",
321+
lval, lv_ty, loc);
322+
}
323+
};
324+
let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
325+
326+
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
327+
loc, lval, move_out, path);
328+
329+
self.data.path_map[path].push(move_out);
330+
self.data.loc_map[loc].push(move_out);
331+
}
332+
}

0 commit comments

Comments
 (0)