rustc_borrowck/
prefixes.rs

1//! From the NLL RFC:
2//! "Shallow prefixes are found by stripping away fields, but stop at
3//! any dereference. So: writing a path like `a` is illegal if `a.b`
4//! is borrowed. But: writing `a` is legal if `*a` is borrowed,
5//! whether or not `a` is a shared or mutable reference. [...] "
6
7use rustc_middle::mir::{PlaceRef, ProjectionElem};
8
9use super::MirBorrowckCtxt;
10
11pub(crate) trait IsPrefixOf<'tcx> {
12    fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool;
13}
14
15impl<'tcx> IsPrefixOf<'tcx> for PlaceRef<'tcx> {
16    fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool {
17        self.local == other.local
18            && self.projection.len() <= other.projection.len()
19            && self.projection == &other.projection[..self.projection.len()]
20    }
21}
22
23pub(super) struct Prefixes<'tcx> {
24    kind: PrefixSet,
25    next: Option<PlaceRef<'tcx>>,
26}
27
28#[derive(Copy, Clone, PartialEq, Eq, Debug)]
29pub(super) enum PrefixSet {
30    /// Doesn't stop until it returns the base case (a Local or
31    /// Static prefix).
32    All,
33    /// Stops at any dereference.
34    Shallow,
35}
36
37impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
38    /// Returns an iterator over the prefixes of `place`
39    /// (inclusive) from longest to smallest, potentially
40    /// terminating the iteration early based on `kind`.
41    pub(super) fn prefixes(&self, place_ref: PlaceRef<'tcx>, kind: PrefixSet) -> Prefixes<'tcx> {
42        Prefixes { next: Some(place_ref), kind }
43    }
44}
45
46impl<'tcx> Iterator for Prefixes<'tcx> {
47    type Item = PlaceRef<'tcx>;
48    fn next(&mut self) -> Option<Self::Item> {
49        let mut cursor = self.next?;
50
51        // Post-processing `place`: Enqueue any remaining
52        // work. Also, `place` may not be a prefix itself, but
53        // may hold one further down (e.g., we never return
54        // downcasts here, but may return a base of a downcast).
55
56        loop {
57            match cursor.last_projection() {
58                None => {
59                    self.next = None;
60                    return Some(cursor);
61                }
62                Some((cursor_base, elem)) => {
63                    match elem {
64                        ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
65                            // FIXME: add union handling
66                            self.next = Some(cursor_base);
67                            return Some(cursor);
68                        }
69                        ProjectionElem::UnwrapUnsafeBinder(_) => {
70                            self.next = Some(cursor_base);
71                            return Some(cursor);
72                        }
73                        ProjectionElem::Downcast(..)
74                        | ProjectionElem::Subslice { .. }
75                        | ProjectionElem::OpaqueCast { .. }
76                        | ProjectionElem::ConstantIndex { .. }
77                        | ProjectionElem::Index(_) => {
78                            cursor = cursor_base;
79                        }
80                        ProjectionElem::Subtype(..) => {
81                            panic!("Subtype projection is not allowed before borrow check")
82                        }
83                        ProjectionElem::Deref => {
84                            match self.kind {
85                                PrefixSet::Shallow => {
86                                    // Shallow prefixes are found by stripping away
87                                    // fields, but stop at *any* dereference.
88                                    // So we can just stop the traversal now.
89                                    self.next = None;
90                                    return Some(cursor);
91                                }
92                                PrefixSet::All => {
93                                    // All prefixes: just blindly enqueue the base
94                                    // of the projection.
95                                    self.next = Some(cursor_base);
96                                    return Some(cursor);
97                                }
98                            }
99                        }
100                    }
101                }
102            }
103        }
104    }
105}