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}