|  | //! From the NLL RFC: | 
|  | //! "Shallow prefixes are found by stripping away fields, but stop at | 
|  | //! any dereference. So: writing a path like `a` is illegal if `a.b` | 
|  | //! is borrowed. But: writing `a` is legal if `*a` is borrowed, | 
|  | //! whether or not `a` is a shared or mutable reference. [...] " | 
|  |  | 
|  | use rustc_middle::mir::{PlaceRef, ProjectionElem}; | 
|  |  | 
|  | use super::MirBorrowckCtxt; | 
|  |  | 
|  | pub(crate) trait IsPrefixOf<'tcx> { | 
|  | fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool; | 
|  | } | 
|  |  | 
|  | impl<'tcx> IsPrefixOf<'tcx> for PlaceRef<'tcx> { | 
|  | fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool { | 
|  | self.local == other.local | 
|  | && self.projection.len() <= other.projection.len() | 
|  | && self.projection == &other.projection[..self.projection.len()] | 
|  | } | 
|  | } | 
|  |  | 
|  | pub(super) struct Prefixes<'tcx> { | 
|  | kind: PrefixSet, | 
|  | next: Option<PlaceRef<'tcx>>, | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 
|  | pub(super) enum PrefixSet { | 
|  | /// Doesn't stop until it returns the base case (a Local or | 
|  | /// Static prefix). | 
|  | All, | 
|  | /// Stops at any dereference. | 
|  | Shallow, | 
|  | } | 
|  |  | 
|  | impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { | 
|  | /// Returns an iterator over the prefixes of `place` | 
|  | /// (inclusive) from longest to smallest, potentially | 
|  | /// terminating the iteration early based on `kind`. | 
|  | pub(super) fn prefixes(&self, place_ref: PlaceRef<'tcx>, kind: PrefixSet) -> Prefixes<'tcx> { | 
|  | Prefixes { next: Some(place_ref), kind } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'tcx> Iterator for Prefixes<'tcx> { | 
|  | type Item = PlaceRef<'tcx>; | 
|  | fn next(&mut self) -> Option<Self::Item> { | 
|  | let mut cursor = self.next?; | 
|  |  | 
|  | // Post-processing `place`: Enqueue any remaining | 
|  | // work. Also, `place` may not be a prefix itself, but | 
|  | // may hold one further down (e.g., we never return | 
|  | // downcasts here, but may return a base of a downcast). | 
|  |  | 
|  | loop { | 
|  | match cursor.last_projection() { | 
|  | None => { | 
|  | self.next = None; | 
|  | return Some(cursor); | 
|  | } | 
|  | Some((cursor_base, elem)) => { | 
|  | match elem { | 
|  | ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { | 
|  | // FIXME: add union handling | 
|  | self.next = Some(cursor_base); | 
|  | return Some(cursor); | 
|  | } | 
|  | ProjectionElem::UnwrapUnsafeBinder(_) => { | 
|  | self.next = Some(cursor_base); | 
|  | return Some(cursor); | 
|  | } | 
|  | ProjectionElem::Downcast(..) | 
|  | | ProjectionElem::Subslice { .. } | 
|  | | ProjectionElem::OpaqueCast { .. } | 
|  | | ProjectionElem::ConstantIndex { .. } | 
|  | | ProjectionElem::Index(_) => { | 
|  | cursor = cursor_base; | 
|  | } | 
|  | ProjectionElem::Subtype(..) => { | 
|  | panic!("Subtype projection is not allowed before borrow check") | 
|  | } | 
|  | ProjectionElem::Deref => { | 
|  | match self.kind { | 
|  | PrefixSet::Shallow => { | 
|  | // Shallow prefixes are found by stripping away | 
|  | // fields, but stop at *any* dereference. | 
|  | // So we can just stop the traversal now. | 
|  | self.next = None; | 
|  | return Some(cursor); | 
|  | } | 
|  | PrefixSet::All => { | 
|  | // All prefixes: just blindly enqueue the base | 
|  | // of the projection. | 
|  | self.next = Some(cursor_base); | 
|  | return Some(cursor); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } |