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