Skip to main content

rustc_mir_dataflow/move_paths/
mod.rs

1//! [`MovePath`]s track the initialization state of places and their sub-paths.
2
3use std::fmt;
4use std::ops::{Index, IndexMut};
5
6use rustc_abi::{FieldIdx, VariantIdx};
7use rustc_data_structures::fx::FxHashMap;
8use rustc_index::{IndexSlice, IndexVec};
9use rustc_middle::mir::*;
10use rustc_middle::ty::{Ty, TyCtxt};
11use rustc_span::Span;
12use smallvec::SmallVec;
13
14use crate::un_derefer::UnDerefer;
15
16impl ::std::fmt::Debug for MovePathIndex {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_fmt(format_args!("mp{0}", self.as_u32()))
    }
}rustc_index::newtype_index! {
17    /// Index identifying a `MovePath`.
18    #[orderable]
19    #[debug_format = "mp{}"]
20    pub struct MovePathIndex {}
21}
22
23impl polonius_engine::Atom for MovePathIndex {
24    fn index(self) -> usize {
25        rustc_index::Idx::index(self)
26    }
27}
28
29impl ::std::fmt::Debug for MoveOutIndex {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_fmt(format_args!("mo{0}", self.as_u32()))
    }
}rustc_index::newtype_index! {
30    /// Index identifying a `MoveOut`.
31    #[orderable]
32    #[debug_format = "mo{}"]
33    pub struct MoveOutIndex {}
34}
35
36impl ::std::fmt::Debug for InitIndex {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_fmt(format_args!("in{0}", self.as_u32()))
    }
}rustc_index::newtype_index! {
37    /// Index identifying an `Init`.
38    #[debug_format = "in{}"]
39    pub struct InitIndex {}
40}
41
42impl MoveOutIndex {
43    pub fn move_path_index(self, move_data: &MoveData<'_>) -> MovePathIndex {
44        move_data.move_outs[self].path
45    }
46}
47
48/// `MovePath` is a canonicalized representation of a place that is of
49/// interest to dataflow analysis, as identified by `gather_moves`. This
50/// is primarily places that are moved or inited (assigned). Each
51/// `MovePath` is assigned a `MovePathIndex` by which it can be referred
52/// to.
53///
54/// `MovePath` follows a tree structure.
55///
56/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
57/// move *out* of the place `x.m`.
58///
59/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
60/// one of them will link to the other via the `next_sibling` field,
61/// and the other will have no entry in its `next_sibling` field), and
62/// they both have the MovePath representing `x` as their parent.
63/// (All tree roots are locals). This structure allows easy traversal
64/// between related paths `x` and `x.m` and `x.n`.
65#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for MovePath<'tcx> {
    #[inline]
    fn clone(&self) -> MovePath<'tcx> {
        MovePath {
            next_sibling: ::core::clone::Clone::clone(&self.next_sibling),
            first_child: ::core::clone::Clone::clone(&self.first_child),
            parent: ::core::clone::Clone::clone(&self.parent),
            place: ::core::clone::Clone::clone(&self.place),
        }
    }
}Clone)]
66pub struct MovePath<'tcx> {
67    pub next_sibling: Option<MovePathIndex>,
68    pub first_child: Option<MovePathIndex>,
69    pub parent: Option<MovePathIndex>,
70    pub place: Place<'tcx>,
71}
72
73impl<'tcx> MovePath<'tcx> {
74    /// Returns an iterator over the parents of `self`.
75    pub fn parents<'a>(
76        &self,
77        move_paths: &'a IndexSlice<MovePathIndex, MovePath<'tcx>>,
78    ) -> impl 'a + Iterator<Item = (MovePathIndex, &'a MovePath<'tcx>)> {
79        let first = self.parent.map(|mpi| (mpi, &move_paths[mpi]));
80        MovePathLinearIter {
81            next: first,
82            fetch_next: move |_, parent: &MovePath<'_>| {
83                parent.parent.map(|mpi| (mpi, &move_paths[mpi]))
84            },
85        }
86    }
87
88    /// Returns an iterator over the immediate children of `self`.
89    pub fn children<'a>(
90        &self,
91        move_paths: &'a IndexSlice<MovePathIndex, MovePath<'tcx>>,
92    ) -> impl 'a + Iterator<Item = (MovePathIndex, &'a MovePath<'tcx>)> {
93        let first = self.first_child.map(|mpi| (mpi, &move_paths[mpi]));
94        MovePathLinearIter {
95            next: first,
96            fetch_next: move |_, child: &MovePath<'_>| {
97                child.next_sibling.map(|mpi| (mpi, &move_paths[mpi]))
98            },
99        }
100    }
101
102    /// Finds the closest descendant of `self` for which `f` returns `true` using a breadth-first
103    /// search.
104    ///
105    /// `f` will **not** be called on `self`.
106    pub fn find_descendant(
107        &self,
108        move_paths: &IndexSlice<MovePathIndex, MovePath<'_>>,
109        f: impl Fn(MovePathIndex) -> bool,
110    ) -> Option<MovePathIndex> {
111        let Some(child) = self.first_child else { return None };
112        let mut todo = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [child]))vec![child];
113
114        while let Some(mpi) = todo.pop() {
115            if f(mpi) {
116                return Some(mpi);
117            }
118
119            let move_path = &move_paths[mpi];
120            if let Some(child) = move_path.first_child {
121                todo.push(child);
122            }
123
124            // After we've processed the original `mpi`, we should always
125            // traverse the siblings of any of its children.
126            if let Some(sibling) = move_path.next_sibling {
127                todo.push(sibling);
128            }
129        }
130
131        None
132    }
133}
134
135impl<'tcx> fmt::Debug for MovePath<'tcx> {
136    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
137        w.write_fmt(format_args!("MovePath {{"))write!(w, "MovePath {{")?;
138        if let Some(parent) = self.parent {
139            w.write_fmt(format_args!(" parent: {0:?},", parent))write!(w, " parent: {parent:?},")?;
140        }
141        if let Some(first_child) = self.first_child {
142            w.write_fmt(format_args!(" first_child: {0:?},", first_child))write!(w, " first_child: {first_child:?},")?;
143        }
144        if let Some(next_sibling) = self.next_sibling {
145            w.write_fmt(format_args!(" next_sibling: {0:?}", next_sibling))write!(w, " next_sibling: {next_sibling:?}")?;
146        }
147        w.write_fmt(format_args!(" place: {0:?} }}", self.place))write!(w, " place: {:?} }}", self.place)
148    }
149}
150
151impl<'tcx> fmt::Display for MovePath<'tcx> {
152    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
153        w.write_fmt(format_args!("{0:?}", self.place))write!(w, "{:?}", self.place)
154    }
155}
156
157struct MovePathLinearIter<'a, 'tcx, F> {
158    next: Option<(MovePathIndex, &'a MovePath<'tcx>)>,
159    fetch_next: F,
160}
161
162impl<'a, 'tcx, F> Iterator for MovePathLinearIter<'a, 'tcx, F>
163where
164    F: FnMut(MovePathIndex, &'a MovePath<'tcx>) -> Option<(MovePathIndex, &'a MovePath<'tcx>)>,
165{
166    type Item = (MovePathIndex, &'a MovePath<'tcx>);
167
168    fn next(&mut self) -> Option<Self::Item> {
169        let ret = self.next.take()?;
170        self.next = (self.fetch_next)(ret.0, ret.1);
171        Some(ret)
172    }
173}
174
175#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for MoveData<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["move_paths", "move_outs", "move_out_loc_map",
                        "move_out_path_map", "rev_lookup", "inits", "init_loc_map",
                        "init_path_map"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.move_paths, &self.move_outs, &self.move_out_loc_map,
                        &self.move_out_path_map, &self.rev_lookup, &self.inits,
                        &self.init_loc_map, &&self.init_path_map];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "MoveData",
            names, values)
    }
}Debug)]
176pub struct MoveData<'tcx> {
177    /// All the gathered `MovePath`s.
178    pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
179
180    /// All the `MoveOut`s.
181    pub move_outs: IndexVec<MoveOutIndex, MoveOut>,
182    /// Map from locations to `MoveOut`s. `SmallVec` because each location might cause more than
183    /// one `MoveOut`. Used during analysis and diagnostics.
184    pub move_out_loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
185    /// Map from `MovePath`s (places) to `MoveOuts`. `SmallVec` because each `MovePath` may be
186    /// moved-out of more than once. Used mostly for diagnostics.
187    pub move_out_path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
188
189    /// Map from places/locals to `MovePath`s.
190    pub rev_lookup: MovePathLookup<'tcx>,
191
192    /// All the `Init`s.
193    pub inits: IndexVec<InitIndex, Init>,
194    /// Map from locations to `Init`s. `SmallVec` because each location might cause more than one
195    /// `Init`, though more than one is very rare (e.g. inline asm).
196    pub init_loc_map: LocationMap<SmallVec<[InitIndex; 1]>>,
197    /// Map from `MovePath`s (places) to `Init`s. `SmallVec` because each `MovePath` (place) might
198    /// be inited more than once.
199    pub init_path_map: IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>,
200}
201
202pub trait HasMoveData<'tcx> {
203    fn move_data(&self) -> &MoveData<'tcx>;
204}
205
206#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for LocationMap<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "LocationMap",
            "data", &self.data, "block_starts", &&self.block_starts)
    }
}Debug)]
207pub struct LocationMap<T> {
208    /// All per-location entries live in the single flat `data` vector.
209    /// `block_starts[bb]` gives the index in `data` where block `bb`'s entries
210    /// start; each block has one entry per statement plus one for its terminator.
211    data: Vec<T>,
212    block_starts: IndexVec<BasicBlock, usize>,
213}
214
215impl<T> LocationMap<T> {
216    #[inline]
217    fn offset(&self, loc: Location) -> usize {
218        let offset = self.block_starts[loc.block] + loc.statement_index;
219        if truecfg!(debug_assertions) {
220            // A block's entries run until the next block's start, or the end of
221            // `data` for the last block.
222            let next = loc.block.as_usize() + 1;
223            let block_end = self.block_starts.raw.get(next).copied().unwrap_or(self.data.len());
224            if !(offset < block_end) {
    {
        ::core::panicking::panic_fmt(format_args!("{0:?} is out of range for its block",
                loc));
    }
};assert!(offset < block_end, "{loc:?} is out of range for its block");
225        }
226        offset
227    }
228}
229
230impl<T> Index<Location> for LocationMap<T> {
231    type Output = T;
232    fn index(&self, index: Location) -> &Self::Output {
233        &self.data[self.offset(index)]
234    }
235}
236
237impl<T> IndexMut<Location> for LocationMap<T> {
238    fn index_mut(&mut self, index: Location) -> &mut Self::Output {
239        let offset = self.offset(index);
240        &mut self.data[offset]
241    }
242}
243
244impl<T> LocationMap<T>
245where
246    T: Default + Clone,
247{
248    fn new(body: &Body<'_>) -> Self {
249        let mut block_starts = IndexVec::with_capacity(body.basic_blocks.len());
250        let mut total = 0;
251        for block in body.basic_blocks.iter() {
252            block_starts.push(total);
253            total += block.statements.len() + 1;
254        }
255        LocationMap { data: ::alloc::vec::from_elem(T::default(), total)vec![T::default(); total], block_starts }
256    }
257}
258
259/// `MoveOut` represents a point in a program that moves out of some
260/// L-value; i.e., "creates" uninitialized memory. The dual of `Init`.
261///
262/// With respect to dataflow analysis:
263/// - Generated by moves and declaration of uninitialized variables.
264/// - Killed by assignments to the memory.
265#[derive(#[automatically_derived]
impl ::core::marker::Copy for MoveOut { }Copy, #[automatically_derived]
impl ::core::clone::Clone for MoveOut {
    #[inline]
    fn clone(&self) -> MoveOut {
        let _: ::core::clone::AssertParamIsClone<MovePathIndex>;
        let _: ::core::clone::AssertParamIsClone<Location>;
        *self
    }
}Clone)]
266pub struct MoveOut {
267    /// path being moved
268    pub path: MovePathIndex,
269    /// location of move
270    pub source: Location,
271}
272
273impl fmt::Debug for MoveOut {
274    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
275        fmt.write_fmt(format_args!("{0:?}@{1:?}", self.path, self.source))write!(fmt, "{:?}@{:?}", self.path, self.source)
276    }
277}
278
279/// `Init` represents a point in a program that initializes some L-value. The dual of `MoveOut`.
280#[derive(#[automatically_derived]
impl ::core::marker::Copy for Init { }Copy, #[automatically_derived]
impl ::core::clone::Clone for Init {
    #[inline]
    fn clone(&self) -> Init {
        let _: ::core::clone::AssertParamIsClone<MovePathIndex>;
        let _: ::core::clone::AssertParamIsClone<InitLocation>;
        let _: ::core::clone::AssertParamIsClone<InitKind>;
        *self
    }
}Clone)]
281pub struct Init {
282    /// path being initialized
283    pub path: MovePathIndex,
284    /// location of initialization
285    pub location: InitLocation,
286    /// Extra information about this initialization
287    pub kind: InitKind,
288}
289
290/// Initializations can be from an argument or from a statement. Arguments
291/// do not have locations, in those cases the `Local` is kept.
292#[derive(#[automatically_derived]
impl ::core::marker::Copy for InitLocation { }Copy, #[automatically_derived]
impl ::core::clone::Clone for InitLocation {
    #[inline]
    fn clone(&self) -> InitLocation {
        let _: ::core::clone::AssertParamIsClone<Local>;
        let _: ::core::clone::AssertParamIsClone<Location>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for InitLocation {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            InitLocation::Argument(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Argument", &__self_0),
            InitLocation::Statement(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Statement", &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for InitLocation {
    #[inline]
    fn eq(&self, other: &InitLocation) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (InitLocation::Argument(__self_0),
                    InitLocation::Argument(__arg1_0)) => __self_0 == __arg1_0,
                (InitLocation::Statement(__self_0),
                    InitLocation::Statement(__arg1_0)) => __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for InitLocation {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Local>;
        let _: ::core::cmp::AssertParamIsEq<Location>;
    }
}Eq)]
293pub enum InitLocation {
294    Argument(Local),
295    Statement(Location),
296}
297
298/// Additional information about the initialization.
299#[derive(#[automatically_derived]
impl ::core::marker::Copy for InitKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for InitKind {
    #[inline]
    fn clone(&self) -> InitKind { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for InitKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                InitKind::Deep => "Deep",
                InitKind::Shallow => "Shallow",
                InitKind::NonPanicPathOnly => "NonPanicPathOnly",
            })
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for InitKind {
    #[inline]
    fn eq(&self, other: &InitKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for InitKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
300pub enum InitKind {
301    /// Deep init, even on panic
302    Deep,
303    /// Only does a shallow init
304    Shallow,
305    /// This doesn't initialize the variable on panic (and a panic is possible).
306    NonPanicPathOnly,
307}
308
309impl fmt::Debug for Init {
310    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
311        fmt.write_fmt(format_args!("{0:?}@{1:?} ({2:?})", self.path, self.location,
        self.kind))write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind)
312    }
313}
314
315impl Init {
316    pub fn span<'tcx>(&self, body: &Body<'tcx>) -> Span {
317        match self.location {
318            InitLocation::Argument(local) => body.local_decls[local].source_info.span,
319            InitLocation::Statement(location) => body.source_info(location).span,
320        }
321    }
322}
323
324/// Tables mapping from a place to its `MovePathIndex`.
325#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for MovePathLookup<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "MovePathLookup", "locals", &self.locals, "projections",
            &self.projections, "un_derefer", &&self.un_derefer)
    }
}Debug)]
326pub struct MovePathLookup<'tcx> {
327    locals: IndexVec<Local, Option<MovePathIndex>>,
328
329    /// projections are made from a base-place and a projection
330    /// elem. The base-place will have a unique MovePathIndex; we use
331    /// the latter as the index into the outer vector (narrowing
332    /// subsequent search so that it is solely relative to that
333    /// base-place). For the remaining lookup, we map the projection
334    /// elem to the associated MovePathIndex.
335    projections: FxHashMap<(MovePathIndex, MoveSubPath), MovePathIndex>,
336
337    un_derefer: UnDerefer<'tcx>,
338}
339
340mod builder;
341
342#[derive(#[automatically_derived]
impl ::core::marker::Copy for LookupResult { }Copy, #[automatically_derived]
impl ::core::clone::Clone for LookupResult {
    #[inline]
    fn clone(&self) -> LookupResult {
        let _: ::core::clone::AssertParamIsClone<MovePathIndex>;
        let _: ::core::clone::AssertParamIsClone<Option<MovePathIndex>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for LookupResult {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            LookupResult::Exact(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Exact",
                    &__self_0),
            LookupResult::Parent(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Parent",
                    &__self_0),
        }
    }
}Debug)]
343pub enum LookupResult {
344    /// This exact thing has a move path. E.g. we looked up `x` or `x.m` and it has been moved.
345    Exact(MovePathIndex),
346
347    /// - If the field is `None`, neither the exact thing nor any ancestor of it has a move path.
348    ///   E.g. we looked up `x.m` and neither it nor `x` have a move path.
349    /// - If the field is `Some`, the exact thing has no move path, but an ancestor does. E.g. we
350    ///   looked up `x.m` which has no move path but `x` has one. Not possible for locals.
351    Parent(Option<MovePathIndex>),
352}
353
354impl<'tcx> MovePathLookup<'tcx> {
355    // Unlike the builder `fn move_path_for` below, this lookup
356    // alternative will *not* create a MovePath on the fly for an
357    // unknown place, but will rather return the nearest available
358    // parent.
359    pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult {
360        // Look first in the locals (roots).
361        let Some(mut result) = self.find_local(place.local) else {
362            return LookupResult::Parent(None);
363        };
364
365        // Look for a projection through the found local.
366        for (_, elem) in self.un_derefer.iter_projections(place) {
367            let subpath = match MoveSubPath::of(elem.kind()) {
368                MoveSubPathResult::One(kind) => self.projections.get(&(result, kind)),
369                MoveSubPathResult::Subslice { .. } => None, // just use the parent MovePath
370                MoveSubPathResult::Skip => continue,
371                MoveSubPathResult::Stop => None,
372            };
373
374            let Some(&subpath) = subpath else {
375                return LookupResult::Parent(Some(result));
376            };
377            result = subpath;
378        }
379
380        LookupResult::Exact(result)
381    }
382
383    /// For locals, which are roots.
384    #[inline]
385    pub fn find_local(&self, local: Local) -> Option<MovePathIndex> {
386        self.locals[local]
387    }
388
389    /// An enumerated iterator of `local`s and their associated
390    /// `MovePathIndex`es.
391    pub fn iter_locals_enumerated(
392        &self,
393    ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> {
394        self.locals.iter_enumerated().filter_map(|(l, &idx)| Some((l, idx?)))
395    }
396}
397
398impl<'tcx> MoveData<'tcx> {
399    pub fn gather_moves(
400        body: &Body<'tcx>,
401        tcx: TyCtxt<'tcx>,
402        filter: impl Fn(Ty<'tcx>) -> bool,
403    ) -> MoveData<'tcx> {
404        builder::gather_moves(body, tcx, filter)
405    }
406
407    /// For the move path `mpi`, returns the root local variable that starts the path.
408    /// (e.g., for a path like `a.b.c` returns `a`)
409    pub fn base_local(&self, mut mpi: MovePathIndex) -> Local {
410        loop {
411            let path = &self.move_paths[mpi];
412            if let Some(l) = path.place.as_local() {
413                return l;
414            }
415            mpi = path.parent.expect("root move paths should be locals");
416        }
417    }
418
419    pub fn find_in_move_path_or_its_descendants(
420        &self,
421        root: MovePathIndex,
422        pred: impl Fn(MovePathIndex) -> bool,
423    ) -> Option<MovePathIndex> {
424        if pred(root) {
425            return Some(root);
426        }
427
428        self.move_paths[root].find_descendant(&self.move_paths, pred)
429    }
430}
431
432/// A projection into a move path producing a child path
433#[derive(#[automatically_derived]
impl ::core::marker::Copy for MoveSubPath { }Copy, #[automatically_derived]
impl ::core::clone::Clone for MoveSubPath {
    #[inline]
    fn clone(&self) -> MoveSubPath {
        let _: ::core::clone::AssertParamIsClone<FieldIdx>;
        let _: ::core::clone::AssertParamIsClone<u64>;
        let _: ::core::clone::AssertParamIsClone<VariantIdx>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for MoveSubPath {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            MoveSubPath::Deref =>
                ::core::fmt::Formatter::write_str(f, "Deref"),
            MoveSubPath::Field(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Field",
                    &__self_0),
            MoveSubPath::ConstantIndex(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ConstantIndex", &__self_0),
            MoveSubPath::Downcast(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Downcast", &__self_0),
            MoveSubPath::UnwrapUnsafeBinder =>
                ::core::fmt::Formatter::write_str(f, "UnwrapUnsafeBinder"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for MoveSubPath {
    #[inline]
    fn eq(&self, other: &MoveSubPath) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (MoveSubPath::Field(__self_0), MoveSubPath::Field(__arg1_0))
                    => __self_0 == __arg1_0,
                (MoveSubPath::ConstantIndex(__self_0),
                    MoveSubPath::ConstantIndex(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (MoveSubPath::Downcast(__self_0),
                    MoveSubPath::Downcast(__arg1_0)) => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for MoveSubPath {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<FieldIdx>;
        let _: ::core::cmp::AssertParamIsEq<u64>;
        let _: ::core::cmp::AssertParamIsEq<VariantIdx>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for MoveSubPath {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state);
        match self {
            MoveSubPath::Field(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            MoveSubPath::ConstantIndex(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            MoveSubPath::Downcast(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            _ => {}
        }
    }
}Hash)]
434enum MoveSubPath {
435    Deref,
436    Field(FieldIdx),
437    ConstantIndex(u64),
438    Downcast(VariantIdx),
439    UnwrapUnsafeBinder,
440}
441
442#[derive(#[automatically_derived]
impl ::core::marker::Copy for MoveSubPathResult { }Copy, #[automatically_derived]
impl ::core::clone::Clone for MoveSubPathResult {
    #[inline]
    fn clone(&self) -> MoveSubPathResult {
        let _: ::core::clone::AssertParamIsClone<MoveSubPath>;
        let _: ::core::clone::AssertParamIsClone<u64>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for MoveSubPathResult {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            MoveSubPathResult::One(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "One",
                    &__self_0),
            MoveSubPathResult::Subslice { from: __self_0, to: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Subslice", "from", __self_0, "to", &__self_1),
            MoveSubPathResult::Skip =>
                ::core::fmt::Formatter::write_str(f, "Skip"),
            MoveSubPathResult::Stop =>
                ::core::fmt::Formatter::write_str(f, "Stop"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for MoveSubPathResult {
    #[inline]
    fn eq(&self, other: &MoveSubPathResult) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (MoveSubPathResult::One(__self_0),
                    MoveSubPathResult::One(__arg1_0)) => __self_0 == __arg1_0,
                (MoveSubPathResult::Subslice { from: __self_0, to: __self_1 },
                    MoveSubPathResult::Subslice { from: __arg1_0, to: __arg1_1
                    }) => __self_0 == __arg1_0 && __self_1 == __arg1_1,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for MoveSubPathResult {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<MoveSubPath>;
        let _: ::core::cmp::AssertParamIsEq<u64>;
    }
}Eq)]
443enum MoveSubPathResult {
444    One(MoveSubPath),
445    Subslice { from: u64, to: u64 },
446    Skip,
447    Stop,
448}
449
450impl MoveSubPath {
451    fn of(elem: ProjectionKind) -> MoveSubPathResult {
452        let subpath = match elem {
453            // correspond to a MoveSubPath
454            ProjectionKind::Deref => MoveSubPath::Deref,
455            ProjectionKind::Field(idx, _) => MoveSubPath::Field(idx),
456            ProjectionKind::ConstantIndex { offset, min_length: _, from_end: false } => {
457                MoveSubPath::ConstantIndex(offset)
458            }
459            ProjectionKind::Downcast(_, idx) => MoveSubPath::Downcast(idx),
460            ProjectionKind::UnwrapUnsafeBinder(_) => MoveSubPath::UnwrapUnsafeBinder,
461
462            // this should be the same move path as its parent
463            // its fine to skip because it cannot have sibling move paths
464            // and it is not a user visible path
465            ProjectionKind::OpaqueCast(_) => {
466                return MoveSubPathResult::Skip;
467            }
468
469            // these cannot be moved through
470            ProjectionKind::Index(_)
471            | ProjectionKind::ConstantIndex { offset: _, min_length: _, from_end: true }
472            | ProjectionKind::Subslice { from: _, to: _, from_end: true } => {
473                return MoveSubPathResult::Stop;
474            }
475
476            // subslice is special.
477            // it needs to be split into individual move paths
478            ProjectionKind::Subslice { from, to, from_end: false } => {
479                return MoveSubPathResult::Subslice { from, to };
480            }
481        };
482
483        MoveSubPathResult::One(subpath)
484    }
485}