]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_dataflow/src/move_paths/mod.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_mir_dataflow / src / move_paths / mod.rs
CommitLineData
064997fb 1use crate::move_paths::builder::MoveDat;
dfeec247 2use rustc_data_structures::fx::FxHashMap;
c295e0f8 3use rustc_index::vec::IndexVec;
ba9703b0
XL
4use rustc_middle::mir::*;
5use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
dfeec247 6use rustc_span::Span;
a1dfa0c6 7use smallvec::SmallVec;
9e0c209e 8
54a0048b 9use std::fmt;
9e0c209e 10use std::ops::{Index, IndexMut};
54a0048b 11
041b39d2
XL
12use self::abs_domain::{AbstractElem, Lift};
13
14mod abs_domain;
54a0048b 15
e74abb32 16rustc_index::newtype_index! {
48663c56
XL
17 pub struct MovePathIndex {
18 DEBUG_FORMAT = "mp{}"
54a0048b 19 }
48663c56 20}
54a0048b 21
c295e0f8
XL
22impl polonius_engine::Atom for MovePathIndex {
23 fn index(self) -> usize {
24 rustc_index::vec::Idx::index(self)
25 }
26}
27
e74abb32 28rustc_index::newtype_index! {
48663c56
XL
29 pub struct MoveOutIndex {
30 DEBUG_FORMAT = "mo{}"
31 }
54a0048b
SL
32}
33
e74abb32 34rustc_index::newtype_index! {
48663c56
XL
35 pub struct InitIndex {
36 DEBUG_FORMAT = "in{}"
37 }
38}
54a0048b 39
041b39d2 40impl MoveOutIndex {
5e7ed085
FG
41 pub fn move_path_index(self, move_data: &MoveData<'_>) -> MovePathIndex {
42 move_data.moves[self].path
3157f602
XL
43 }
44}
45
54a0048b
SL
46/// `MovePath` is a canonicalized representation of a path that is
47/// moved or assigned to.
48///
49/// It follows a tree structure.
50///
51/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
2c00a5a8 52/// move *out* of the place `x.m`.
54a0048b
SL
53///
54/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
55/// one of them will link to the other via the `next_sibling` field,
56/// and the other will have no entry in its `next_sibling` field), and
57/// they both have the MovePath representing `x` as their parent.
58#[derive(Clone)]
59pub struct MovePath<'tcx> {
60 pub next_sibling: Option<MovePathIndex>,
61 pub first_child: Option<MovePathIndex>,
62 pub parent: Option<MovePathIndex>,
ff7c6d11 63 pub place: Place<'tcx>,
54a0048b
SL
64}
65
0bf4aa26 66impl<'tcx> MovePath<'tcx> {
74b04a01
XL
67 /// Returns an iterator over the parents of `self`.
68 pub fn parents<'a>(
69 &self,
70 move_paths: &'a IndexVec<MovePathIndex, MovePath<'tcx>>,
71 ) -> impl 'a + Iterator<Item = (MovePathIndex, &'a MovePath<'tcx>)> {
72 let first = self.parent.map(|mpi| (mpi, &move_paths[mpi]));
73 MovePathLinearIter {
74 next: first,
75 fetch_next: move |_, parent: &MovePath<'_>| {
76 parent.parent.map(|mpi| (mpi, &move_paths[mpi]))
77 },
78 }
79 }
80
81 /// Returns an iterator over the immediate children of `self`.
82 pub fn children<'a>(
83 &self,
84 move_paths: &'a IndexVec<MovePathIndex, MovePath<'tcx>>,
85 ) -> impl 'a + Iterator<Item = (MovePathIndex, &'a MovePath<'tcx>)> {
86 let first = self.first_child.map(|mpi| (mpi, &move_paths[mpi]));
87 MovePathLinearIter {
88 next: first,
89 fetch_next: move |_, child: &MovePath<'_>| {
90 child.next_sibling.map(|mpi| (mpi, &move_paths[mpi]))
91 },
92 }
93 }
94
95 /// Finds the closest descendant of `self` for which `f` returns `true` using a breadth-first
96 /// search.
97 ///
98 /// `f` will **not** be called on `self`.
99 pub fn find_descendant(
9fa01778
XL
100 &self,
101 move_paths: &IndexVec<MovePathIndex, MovePath<'_>>,
74b04a01
XL
102 f: impl Fn(MovePathIndex) -> bool,
103 ) -> Option<MovePathIndex> {
104 let mut todo = if let Some(child) = self.first_child {
105 vec![child]
106 } else {
107 return None;
108 };
109
110 while let Some(mpi) = todo.pop() {
111 if f(mpi) {
112 return Some(mpi);
113 }
114
115 let move_path = &move_paths[mpi];
116 if let Some(child) = move_path.first_child {
117 todo.push(child);
118 }
0bf4aa26 119
74b04a01
XL
120 // After we've processed the original `mpi`, we should always
121 // traverse the siblings of any of its children.
122 if let Some(sibling) = move_path.next_sibling {
123 todo.push(sibling);
124 }
0bf4aa26
XL
125 }
126
74b04a01 127 None
0bf4aa26
XL
128 }
129}
130
54a0048b 131impl<'tcx> fmt::Debug for MovePath<'tcx> {
9fa01778 132 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
54a0048b
SL
133 write!(w, "MovePath {{")?;
134 if let Some(parent) = self.parent {
135 write!(w, " parent: {:?},", parent)?;
136 }
137 if let Some(first_child) = self.first_child {
138 write!(w, " first_child: {:?},", first_child)?;
139 }
140 if let Some(next_sibling) = self.next_sibling {
141 write!(w, " next_sibling: {:?}", next_sibling)?;
142 }
ff7c6d11 143 write!(w, " place: {:?} }}", self.place)
54a0048b
SL
144 }
145}
146
3b2f2976 147impl<'tcx> fmt::Display for MovePath<'tcx> {
9fa01778 148 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
ff7c6d11 149 write!(w, "{:?}", self.place)
3b2f2976
XL
150 }
151}
152
74b04a01
XL
153struct MovePathLinearIter<'a, 'tcx, F> {
154 next: Option<(MovePathIndex, &'a MovePath<'tcx>)>,
155 fetch_next: F,
156}
157
158impl<'a, 'tcx, F> Iterator for MovePathLinearIter<'a, 'tcx, F>
159where
160 F: FnMut(MovePathIndex, &'a MovePath<'tcx>) -> Option<(MovePathIndex, &'a MovePath<'tcx>)>,
161{
162 type Item = (MovePathIndex, &'a MovePath<'tcx>);
163
164 fn next(&mut self) -> Option<Self::Item> {
165 let ret = self.next.take()?;
166 self.next = (self.fetch_next)(ret.0, ret.1);
167 Some(ret)
168 }
169}
170
3157f602 171#[derive(Debug)]
54a0048b 172pub struct MoveData<'tcx> {
9e0c209e
SL
173 pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
174 pub moves: IndexVec<MoveOutIndex, MoveOut>,
175 /// Each Location `l` is mapped to the MoveOut's that are effects
176 /// of executing the code at `l`. (There can be multiple MoveOut's
177 /// for a given `l` because each MoveOut is associated with one
178 /// particular path being moved.)
a1dfa0c6
XL
179 pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
180 pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
532ac7d7 181 pub rev_lookup: MovePathLookup,
ff7c6d11
XL
182 pub inits: IndexVec<InitIndex, Init>,
183 /// Each Location `l` is mapped to the Inits that are effects
184 /// of executing the code at `l`.
a1dfa0c6
XL
185 pub init_loc_map: LocationMap<SmallVec<[InitIndex; 4]>>,
186 pub init_path_map: IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
54a0048b
SL
187}
188
32a655c1
SL
189pub trait HasMoveData<'tcx> {
190 fn move_data(&self) -> &MoveData<'tcx>;
191}
192
3157f602 193#[derive(Debug)]
9e0c209e 194pub struct LocationMap<T> {
54a0048b 195 /// Location-indexed (BasicBlock for outer index, index within BB
9e0c209e 196 /// for inner index) map.
041b39d2 197 pub(crate) map: IndexVec<BasicBlock, Vec<T>>,
54a0048b
SL
198}
199
9e0c209e
SL
200impl<T> Index<Location> for LocationMap<T> {
201 type Output = T;
54a0048b 202 fn index(&self, index: Location) -> &Self::Output {
9e0c209e 203 &self.map[index.block][index.statement_index]
54a0048b
SL
204 }
205}
206
9e0c209e
SL
207impl<T> IndexMut<Location> for LocationMap<T> {
208 fn index_mut(&mut self, index: Location) -> &mut Self::Output {
209 &mut self.map[index.block][index.statement_index]
210 }
54a0048b
SL
211}
212
e1599b0c
XL
213impl<T> LocationMap<T>
214where
215 T: Default + Clone,
216{
dc9dc135 217 fn new(body: &Body<'_>) -> Self {
9e0c209e 218 LocationMap {
e1599b0c 219 map: body
f2b60f7d 220 .basic_blocks
e1599b0c
XL
221 .iter()
222 .map(|block| vec![T::default(); block.statements.len() + 1])
223 .collect(),
9e0c209e 224 }
54a0048b
SL
225 }
226}
227
228/// `MoveOut` represents a point in a program that moves out of some
229/// L-value; i.e., "creates" uninitialized memory.
230///
231/// With respect to dataflow analysis:
232/// - Generated by moves and declaration of uninitialized variables.
233/// - Killed by assignments to the memory.
234#[derive(Copy, Clone)]
235pub struct MoveOut {
236 /// path being moved
237 pub path: MovePathIndex,
238 /// location of move
239 pub source: Location,
240}
241
242impl fmt::Debug for MoveOut {
9fa01778 243 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
9e0c209e 244 write!(fmt, "{:?}@{:?}", self.path, self.source)
54a0048b
SL
245 }
246}
247
ff7c6d11
XL
248/// `Init` represents a point in a program that initializes some L-value;
249#[derive(Copy, Clone)]
250pub struct Init {
251 /// path being initialized
252 pub path: MovePathIndex,
b7449926
XL
253 /// location of initialization
254 pub location: InitLocation,
ff7c6d11
XL
255 /// Extra information about this initialization
256 pub kind: InitKind,
257}
258
b7449926
XL
259/// Initializations can be from an argument or from a statement. Arguments
260/// do not have locations, in those cases the `Local` is kept..
261#[derive(Copy, Clone, Debug, PartialEq, Eq)]
262pub enum InitLocation {
263 Argument(Local),
264 Statement(Location),
265}
266
ff7c6d11
XL
267/// Additional information about the initialization.
268#[derive(Copy, Clone, Debug, PartialEq, Eq)]
269pub enum InitKind {
270 /// Deep init, even on panic
271 Deep,
272 /// Only does a shallow init
273 Shallow,
8faf50e0 274 /// This doesn't initialize the variable on panic (and a panic is possible).
ff7c6d11
XL
275 NonPanicPathOnly,
276}
277
278impl fmt::Debug for Init {
9fa01778 279 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
b7449926
XL
280 write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind)
281 }
282}
283
284impl Init {
c295e0f8 285 pub fn span<'tcx>(&self, body: &Body<'tcx>) -> Span {
b7449926 286 match self.location {
dc9dc135
XL
287 InitLocation::Argument(local) => body.local_decls[local].source_info.span,
288 InitLocation::Statement(location) => body.source_info(location).span,
b7449926 289 }
ff7c6d11
XL
290 }
291}
292
2c00a5a8 293/// Tables mapping from a place to its MovePathIndex.
3157f602 294#[derive(Debug)]
532ac7d7 295pub struct MovePathLookup {
c30ab7b3 296 locals: IndexVec<Local, MovePathIndex>,
54a0048b 297
ff7c6d11
XL
298 /// projections are made from a base-place and a projection
299 /// elem. The base-place will have a unique MovePathIndex; we use
54a0048b
SL
300 /// the latter as the index into the outer vector (narrowing
301 /// subsequent search so that it is solely relative to that
ff7c6d11 302 /// base-place). For the remaining lookup, we map the projection
54a0048b 303 /// elem to the associated MovePathIndex.
e1599b0c 304 projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>,
9e0c209e
SL
305}
306
3b2f2976 307mod builder;
54a0048b 308
9e0c209e
SL
309#[derive(Copy, Clone, Debug)]
310pub enum LookupResult {
311 Exact(MovePathIndex),
e1599b0c 312 Parent(Option<MovePathIndex>),
54a0048b
SL
313}
314
532ac7d7 315impl MovePathLookup {
54a0048b
SL
316 // Unlike the builder `fn move_path_for` below, this lookup
317 // alternative will *not* create a MovePath on the fly for an
2c00a5a8 318 // unknown place, but will rather return the nearest available
9e0c209e 319 // parent.
74b04a01
XL
320 pub fn find(&self, place: PlaceRef<'_>) -> LookupResult {
321 let mut result = self.locals[place.local];
e1599b0c
XL
322
323 for elem in place.projection.iter() {
324 if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
325 result = subpath;
326 } else {
327 return LookupResult::Parent(Some(result));
54a0048b 328 }
e1599b0c 329 }
dc9dc135 330
e1599b0c 331 LookupResult::Exact(result)
54a0048b 332 }
ff7c6d11
XL
333
334 pub fn find_local(&self, local: Local) -> MovePathIndex {
335 self.locals[local]
336 }
e1599b0c
XL
337
338 /// An enumerated iterator of `local`s and their associated
339 /// `MovePathIndex`es.
c295e0f8
XL
340 pub fn iter_locals_enumerated(
341 &self,
5e7ed085
FG
342 ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ {
343 self.locals.iter_enumerated().map(|(l, &idx)| (l, idx))
e1599b0c 344 }
54a0048b
SL
345}
346
ea8adc8c
XL
347#[derive(Debug)]
348pub struct IllegalMoveOrigin<'tcx> {
c295e0f8
XL
349 pub location: Location,
350 pub kind: IllegalMoveOriginKind<'tcx>,
ea8adc8c
XL
351}
352
353#[derive(Debug)]
c295e0f8 354pub enum IllegalMoveOriginKind<'tcx> {
94b46f34
XL
355 /// Illegal move due to attempt to move from behind a reference.
356 BorrowedContent {
8faf50e0
XL
357 /// The place the reference refers to: if erroneous code was trying to
358 /// move from `(*x).f` this will be `*x`.
359 target_place: Place<'tcx>,
94b46f34
XL
360 },
361
362 /// Illegal move due to attempt to move from field of an ADT that
363 /// implements `Drop`. Rust maintains invariant that all `Drop`
364 /// ADT's remain fully-initialized so that user-defined destructor
365 /// can safely read from all of the ADT's fields.
48663c56 366 InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> },
94b46f34
XL
367
368 /// Illegal move due to attempt to move out of a slice or array.
e1599b0c 369 InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool },
ea8adc8c
XL
370}
371
372#[derive(Debug)]
373pub enum MoveError<'tcx> {
374 IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> },
375 UnionMove { path: MovePathIndex },
376}
377
378impl<'tcx> MoveError<'tcx> {
8faf50e0
XL
379 fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
380 let origin = IllegalMoveOrigin { location, kind };
ea8adc8c
XL
381 MoveError::IllegalMove { cannot_move_out_of: origin }
382 }
383}
384
dc9dc135
XL
385impl<'tcx> MoveData<'tcx> {
386 pub fn gather_moves(
387 body: &Body<'tcx>,
388 tcx: TyCtxt<'tcx>,
60c5eb7d 389 param_env: ParamEnv<'tcx>,
064997fb 390 ) -> MoveDat<'tcx> {
60c5eb7d 391 builder::gather_moves(body, tcx, param_env)
54a0048b 392 }
b7449926
XL
393
394 /// For the move path `mpi`, returns the root local variable (if any) that starts the path.
395 /// (e.g., for a path like `a.b.c` returns `Some(a)`)
396 pub fn base_local(&self, mut mpi: MovePathIndex) -> Option<Local> {
397 loop {
398 let path = &self.move_paths[mpi];
e74abb32 399 if let Some(l) = path.place.as_local() {
e1599b0c
XL
400 return Some(l);
401 }
402 if let Some(parent) = path.parent {
403 mpi = parent;
404 continue;
405 } else {
406 return None;
407 }
b7449926
XL
408 }
409 }
74b04a01
XL
410
411 pub fn find_in_move_path_or_its_descendants(
412 &self,
413 root: MovePathIndex,
414 pred: impl Fn(MovePathIndex) -> bool,
415 ) -> Option<MovePathIndex> {
416 if pred(root) {
417 return Some(root);
418 }
419
420 self.move_paths[root].find_descendant(&self.move_paths, pred)
421 }
54a0048b 422}