-
Notifications
You must be signed in to change notification settings - Fork 12.7k
/
mod.rs
343 lines (299 loc) · 10.3 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
use core::slice::Iter;
use rustc::mir::*;
use rustc::ty::{Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::{Enumerated, Idx, IndexVec};
use smallvec::SmallVec;
use syntax_pos::Span;
use std::fmt;
use std::ops::{Index, IndexMut};
use self::abs_domain::{AbstractElem, Lift};
mod abs_domain;
newtype_index! {
pub struct MovePathIndex {
DEBUG_FORMAT = "mp{}"
}
}
newtype_index! {
pub struct MoveOutIndex {
DEBUG_FORMAT = "mo{}"
}
}
newtype_index! {
pub struct InitIndex {
DEBUG_FORMAT = "in{}"
}
}
impl MoveOutIndex {
pub fn move_path_index(&self, move_data: &MoveData<'_>) -> MovePathIndex {
move_data.moves[*self].path
}
}
/// `MovePath` is a canonicalized representation of a path that is
/// moved or assigned to.
///
/// It follows a tree structure.
///
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
/// move *out* of the place `x.m`.
///
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
/// one of them will link to the other via the `next_sibling` field,
/// and the other will have no entry in its `next_sibling` field), and
/// they both have the MovePath representing `x` as their parent.
#[derive(Clone)]
pub struct MovePath<'tcx> {
pub next_sibling: Option<MovePathIndex>,
pub first_child: Option<MovePathIndex>,
pub parent: Option<MovePathIndex>,
pub place: Place<'tcx>,
}
impl<'tcx> MovePath<'tcx> {
pub fn parents(
&self,
move_paths: &IndexVec<MovePathIndex, MovePath<'_>>,
) -> Vec<MovePathIndex> {
let mut parents = Vec::new();
let mut curr_parent = self.parent;
while let Some(parent_mpi) = curr_parent {
parents.push(parent_mpi);
curr_parent = move_paths[parent_mpi].parent;
}
parents
}
}
impl<'tcx> fmt::Debug for MovePath<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "MovePath {{")?;
if let Some(parent) = self.parent {
write!(w, " parent: {:?},", parent)?;
}
if let Some(first_child) = self.first_child {
write!(w, " first_child: {:?},", first_child)?;
}
if let Some(next_sibling) = self.next_sibling {
write!(w, " next_sibling: {:?}", next_sibling)?;
}
write!(w, " place: {:?} }}", self.place)
}
}
impl<'tcx> fmt::Display for MovePath<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "{:?}", self.place)
}
}
#[derive(Debug)]
pub struct MoveData<'tcx> {
pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
pub moves: IndexVec<MoveOutIndex, MoveOut>,
/// Each Location `l` is mapped to the MoveOut's that are effects
/// of executing the code at `l`. (There can be multiple MoveOut's
/// for a given `l` because each MoveOut is associated with one
/// particular path being moved.)
pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
pub rev_lookup: MovePathLookup,
pub inits: IndexVec<InitIndex, Init>,
/// Each Location `l` is mapped to the Inits that are effects
/// of executing the code at `l`.
pub init_loc_map: LocationMap<SmallVec<[InitIndex; 4]>>,
pub init_path_map: IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
}
pub trait HasMoveData<'tcx> {
fn move_data(&self) -> &MoveData<'tcx>;
}
#[derive(Debug)]
pub struct LocationMap<T> {
/// Location-indexed (BasicBlock for outer index, index within BB
/// for inner index) map.
pub(crate) map: IndexVec<BasicBlock, Vec<T>>,
}
impl<T> Index<Location> for LocationMap<T> {
type Output = T;
fn index(&self, index: Location) -> &Self::Output {
&self.map[index.block][index.statement_index]
}
}
impl<T> IndexMut<Location> for LocationMap<T> {
fn index_mut(&mut self, index: Location) -> &mut Self::Output {
&mut self.map[index.block][index.statement_index]
}
}
impl<T> LocationMap<T>
where
T: Default + Clone,
{
fn new(body: &Body<'_>) -> Self {
LocationMap {
map: body
.basic_blocks()
.iter()
.map(|block| vec![T::default(); block.statements.len() + 1])
.collect(),
}
}
}
/// `MoveOut` represents a point in a program that moves out of some
/// L-value; i.e., "creates" uninitialized memory.
///
/// With respect to dataflow analysis:
/// - Generated by moves and declaration of uninitialized variables.
/// - Killed by assignments to the memory.
#[derive(Copy, Clone)]
pub struct MoveOut {
/// path being moved
pub path: MovePathIndex,
/// location of move
pub source: Location,
}
impl fmt::Debug for MoveOut {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}@{:?}", self.path, self.source)
}
}
/// `Init` represents a point in a program that initializes some L-value;
#[derive(Copy, Clone)]
pub struct Init {
/// path being initialized
pub path: MovePathIndex,
/// location of initialization
pub location: InitLocation,
/// Extra information about this initialization
pub kind: InitKind,
}
/// Initializations can be from an argument or from a statement. Arguments
/// do not have locations, in those cases the `Local` is kept..
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitLocation {
Argument(Local),
Statement(Location),
}
/// Additional information about the initialization.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitKind {
/// Deep init, even on panic
Deep,
/// Only does a shallow init
Shallow,
/// This doesn't initialize the variable on panic (and a panic is possible).
NonPanicPathOnly,
}
impl fmt::Debug for Init {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind)
}
}
impl Init {
crate fn span<'tcx>(&self, body: &Body<'tcx>) -> Span {
match self.location {
InitLocation::Argument(local) => body.local_decls[local].source_info.span,
InitLocation::Statement(location) => body.source_info(location).span,
}
}
}
/// Tables mapping from a place to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup {
locals: IndexVec<Local, MovePathIndex>,
/// projections are made from a base-place and a projection
/// elem. The base-place will have a unique MovePathIndex; we use
/// the latter as the index into the outer vector (narrowing
/// subsequent search so that it is solely relative to that
/// base-place). For the remaining lookup, we map the projection
/// elem to the associated MovePathIndex.
projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>,
}
mod builder;
#[derive(Copy, Clone, Debug)]
pub enum LookupResult {
Exact(MovePathIndex),
Parent(Option<MovePathIndex>),
}
impl MovePathLookup {
// Unlike the builder `fn move_path_for` below, this lookup
// alternative will *not* create a MovePath on the fly for an
// unknown place, but will rather return the nearest available
// parent.
pub fn find(&self, place_ref: PlaceRef<'_, '_>) -> LookupResult {
place_ref.iterate(|place_base, place_projection| {
let mut result = match place_base {
PlaceBase::Local(local) => self.locals[*local],
PlaceBase::Static(..) => return LookupResult::Parent(None),
};
for proj in place_projection {
if let Some(&subpath) = self.projections.get(&(result, proj.elem.lift())) {
result = subpath;
} else {
return LookupResult::Parent(Some(result));
}
}
LookupResult::Exact(result)
})
}
pub fn find_local(&self, local: Local) -> MovePathIndex {
self.locals[local]
}
/// An enumerated iterator of `local`s and their associated
/// `MovePathIndex`es.
pub fn iter_locals_enumerated(&self) -> Enumerated<Local, Iter<'_, MovePathIndex>> {
self.locals.iter_enumerated()
}
}
#[derive(Debug)]
pub struct IllegalMoveOrigin<'tcx> {
pub(crate) location: Location,
pub(crate) kind: IllegalMoveOriginKind<'tcx>,
}
#[derive(Debug)]
pub(crate) enum IllegalMoveOriginKind<'tcx> {
/// Illegal move due to attempt to move from `static` variable.
Static,
/// Illegal move due to attempt to move from behind a reference.
BorrowedContent {
/// The place the reference refers to: if erroneous code was trying to
/// move from `(*x).f` this will be `*x`.
target_place: Place<'tcx>,
},
/// Illegal move due to attempt to move from field of an ADT that
/// implements `Drop`. Rust maintains invariant that all `Drop`
/// ADT's remain fully-initialized so that user-defined destructor
/// can safely read from all of the ADT's fields.
InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
/// Illegal move due to attempt to move out of a slice or array.
InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
}
#[derive(Debug)]
pub enum MoveError<'tcx> {
IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
UnionMove { path: MovePathIndex },
}
impl<'tcx> MoveError<'tcx> {
fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
let origin = IllegalMoveOrigin { location, kind };
MoveError::IllegalMove { cannot_move_out_of: origin }
}
}
impl<'tcx> MoveData<'tcx> {
pub fn gather_moves(
body: &Body<'tcx>,
tcx: TyCtxt<'tcx>,
) -> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
builder::gather_moves(body, tcx)
}
/// For the move path `mpi`, returns the root local variable (if any) that starts the path.
/// (e.g., for a path like `a.b.c` returns `Some(a)`)
pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
loop {
let path = &self.move_paths[mpi];
if let Place { base: PlaceBase::Local(l), projection: None } = path.place {
return Some(l);
}
if let Some(parent) = path.parent {
mpi = parent;
continue;
} else {
return None;
}
}
}
}