|
| 1 | +//! In this file we handle the "Tree" part of Tree Borrows, i.e. all tree |
| 2 | +//! traversal functions, optimizations to trim branches, and keeping track of |
| 3 | +//! the relative position of the access to each node being updated. This of course |
| 4 | +//! also includes the definition of the tree structure. |
| 5 | +//! |
| 6 | +//! Functions here manipulate permissions but are oblivious to them: as |
| 7 | +//! the internals of `Permission` are private, the update process is a black |
| 8 | +//! box. All we need to know here are |
| 9 | +//! - the fact that updates depend only on the old state, the status of protectors, |
| 10 | +//! and the relative position of the access; |
| 11 | +//! - idempotency properties asserted in `perms.rs` (for optimizations) |
| 12 | +
|
| 13 | +use smallvec::SmallVec; |
| 14 | + |
| 15 | +use rustc_const_eval::interpret::InterpResult; |
| 16 | +use rustc_data_structures::fx::FxHashSet; |
| 17 | +use rustc_target::abi::Size; |
| 18 | + |
| 19 | +use crate::borrow_tracker::tree_borrows::{ |
| 20 | + diagnostics::{NodeDebugInfo, TbError, TransitionError}, |
| 21 | + unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap}, |
| 22 | + Permission, |
| 23 | +}; |
| 24 | +use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind}; |
| 25 | +use crate::*; |
| 26 | + |
| 27 | +/// Data for a single *location*. |
| 28 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 29 | +pub(super) struct LocationState { |
| 30 | + /// This pointer's current permission |
| 31 | + permission: Permission, |
| 32 | + /// A location is initialized when it is child accessed for the first time, |
| 33 | + /// and it then stays initialized forever. |
| 34 | + /// Before initialization we still apply some preemptive transitions on |
| 35 | + /// `permission` to know what to do in case it ever gets initialized, |
| 36 | + /// but these can never cause any immediate UB. There can however be UB |
| 37 | + /// the moment we attempt to initalize (i.e. child-access) because some |
| 38 | + /// foreign access done between the creation and the initialization is |
| 39 | + /// incompatible with child accesses. |
| 40 | + initialized: bool, |
| 41 | + /// Strongest foreign access whose effects have already been applied to |
| 42 | + /// this node and all its children since the last child access. |
| 43 | + /// This is `None` if the most recent access is a child access, |
| 44 | + /// `Some(Write)` if at least one foreign write access has been applied |
| 45 | + /// since the previous child access, and `Some(Read)` if at least one |
| 46 | + /// foreign read and no foreign write have occurred since the last child access. |
| 47 | + latest_foreign_access: Option<AccessKind>, |
| 48 | +} |
| 49 | + |
| 50 | +impl LocationState { |
| 51 | + /// Default initial state has never been accessed and has been subjected to no |
| 52 | + /// foreign access. |
| 53 | + fn new(permission: Permission) -> Self { |
| 54 | + Self { permission, initialized: false, latest_foreign_access: None } |
| 55 | + } |
| 56 | + |
| 57 | + /// Record that this location was accessed through a child pointer by |
| 58 | + /// marking it as initialized |
| 59 | + fn with_access(mut self) -> Self { |
| 60 | + self.initialized = true; |
| 61 | + self |
| 62 | + } |
| 63 | + |
| 64 | + pub fn is_initialized(&self) -> bool { |
| 65 | + self.initialized |
| 66 | + } |
| 67 | + |
| 68 | + pub fn permission(&self) -> Permission { |
| 69 | + self.permission |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +/// Tree structure with both parents and children since we want to be |
| 74 | +/// able to traverse the tree efficiently in both directions. |
| 75 | +#[derive(Clone, Debug)] |
| 76 | +pub struct Tree { |
| 77 | + /// Mapping from tags to keys. The key obtained can then be used in |
| 78 | + /// any of the `UniValMap` relative to this allocation, i.e. both the |
| 79 | + /// `nodes` and `rperms` of the same `Tree`. |
| 80 | + /// The parent-child relationship in `Node` is encoded in terms of these same |
| 81 | + /// keys, so traversing the entire tree needs exactly one access to |
| 82 | + /// `tag_mapping`. |
| 83 | + pub(super) tag_mapping: UniKeyMap<BorTag>, |
| 84 | + /// All nodes of this tree. |
| 85 | + pub(super) nodes: UniValMap<Node>, |
| 86 | + /// Maps a tag and a location to a perm, with possible lazy |
| 87 | + /// initialization. |
| 88 | + /// |
| 89 | + /// NOTE: not all tags registered in `nodes` are necessarily in all |
| 90 | + /// ranges of `rperms`, because `rperms` is in part lazily initialized. |
| 91 | + /// Just because `nodes.get(key)` is `Some(_)` does not mean you can safely |
| 92 | + /// `unwrap` any `perm.get(key)`. |
| 93 | + /// |
| 94 | + /// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)` |
| 95 | + pub(super) rperms: RangeMap<UniValMap<LocationState>>, |
| 96 | + /// The index of the root node. |
| 97 | + pub(super) root: UniIndex, |
| 98 | +} |
| 99 | + |
| 100 | +/// A node in the borrow tree. Each node is uniquely identified by a tag via |
| 101 | +/// the `nodes` map of `Tree`. |
| 102 | +#[derive(Clone, Debug)] |
| 103 | +pub(super) struct Node { |
| 104 | + /// The tag of this node. |
| 105 | + pub tag: BorTag, |
| 106 | + /// All tags except the root have a parent tag. |
| 107 | + pub parent: Option<UniIndex>, |
| 108 | + /// If the pointer was reborrowed, it has children. |
| 109 | + // FIXME: bench to compare this to FxHashSet and to other SmallVec sizes |
| 110 | + pub children: SmallVec<[UniIndex; 4]>, |
| 111 | + /// Either `Reserved` or `Frozen`, the permission this tag will be lazily initialized |
| 112 | + /// to on the first access. |
| 113 | + default_initial_perm: Permission, |
| 114 | + /// Some extra information useful only for debugging purposes |
| 115 | + pub debug_info: NodeDebugInfo, |
| 116 | +} |
| 117 | + |
| 118 | +impl Tree { |
| 119 | + /// Create a new tree, with only a root pointer. |
| 120 | + pub fn new(root_tag: BorTag, size: Size) -> Self { |
| 121 | + let root_perm = Permission::new_root(); |
| 122 | + let mut tag_mapping = UniKeyMap::default(); |
| 123 | + let root_idx = tag_mapping.insert(root_tag); |
| 124 | + let nodes = { |
| 125 | + let mut nodes = UniValMap::<Node>::default(); |
| 126 | + nodes.insert( |
| 127 | + root_idx, |
| 128 | + Node { |
| 129 | + tag: root_tag, |
| 130 | + parent: None, |
| 131 | + children: SmallVec::default(), |
| 132 | + default_initial_perm: root_perm, |
| 133 | + debug_info: NodeDebugInfo::new(root_tag), |
| 134 | + }, |
| 135 | + ); |
| 136 | + nodes |
| 137 | + }; |
| 138 | + let rperms = { |
| 139 | + let mut perms = UniValMap::default(); |
| 140 | + perms.insert(root_idx, LocationState::new(root_perm).with_access()); |
| 141 | + RangeMap::new(size, perms) |
| 142 | + }; |
| 143 | + Self { root: root_idx, nodes, rperms, tag_mapping } |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +impl<'tcx> Tree { |
| 148 | + /// Insert a new tag in the tree |
| 149 | + pub fn new_child( |
| 150 | + &mut self, |
| 151 | + parent_tag: BorTag, |
| 152 | + new_tag: BorTag, |
| 153 | + default_initial_perm: Permission, |
| 154 | + range: AllocRange, |
| 155 | + ) -> InterpResult<'tcx> { |
| 156 | + assert!(!self.tag_mapping.contains_key(&new_tag)); |
| 157 | + let idx = self.tag_mapping.insert(new_tag); |
| 158 | + let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); |
| 159 | + // Create the node |
| 160 | + self.nodes.insert( |
| 161 | + idx, |
| 162 | + Node { |
| 163 | + tag: new_tag, |
| 164 | + parent: Some(parent_idx), |
| 165 | + children: SmallVec::default(), |
| 166 | + default_initial_perm, |
| 167 | + debug_info: NodeDebugInfo::new(new_tag), |
| 168 | + }, |
| 169 | + ); |
| 170 | + // Register new_tag as a child of parent_tag |
| 171 | + self.nodes.get_mut(parent_idx).unwrap().children.push(idx); |
| 172 | + // Initialize perms |
| 173 | + let perm = LocationState::new(default_initial_perm).with_access(); |
| 174 | + for (_range, perms) in self.rperms.iter_mut(range.start, range.size) { |
| 175 | + perms.insert(idx, perm); |
| 176 | + } |
| 177 | + Ok(()) |
| 178 | + } |
| 179 | + |
| 180 | +/// Relative position of the access |
| 181 | +#[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 182 | +pub enum AccessRelatedness { |
| 183 | + /// The accessed pointer is the current one |
| 184 | + This, |
| 185 | + /// The accessed pointer is a (transitive) child of the current one. |
| 186 | + // Current pointer is excluded (unlike in some other places of this module |
| 187 | + // where "child" is inclusive). |
| 188 | + StrictChildAccess, |
| 189 | + /// The accessed pointer is a (transitive) parent of the current one. |
| 190 | + // Current pointer is excluded. |
| 191 | + AncestorAccess, |
| 192 | + /// The accessed pointer is neither of the above. |
| 193 | + // It's a cousin/uncle/etc., something in a side branch. |
| 194 | + // FIXME: find a better name ? |
| 195 | + DistantAccess, |
| 196 | +} |
| 197 | + |
| 198 | +impl AccessRelatedness { |
| 199 | + /// Check that access is either Ancestor or Distant, i.e. not |
| 200 | + /// a transitive child (initial pointer included). |
| 201 | + pub fn is_foreign(self) -> bool { |
| 202 | + matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::DistantAccess) |
| 203 | + } |
| 204 | + |
| 205 | + /// Given the AccessRelatedness for the parent node, compute the AccessRelatedness |
| 206 | + /// for the child node. This function assumes that we propagate away from the initial |
| 207 | + /// access. |
| 208 | + pub fn for_child(self) -> Self { |
| 209 | + use AccessRelatedness::*; |
| 210 | + match self { |
| 211 | + AncestorAccess | This => AncestorAccess, |
| 212 | + StrictChildAccess | DistantAccess => DistantAccess, |
| 213 | + } |
| 214 | + } |
| 215 | +} |
0 commit comments