Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 1 | //! From the NLL RFC: "The deep [aka 'supporting'] prefixes for an |
| 2 | //! place are formed by stripping away fields and derefs, except that |
| 3 | //! we stop when we reach the deref of a shared reference. [...] " |
| 4 | //! |
| 5 | //! "Shallow prefixes are found by stripping away fields, but stop at |
| 6 | //! any dereference. So: writing a path like `a` is illegal if `a.b` |
| 7 | //! is borrowed. But: writing `a` is legal if `*a` is borrowed, |
| 8 | //! whether or not `a` is a shared or mutable reference. [...] " |
| 9 | |
| 10 | use super::MirBorrowckCtxt; |
| 11 | |
| 12 | use rustc_hir as hir; |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 13 | use rustc_middle::mir::{Body, PlaceRef, ProjectionElem}; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 14 | use rustc_middle::ty::{self, TyCtxt}; |
| 15 | |
| 16 | pub trait IsPrefixOf<'tcx> { |
| 17 | fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool; |
| 18 | } |
| 19 | |
| 20 | impl<'tcx> IsPrefixOf<'tcx> for PlaceRef<'tcx> { |
| 21 | fn is_prefix_of(&self, other: PlaceRef<'tcx>) -> bool { |
| 22 | self.local == other.local |
| 23 | && self.projection.len() <= other.projection.len() |
| 24 | && self.projection == &other.projection[..self.projection.len()] |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | pub(super) struct Prefixes<'cx, 'tcx> { |
| 29 | body: &'cx Body<'tcx>, |
| 30 | tcx: TyCtxt<'tcx>, |
| 31 | kind: PrefixSet, |
| 32 | next: Option<PlaceRef<'tcx>>, |
| 33 | } |
| 34 | |
| 35 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| 36 | pub(super) enum PrefixSet { |
| 37 | /// Doesn't stop until it returns the base case (a Local or |
| 38 | /// Static prefix). |
| 39 | All, |
| 40 | /// Stops at any dereference. |
| 41 | Shallow, |
| 42 | /// Stops at the deref of a shared reference. |
| 43 | Supporting, |
| 44 | } |
| 45 | |
| 46 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
| 47 | /// Returns an iterator over the prefixes of `place` |
| 48 | /// (inclusive) from longest to smallest, potentially |
| 49 | /// terminating the iteration early based on `kind`. |
| 50 | pub(super) fn prefixes( |
| 51 | &self, |
| 52 | place_ref: PlaceRef<'tcx>, |
| 53 | kind: PrefixSet, |
| 54 | ) -> Prefixes<'cx, 'tcx> { |
| 55 | Prefixes { next: Some(place_ref), kind, body: self.body, tcx: self.infcx.tcx } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { |
| 60 | type Item = PlaceRef<'tcx>; |
| 61 | fn next(&mut self) -> Option<Self::Item> { |
| 62 | let mut cursor = self.next?; |
| 63 | |
| 64 | // Post-processing `place`: Enqueue any remaining |
| 65 | // work. Also, `place` may not be a prefix itself, but |
| 66 | // may hold one further down (e.g., we never return |
| 67 | // downcasts here, but may return a base of a downcast). |
| 68 | |
| 69 | 'cursor: loop { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 70 | match cursor.last_projection() { |
| 71 | None => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 72 | self.next = None; |
| 73 | return Some(cursor); |
| 74 | } |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 75 | Some((cursor_base, elem)) => { |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 76 | match elem { |
| 77 | ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { |
| 78 | // FIXME: add union handling |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 79 | self.next = Some(cursor_base); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 80 | return Some(cursor); |
| 81 | } |
| 82 | ProjectionElem::Downcast(..) |
| 83 | | ProjectionElem::Subslice { .. } |
Charisee | 89a0a0c | 2023-01-24 17:33:30 +0000 | [diff] [blame^] | 84 | | ProjectionElem::OpaqueCast { .. } |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 85 | | ProjectionElem::ConstantIndex { .. } |
| 86 | | ProjectionElem::Index(_) => { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 87 | cursor = cursor_base; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 88 | continue 'cursor; |
| 89 | } |
| 90 | ProjectionElem::Deref => { |
| 91 | // (handled below) |
| 92 | } |
| 93 | } |
| 94 | |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 95 | assert_eq!(elem, ProjectionElem::Deref); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 96 | |
| 97 | match self.kind { |
| 98 | PrefixSet::Shallow => { |
| 99 | // Shallow prefixes are found by stripping away |
| 100 | // fields, but stop at *any* dereference. |
| 101 | // So we can just stop the traversal now. |
| 102 | self.next = None; |
| 103 | return Some(cursor); |
| 104 | } |
| 105 | PrefixSet::All => { |
| 106 | // All prefixes: just blindly enqueue the base |
| 107 | // of the projection. |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 108 | self.next = Some(cursor_base); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 109 | return Some(cursor); |
| 110 | } |
| 111 | PrefixSet::Supporting => { |
| 112 | // Fall through! |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | assert_eq!(self.kind, PrefixSet::Supporting); |
| 117 | // Supporting prefixes: strip away fields and |
| 118 | // derefs, except we stop at the deref of a shared |
| 119 | // reference. |
| 120 | |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 121 | let ty = cursor_base.ty(self.body, self.tcx).ty; |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 122 | match ty.kind() { |
| 123 | ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { |
| 124 | // don't continue traversing over derefs of raw pointers or shared |
| 125 | // borrows. |
| 126 | self.next = None; |
| 127 | return Some(cursor); |
| 128 | } |
| 129 | |
| 130 | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Mut) => { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 131 | self.next = Some(cursor_base); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 132 | return Some(cursor); |
| 133 | } |
| 134 | |
| 135 | ty::Adt(..) if ty.is_box() => { |
Jeff Vander Stoep | 59fbe18 | 2021-03-29 10:17:52 +0200 | [diff] [blame] | 136 | self.next = Some(cursor_base); |
Thiébaud Weksteen | 3b664ca | 2020-11-26 14:41:59 +0100 | [diff] [blame] | 137 | return Some(cursor); |
| 138 | } |
| 139 | |
| 140 | _ => panic!("unknown type fed to Projection Deref."), |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | } |