Skip to main content

miri/borrow_tracker/tree_borrows/
tree.rs

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
13use std::ops::Range;
14use std::{cmp, fmt, mem};
15
16use rustc_abi::Size;
17use rustc_data_structures::fx::FxHashSet;
18use rustc_span::Span;
19use smallvec::SmallVec;
20
21use super::diagnostics::{
22    AccessCause, DiagnosticInfo, NodeDebugInfo, TbError, TransitionError,
23    no_valid_exposed_references_error,
24};
25use super::foreign_access_skipping::IdempotentForeignAccess;
26use super::perms::{PermTransition, Permission};
27use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor};
28use super::unimap::{UniIndex, UniKeyMap, UniValMap};
29use super::wildcard::ExposedCache;
30use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};
31use crate::*;
32
33mod tests;
34
35/// Data for a reference at single *location*.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub(super) struct LocationState {
38    /// A location is "accessed" when it is child-accessed for the first time (and the initial
39    /// retag initializes the location for the range covered by the type), and it then stays
40    /// accessed forever.
41    /// For accessed locations, "permission" is the current permission. However, for
42    /// non-accessed locations, we still need to track the "future initial permission": this will
43    /// start out to be `default_initial_perm`, but foreign accesses need to be taken into account.
44    /// Crucially however, while transitions to `Disabled` would usually be UB if this location is
45    /// protected, that is *not* the case for non-accessed locations. Instead we just have a latent
46    /// "future initial permission" of `Disabled`, causing UB only if an access is ever actually
47    /// performed.
48    /// Note that the tree root is also always accessed, as if the allocation was a write access.
49    accessed: bool,
50    /// This pointer's current permission / future initial permission.
51    permission: Permission,
52    /// See `foreign_access_skipping.rs`.
53    /// Stores an idempotent foreign access for this location and its children.
54    /// For correctness, this must not be too strong, and the recorded idempotent foreign access
55    /// of all children must be at least as strong as this. For performance, it should be as strong as possible.
56    idempotent_foreign_access: IdempotentForeignAccess,
57}
58
59impl LocationState {
60    /// Constructs a new initial state. It has neither been accessed, nor been subjected
61    /// to any foreign access yet.
62    /// The permission is not allowed to be `Unique`.
63    /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
64    pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
65        assert!(permission.is_initial() || permission.is_disabled());
66        assert!(!permission.is_unique());
67        Self { permission, accessed: false, idempotent_foreign_access: sifa }
68    }
69
70    /// Constructs a new initial state. It has not yet been subjected
71    /// to any foreign access. However, it is already marked as having been accessed.
72    /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
73    pub fn new_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
74        Self { permission, accessed: true, idempotent_foreign_access: sifa }
75    }
76
77    /// Checks whether the current location state is ever reachable in a real execution.
78    pub fn possible(&self) -> bool {
79        // `Unique` can only be reached on actually accessed locations.
80        self.accessed || !self.permission.is_unique()
81    }
82
83    /// Check if the location has been accessed, i.e. if it has
84    /// ever been accessed through a child pointer.
85    pub fn accessed(&self) -> bool {
86        self.accessed
87    }
88
89    pub fn permission(&self) -> Permission {
90        self.permission
91    }
92
93    /// Performs an access on this index and updates node,
94    /// perm and wildcard_state to reflect the transition.
95    fn perform_transition(
96        &mut self,
97        idx: UniIndex,
98        nodes: &mut UniValMap<Node>,
99        exposed_cache: &mut ExposedCache,
100        access_kind: AccessKind,
101        relatedness: AccessRelatedness,
102        protected: bool,
103        diagnostics: &DiagnosticInfo,
104    ) -> Result<(), TransitionError> {
105        // Call this function now (i.e. only if we know `relatedness`), which
106        // ensures it is only called when `skip_if_known_noop` returns
107        // `Recurse`, due to the contract of `traverse_this_parents_children_other`.
108        self.record_new_access(access_kind, relatedness);
109        let old_access_level = self.permission.strongest_allowed_local_access(protected);
110        let transition = self.perform_access(access_kind, relatedness, protected)?;
111        if !transition.is_noop() {
112            let node = nodes.get_mut(idx).unwrap();
113            // Record the event as part of the history.
114            node.debug_info
115                .history
116                .push(diagnostics.create_event(transition, relatedness.is_foreign()));
117
118            // We need to update the wildcard state, if the permission
119            // of an exposed pointer changes.
120            if node.is_exposed {
121                let access_level = self.permission.strongest_allowed_local_access(protected);
122                exposed_cache.update_exposure(nodes, idx, old_access_level, access_level);
123            }
124        }
125        Ok(())
126    }
127
128    /// Apply the effect of an access to one location, including
129    /// - applying `Permission::perform_access` to the inner `Permission`,
130    /// - emitting protector UB if the location is accessed,
131    /// - updating the accessed status (child accesses produce accessed locations).
132    fn perform_access(
133        &mut self,
134        access_kind: AccessKind,
135        rel_pos: AccessRelatedness,
136        protected: bool,
137    ) -> Result<PermTransition, TransitionError> {
138        let old_perm = self.permission;
139        let transition = Permission::perform_access(access_kind, rel_pos, old_perm, protected)
140            .ok_or(TransitionError::ChildAccessForbidden(old_perm))?;
141        self.accessed |= !rel_pos.is_foreign();
142        self.permission = transition.applied(old_perm).unwrap();
143        // Why do only accessed locations cause protector errors?
144        // Consider two mutable references `x`, `y` into disjoint parts of
145        // the same allocation. A priori, these may actually both be used to
146        // access the entire allocation, as long as only reads occur. However,
147        // a write to `y` needs to somehow record that `x` can no longer be used
148        // on that location at all. For these non-accessed locations (i.e., locations
149        // that haven't been accessed with `x` yet), we track the "future initial state":
150        // it defaults to whatever the initial state of the tag is,
151        // but the access to `y` moves that "future initial state" of `x` to `Disabled`.
152        // However, usually a `Reserved -> Disabled` transition would be UB due to the protector!
153        // So clearly protectors shouldn't fire for such "future initial state" transitions.
154        //
155        // See the test `two_mut_protected_same_alloc` in `tests/pass/tree_borrows/tree-borrows.rs`
156        // for an example of safe code that would be UB if we forgot to check `self.accessed`.
157        if protected && self.accessed && transition.produces_disabled() {
158            return Err(TransitionError::ProtectedDisabled(old_perm));
159        }
160        debug_assert!(self.possible());
161        Ok(transition)
162    }
163
164    /// Like `perform_access`, but ignores the concrete error cause and also uses state-passing
165    /// rather than a mutable reference. As such, it returns `Some(x)` if the transition succeeded,
166    /// or `None` if there was an error.
167    #[cfg(test)]
168    fn perform_access_no_fluff(
169        mut self,
170        access_kind: AccessKind,
171        rel_pos: AccessRelatedness,
172        protected: bool,
173    ) -> Option<Self> {
174        match self.perform_access(access_kind, rel_pos, protected) {
175            Ok(_) => Some(self),
176            Err(_) => None,
177        }
178    }
179
180    /// Tree traversal optimizations. See `foreign_access_skipping.rs`.
181    /// This checks if such a foreign access can be skipped.
182    fn skip_if_known_noop(
183        &self,
184        access_kind: AccessKind,
185        rel_pos: AccessRelatedness,
186    ) -> ContinueTraversal {
187        if rel_pos.is_foreign() {
188            let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
189            let mut new_access_noop =
190                self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
191            if self.permission.is_disabled() {
192                // A foreign access to a `Disabled` tag will have almost no observable effect.
193                // It's a theorem that `Disabled` node have no protected accessed children,
194                // and so this foreign access will never trigger any protector.
195                // (Intuition: You're either protected accessed, and thus can't become Disabled
196                // or you're already Disabled protected, but not accessed, and then can't
197                // become accessed since that requires a child access, which Disabled blocks.)
198                // Further, the children will never be able to read or write again, since they
199                // have a `Disabled` parent. So this only affects diagnostics, such that the
200                // blocking write will still be identified directly, just at a different tag.
201                new_access_noop = true;
202            }
203            if self.permission.is_frozen() && access_kind == AccessKind::Read {
204                // A foreign read to a `Frozen` tag will have almost no observable effect.
205                // It's a theorem that `Frozen` nodes have no `Unique` children, so all children
206                // already survive foreign reads. Foreign reads in general have almost no
207                // effect, the only further thing they could do is make protected `Reserved`
208                // nodes become conflicted, i.e. make them reject child writes for the further
209                // duration of their protector. But such a child write is already rejected
210                // because this node is frozen. So this only affects diagnostics, but the
211                // blocking read will still be identified directly, just at a different tag.
212                new_access_noop = true;
213            }
214            if new_access_noop {
215                // Abort traversal if the new access is indeed guaranteed
216                // to be noop.
217                // No need to update `self.idempotent_foreign_access`,
218                // the type of the current streak among nonempty read-only
219                // or nonempty with at least one write has not changed.
220                ContinueTraversal::SkipSelfAndChildren
221            } else {
222                // Otherwise propagate this time, and also record the
223                // access that just occurred so that we can skip the propagation
224                // next time.
225                ContinueTraversal::Recurse
226            }
227        } else {
228            // A child access occurred, this breaks the streak of foreign
229            // accesses in a row and the sequence since the previous child access
230            // is now empty.
231            ContinueTraversal::Recurse
232        }
233    }
234
235    /// Records a new access, so that future access can potentially be skipped
236    /// by `skip_if_known_noop`. This must be called on child accesses, and otherwise
237    /// should be called on foreign accesses for increased performance. It should not be called
238    /// when `skip_if_known_noop` indicated skipping, since it then is a no-op.
239    /// See `foreign_access_skipping.rs`
240    fn record_new_access(&mut self, access_kind: AccessKind, rel_pos: AccessRelatedness) {
241        debug_assert!(matches!(
242            self.skip_if_known_noop(access_kind, rel_pos),
243            ContinueTraversal::Recurse
244        ));
245        self.idempotent_foreign_access
246            .record_new(IdempotentForeignAccess::from_acc_and_rel(access_kind, rel_pos));
247    }
248}
249
250impl fmt::Display for LocationState {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        write!(f, "{}", self.permission)?;
253        if !self.accessed {
254            write!(f, "?")?;
255        }
256        Ok(())
257    }
258}
259/// The state of the full tree for a particular location: for all nodes, the local permissions
260/// of that node, and the tracking for wildcard accesses.
261#[derive(Clone, Debug, PartialEq, Eq)]
262pub struct LocationTree {
263    /// Maps a tag to a perm, with possible lazy initialization.
264    ///
265    /// NOTE: not all tags registered in `Tree::nodes` are necessarily in all
266    /// ranges of `perms`, because `perms` is in part lazily initialized.
267    /// Just because `nodes.get(key)` is `Some(_)` does not mean you can safely
268    /// `unwrap` any `perm.get(key)`.
269    ///
270    /// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
271    pub perms: UniValMap<LocationState>,
272    /// Caches information about the relatedness of nodes for a wildcard access.
273    pub exposed_cache: ExposedCache,
274}
275/// Tree structure with both parents and children since we want to be
276/// able to traverse the tree efficiently in both directions.
277#[derive(Clone, Debug)]
278pub struct Tree {
279    /// Mapping from tags to keys. The key obtained can then be used in
280    /// any of the `UniValMap` relative to this allocation, i.e.
281    /// `nodes`, `LocationTree::perms` and `LocationTree::exposed_cache`
282    /// of the same `Tree`.
283    /// The parent-child relationship in `Node` is encoded in terms of these same
284    /// keys, so traversing the entire tree needs exactly one access to
285    /// `tag_mapping`.
286    pub(super) tag_mapping: UniKeyMap<BorTag>,
287    /// All nodes of this tree.
288    pub(super) nodes: UniValMap<Node>,
289    /// Associates with each location its state and wildcard access tracking.
290    pub(super) locations: DedupRangeMap<LocationTree>,
291    /// Contains both the root of the main tree as well as the roots of the wildcard subtrees.
292    ///
293    /// If we reborrow a reference which has wildcard provenance, then we do not know where in
294    /// the tree to attach them. Instead we create a new additional tree for this allocation
295    /// with this new reference as a root. We call this additional tree a wildcard subtree.
296    ///
297    /// The actual structure should be a single tree but with wildcard provenance we approximate
298    /// this with this ordered set of trees. Each wildcard subtree is the direct child of *some* exposed
299    /// tag (that is smaller than the root), but we do not know which. This also means that it can only be the
300    /// child of a tree that comes before it in the vec ensuring we don't have any cycles in our
301    /// approximated tree.
302    ///
303    /// Sorted according to `BorTag` from low to high. This also means the main root is `root[0]`.
304    ///
305    /// Has array size 2 because that still ensures the minimum size for SmallVec.
306    pub(super) roots: SmallVec<[UniIndex; 2]>,
307}
308
309/// A node in the borrow tree. Each node is uniquely identified by a tag via
310/// the `nodes` map of `Tree`.
311#[derive(Clone, Debug)]
312pub(super) struct Node {
313    /// The tag of this node.
314    pub tag: BorTag,
315    /// All tags except the root have a parent tag.
316    pub parent: Option<UniIndex>,
317    /// If the pointer was reborrowed, it has children.
318    // FIXME: bench to compare this to FxHashSet and to other SmallVec sizes
319    pub children: SmallVec<[UniIndex; 4]>,
320    /// Either `Reserved`,  `Frozen`, or `Disabled`, it is the permission this tag will
321    /// lazily be initialized to on the first access.
322    /// It is only ever `Disabled` for a tree root, since the root is initialized to `Unique` by
323    /// its own separate mechanism.
324    default_initial_perm: Permission,
325    /// The default initial (strongest) idempotent foreign access.
326    /// This participates in the invariant for `LocationState::idempotent_foreign_access`
327    /// in cases where there is no location state yet. See `foreign_access_skipping.rs`,
328    /// and `LocationState::idempotent_foreign_access` for more information
329    default_initial_idempotent_foreign_access: IdempotentForeignAccess,
330    /// Whether a wildcard access could happen through this node.
331    pub is_exposed: bool,
332    /// Some extra information useful only for debugging purposes.
333    pub debug_info: NodeDebugInfo,
334}
335
336impl Tree {
337    /// Create a new tree, with only a root pointer.
338    pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
339        // The root has `Disabled` as the default permission,
340        // so that any access out of bounds is invalid.
341        let root_default_perm = Permission::new_disabled();
342        let mut tag_mapping = UniKeyMap::default();
343        let root_idx = tag_mapping.insert(root_tag);
344        let nodes = {
345            let mut nodes = UniValMap::<Node>::default();
346            let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span);
347            // name the root so that all allocations contain one named pointer
348            debug_info.add_name("root of the allocation");
349            nodes.insert(
350                root_idx,
351                Node {
352                    tag: root_tag,
353                    parent: None,
354                    children: SmallVec::default(),
355                    default_initial_perm: root_default_perm,
356                    // The root may never be skipped, all accesses will be local.
357                    default_initial_idempotent_foreign_access: IdempotentForeignAccess::None,
358                    is_exposed: false,
359                    debug_info,
360                },
361            );
362            nodes
363        };
364        let locations = {
365            let mut perms = UniValMap::default();
366            // We manually set it to `Unique` on all in-bounds positions.
367            // We also ensure that it is accessed, so that no `Unique` but
368            // not yet accessed nodes exist. Essentially, we pretend there
369            // was a write that initialized these to `Unique`.
370            perms.insert(
371                root_idx,
372                LocationState::new_accessed(
373                    Permission::new_unique(),
374                    IdempotentForeignAccess::None,
375                ),
376            );
377            let exposed_cache = ExposedCache::default();
378            DedupRangeMap::new(size, LocationTree { perms, exposed_cache })
379        };
380        Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping }
381    }
382}
383
384impl<'tcx> Tree {
385    /// Insert a new tag in the tree.
386    ///
387    /// `inside_perm` defines the initial permissions for a block of memory starting at
388    /// `base_offset`. These may nor may not be already marked as "accessed".
389    /// `outside_perm` defines the initial permission for the rest of the allocation.
390    /// These are definitely not "accessed".
391    pub(super) fn new_child(
392        &mut self,
393        base_offset: Size,
394        parent_prov: ProvenanceExtra,
395        new_tag: BorTag,
396        inside_perms: DedupRangeMap<LocationState>,
397        outside_perm: Permission,
398        protected: bool,
399        span: Span,
400    ) -> InterpResult<'tcx> {
401        let idx = self.tag_mapping.insert(new_tag);
402        let parent_idx = match parent_prov {
403            ProvenanceExtra::Concrete(parent_tag) =>
404                Some(self.tag_mapping.get(&parent_tag).unwrap()),
405            ProvenanceExtra::Wildcard => None,
406        };
407        assert!(outside_perm.is_initial());
408        assert!(!outside_perm.is_unique());
409
410        let default_strongest_idempotent =
411            outside_perm.strongest_idempotent_foreign_access(protected);
412        // Create the node
413        self.nodes.insert(
414            idx,
415            Node {
416                tag: new_tag,
417                parent: parent_idx,
418                children: SmallVec::default(),
419                default_initial_perm: outside_perm,
420                default_initial_idempotent_foreign_access: default_strongest_idempotent,
421                is_exposed: false,
422                debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
423            },
424        );
425        if let Some(parent_idx) = parent_idx {
426            let parent_node = self.nodes.get_mut(parent_idx).unwrap();
427            // Register new_tag as a child of parent_tag
428            parent_node.children.push(idx);
429        } else {
430            // If the parent had wildcard provenance, then register the idx
431            // as a new wildcard root.
432            // This preserves the orderedness of `roots` because a newly created
433            // tag is greater than all previous tags.
434            self.roots.push(idx);
435        }
436
437        // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.
438        let mut min_sifa = default_strongest_idempotent;
439        for (Range { start, end }, &perm) in
440            inside_perms.iter(Size::from_bytes(0), inside_perms.size())
441        {
442            assert!(perm.permission.is_initial());
443            assert_eq!(
444                perm.idempotent_foreign_access,
445                perm.permission.strongest_idempotent_foreign_access(protected)
446            );
447
448            min_sifa = cmp::min(min_sifa, perm.idempotent_foreign_access);
449            for (_range, loc) in self
450                .locations
451                .iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start))
452            {
453                loc.perms.insert(idx, perm);
454            }
455        }
456
457        // We don't have to update `exposed_cache` as the new node is not exposed and
458        // has no children so the default counts of 0 are correct.
459
460        // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated.
461        if let Some(parent_idx) = parent_idx {
462            // Inserting the new perms might have broken the SIFA invariant (see
463            // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.
464            // We now weaken the recorded SIFA for our parents, until the invariant is restored. We
465            // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new
466            // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs
467            // more fixup than `Write`).
468            self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);
469        }
470
471        interp_ok(())
472    }
473
474    /// Restores the SIFA "children are stronger"/"parents are weaker" invariant after a retag:
475    /// reduce the SIFA of `current` and its parents to be no stronger than `strongest_allowed`.
476    /// See `foreign_access_skipping.rs` and [`Tree::new_child`].
477    fn update_idempotent_foreign_access_after_retag(
478        &mut self,
479        mut current: UniIndex,
480        strongest_allowed: IdempotentForeignAccess,
481    ) {
482        if strongest_allowed == IdempotentForeignAccess::Write {
483            // Nothing is stronger than `Write`.
484            return;
485        }
486        // We walk the tree upwards, until the invariant is restored
487        loop {
488            let current_node = self.nodes.get_mut(current).unwrap();
489            // Call `ensure_no_stronger_than` on all SIFAs for this node: the per-location SIFA, as well
490            // as the default SIFA for not-yet-initialized locations.
491            // Record whether we did any change; if not, the invariant is restored and we can stop the traversal.
492            let mut any_change = false;
493            for (_range, loc) in self.locations.iter_mut_all() {
494                // Check if this node has a state for this location (or range of locations).
495                if let Some(perm) = loc.perms.get_mut(current) {
496                    // Update the per-location SIFA, recording if it changed.
497                    any_change |=
498                        perm.idempotent_foreign_access.ensure_no_stronger_than(strongest_allowed);
499                }
500            }
501            // Now update `default_initial_idempotent_foreign_access`, which stores the default SIFA for not-yet-initialized locations.
502            any_change |= current_node
503                .default_initial_idempotent_foreign_access
504                .ensure_no_stronger_than(strongest_allowed);
505
506            if any_change {
507                let Some(next) = self.nodes.get(current).unwrap().parent else {
508                    // We have arrived at the root.
509                    break;
510                };
511                current = next;
512                continue;
513            } else {
514                break;
515            }
516        }
517    }
518
519    /// Deallocation requires
520    /// - a pointer that permits write accesses
521    /// - the absence of Strong Protectors anywhere in the allocation
522    pub fn dealloc(
523        &mut self,
524        prov: ProvenanceExtra,
525        access_range: AllocRange,
526        global: &GlobalState,
527        alloc_id: AllocId, // diagnostics
528        span: Span,        // diagnostics
529    ) -> InterpResult<'tcx> {
530        self.perform_access(
531            prov,
532            access_range,
533            AccessKind::Write,
534            AccessCause::Dealloc,
535            global,
536            alloc_id,
537            span,
538        )?;
539
540        let start_idx = match prov {
541            ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
542            ProvenanceExtra::Wildcard => None,
543        };
544
545        // Check if this breaks any strong protector.
546        // (Weak protectors are already handled by `perform_access`.)
547        for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
548            let diagnostics = DiagnosticInfo {
549                alloc_id,
550                span,
551                transition_range: loc_range,
552                access_range: Some(access_range),
553                access_cause: AccessCause::Dealloc,
554            };
555            // Checks the tree containing `idx` for strong protector violations.
556            // It does this in traversal order.
557            let mut check_tree = |idx| {
558                TreeVisitor { nodes: &mut self.nodes, data: loc }
559                    .traverse_this_parents_children_other(
560                        idx,
561                        // Visit all children, skipping none.
562                        |_| ContinueTraversal::Recurse,
563                        |args: NodeAppArgs<'_, _>| {
564                            let node = args.nodes.get(args.idx).unwrap();
565
566                            let perm = args
567                                .data
568                                .perms
569                                .get(args.idx)
570                                .copied()
571                                .unwrap_or_else(|| node.default_location_state());
572                            if global.borrow().protected_tags.get(&node.tag)
573                                == Some(&ProtectorKind::StrongProtector)
574                                // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
575                                // Related to https://github.com/rust-lang/rust/issues/55005.
576                                && !perm.permission.is_cell()
577                                // Only trigger UB if the accessed bit is set, i.e. if the protector
578                                // is actually protecting this offset. See #4579. Note that this
579                                // takes into account the access we just did above!
580                                && perm.accessed
581                            {
582                                Err(TbError {
583                                    error_kind: TransitionError::ProtectedDealloc,
584                                    access_info: &diagnostics,
585                                    conflicting_node_info: &node.debug_info,
586                                    accessed_node_info: start_idx
587                                        .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
588                                }
589                                .build())
590                            } else {
591                                Ok(())
592                            }
593                        },
594                    )
595            };
596            // If we have a start index we first check its subtree in traversal order.
597            // This results in us showing the error of the closest node instead of an
598            // arbitrary one.
599            let accessed_root = start_idx.map(&mut check_tree).transpose()?;
600            // Afterwards we check all other trees.
601            // We iterate over the list in reverse order to ensure that we do not visit
602            // a parent before its child.
603            for &root in self.roots.iter().rev() {
604                if Some(root) == accessed_root {
605                    continue;
606                }
607                check_tree(root)?;
608            }
609        }
610        interp_ok(())
611    }
612
613    /// Map the per-node and per-location `LocationState::perform_access`
614    /// to each location of the first component of `access_range_and_kind`,
615    /// on every tag of the allocation.
616    ///
617    /// `LocationState::perform_access` will take care of raising transition
618    /// errors and updating the `accessed` status of each location,
619    /// this traversal adds to that:
620    /// - inserting into the map locations that do not exist yet,
621    /// - trimming the traversal,
622    /// - recording the history.
623    pub fn perform_access(
624        &mut self,
625        prov: ProvenanceExtra,
626        access_range: AllocRange,
627        access_kind: AccessKind,
628        access_cause: AccessCause, // diagnostics
629        global: &GlobalState,
630        alloc_id: AllocId, // diagnostics
631        span: Span,        // diagnostics
632    ) -> InterpResult<'tcx> {
633        #[cfg(feature = "expensive-consistency-checks")]
634        if self.roots.len() > 1 || matches!(prov, ProvenanceExtra::Wildcard) {
635            self.verify_wildcard_consistency(global);
636        }
637
638        let source_idx = match prov {
639            ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
640            ProvenanceExtra::Wildcard => None,
641        };
642        // We iterate over affected locations and traverse the tree for each of them.
643        for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
644            let diagnostics = DiagnosticInfo {
645                access_cause,
646                access_range: Some(access_range),
647                alloc_id,
648                span,
649                transition_range: loc_range,
650            };
651            loc.perform_access(
652                self.roots.iter().copied(),
653                &mut self.nodes,
654                source_idx,
655                access_kind,
656                global,
657                ChildrenVisitMode::VisitChildrenOfAccessed,
658                &diagnostics,
659                /* min_exposed_child */ None, // only matters for protector end access,
660            )?;
661        }
662        interp_ok(())
663    }
664    /// This is the special access that is applied on protector release:
665    /// - the access will be applied only to accessed locations of the allocation,
666    /// - it will not be visible to children,
667    /// - it will be recorded as a `FnExit` diagnostic access
668    /// - and it will be a read except if the location is `Unique`, i.e. has been written to,
669    ///   in which case it will be a write.
670    /// - otherwise identical to `Tree::perform_access`
671    pub fn perform_protector_end_access(
672        &mut self,
673        tag: BorTag,
674        global: &GlobalState,
675        alloc_id: AllocId, // diagnostics
676        span: Span,        // diagnostics
677    ) -> InterpResult<'tcx> {
678        #[cfg(feature = "expensive-consistency-checks")]
679        if self.roots.len() > 1 {
680            self.verify_wildcard_consistency(global);
681        }
682
683        let source_idx = self.tag_mapping.get(&tag).unwrap();
684
685        let min_exposed_child = if self.roots.len() > 1 {
686            LocationTree::get_min_exposed_child(source_idx, &self.nodes)
687        } else {
688            // There's no point in computing this when there is just one tree.
689            None
690        };
691
692        // This is a special access through the entire allocation.
693        // It actually only affects `accessed` locations, so we need
694        // to filter on those before initiating the traversal.
695        //
696        // In addition this implicit access should not be visible to children,
697        // thus the use of `traverse_nonchildren`.
698        // See the test case `returned_mut_is_usable` from
699        // `tests/pass/tree_borrows/tree-borrows.rs` for an example of
700        // why this is important.
701        for (loc_range, loc) in self.locations.iter_mut_all() {
702            // Only visit accessed permissions
703            if let Some(p) = loc.perms.get(source_idx)
704                && let Some(access_kind) = p.permission.associated_access()
705                && p.accessed
706            {
707                let diagnostics = DiagnosticInfo {
708                    access_cause: AccessCause::FnExit(access_kind),
709                    access_range: None,
710                    alloc_id,
711                    span,
712                    transition_range: loc_range,
713                };
714                loc.perform_access(
715                    self.roots.iter().copied(),
716                    &mut self.nodes,
717                    Some(source_idx),
718                    access_kind,
719                    global,
720                    ChildrenVisitMode::SkipChildrenOfAccessed,
721                    &diagnostics,
722                    min_exposed_child,
723                )?;
724            }
725        }
726        interp_ok(())
727    }
728}
729
730/// Integration with the BorTag garbage collector
731impl Tree {
732    pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
733        for i in 0..(self.roots.len()) {
734            self.remove_useless_children(self.roots[i], live_tags);
735        }
736        // Right after the GC runs is a good moment to check if we can
737        // merge some adjacent ranges that were made equal by the removal of some
738        // tags (this does not necessarily mean that they have identical internal representations,
739        // see the `PartialEq` impl for `UniValMap`)
740        self.locations.merge_adjacent_thorough();
741    }
742
743    /// Checks if a node is useless and should be GC'ed.
744    /// A node is useless if it has no children and also the tag is no longer live.
745    fn is_useless(&self, idx: UniIndex, live: &FxHashSet<BorTag>) -> bool {
746        let node = self.nodes.get(idx).unwrap();
747        node.children.is_empty() && !live.contains(&node.tag)
748    }
749
750    /// Checks whether a node can be replaced by its only child.
751    /// If so, returns the index of said only child.
752    /// If not, returns none.
753    fn can_be_replaced_by_single_child(
754        &self,
755        idx: UniIndex,
756        live: &FxHashSet<BorTag>,
757    ) -> Option<UniIndex> {
758        let node = self.nodes.get(idx).unwrap();
759
760        let [child_idx] = node.children[..] else { return None };
761
762        // We never want to replace the root node, as it is also kept in `root_ptr_tags`.
763        if live.contains(&node.tag) || node.parent.is_none() {
764            return None;
765        }
766        // Since protected nodes are never GC'd (see `borrow_tracker::FrameExtra::visit_provenance`),
767        // we know that `node` is not protected because otherwise `live` would
768        // have contained `node.tag`.
769        let child = self.nodes.get(child_idx).unwrap();
770        // Check that for that one child, `can_be_replaced_by_child` holds for the permission
771        // on all locations.
772        for (_range, loc) in self.locations.iter_all() {
773            let parent_perm = loc
774                .perms
775                .get(idx)
776                .map(|x| x.permission)
777                .unwrap_or_else(|| node.default_initial_perm);
778            let child_perm = loc
779                .perms
780                .get(child_idx)
781                .map(|x| x.permission)
782                .unwrap_or_else(|| child.default_initial_perm);
783            if !parent_perm.can_be_replaced_by_child(child_perm) {
784                return None;
785            }
786        }
787
788        Some(child_idx)
789    }
790
791    /// Properly removes a node.
792    /// The node to be removed should not otherwise be usable. It also
793    /// should have no children, but this is not checked, so that nodes
794    /// whose children were rotated somewhere else can be deleted without
795    /// having to first modify them to clear that array.
796    fn remove_useless_node(&mut self, this: UniIndex) {
797        // Due to the API of UniMap we must make sure to call
798        // `UniValMap::remove` for the key of this node on *all* maps that used it
799        // (which are `self.nodes` and every range of `self.rperms`)
800        // before we can safely apply `UniKeyMap::remove` to truly remove
801        // this tag from the `tag_mapping`.
802        let node = self.nodes.remove(this).unwrap();
803        for (_range, loc) in self.locations.iter_mut_all() {
804            loc.perms.remove(this);
805            loc.exposed_cache.remove(this);
806        }
807        self.tag_mapping.remove(&node.tag);
808    }
809
810    /// Traverses the entire tree looking for useless tags.
811    /// Removes from the tree all useless child nodes of root.
812    /// It will not delete the root itself.
813    ///
814    /// NOTE: This leaves in the middle of the tree tags that are unreachable but have
815    /// reachable children. There is a potential for compacting the tree by reassigning
816    /// children of dead tags to the nearest live parent, but it must be done with care
817    /// not to remove UB.
818    ///
819    /// Example: Consider the tree `root - parent - child`, with `parent: Frozen` and
820    /// `child: Reserved`. This tree can exist. If we blindly delete `parent` and reassign
821    /// `child` to be a direct child of `root` then Writes to `child` are now permitted
822    /// whereas they were not when `parent` was still there.
823    fn remove_useless_children(&mut self, root: UniIndex, live: &FxHashSet<BorTag>) {
824        // To avoid stack overflows, we roll our own stack.
825        // Each element in the stack consists of the current tag, and the number of the
826        // next child to be processed.
827
828        // The other functions are written using the `TreeVisitorStack`, but that does not work here
829        // since we need to 1) do a post-traversal and 2) remove nodes from the tree.
830        // Since we do a post-traversal (by deleting nodes only after handling all children),
831        // we also need to be a bit smarter than "pop node, push all children."
832        let mut stack = vec![(root, 0)];
833        while let Some((tag, nth_child)) = stack.last_mut() {
834            let node = self.nodes.get(*tag).unwrap();
835            if *nth_child < node.children.len() {
836                // Visit the child by pushing it to the stack.
837                // Also increase `nth_child` so that when we come back to the `tag` node, we
838                // look at the next child.
839                let next_child = node.children[*nth_child];
840                *nth_child += 1;
841                stack.push((next_child, 0));
842                continue;
843            } else {
844                // We have processed all children of `node`, so now it is time to process `node` itself.
845                // First, get the current children of `node`. To appease the borrow checker,
846                // we have to temporarily move the list out of the node, and then put the
847                // list of remaining children back in.
848                let mut children_of_node =
849                    mem::take(&mut self.nodes.get_mut(*tag).unwrap().children);
850                // Remove all useless children.
851                children_of_node.retain_mut(|idx| {
852                    if self.is_useless(*idx, live) {
853                        // Delete `idx` node everywhere else.
854                        self.remove_useless_node(*idx);
855                        // And delete it from children_of_node.
856                        false
857                    } else {
858                        if let Some(nextchild) = self.can_be_replaced_by_single_child(*idx, live) {
859                            // `nextchild` is our grandchild, and will become our direct child.
860                            // Delete the in-between node, `idx`.
861                            self.remove_useless_node(*idx);
862                            // Set the new child's parent.
863                            self.nodes.get_mut(nextchild).unwrap().parent = Some(*tag);
864                            // Save the new child in children_of_node.
865                            *idx = nextchild;
866                        }
867                        // retain it
868                        true
869                    }
870                });
871                // Put back the now-filtered vector.
872                self.nodes.get_mut(*tag).unwrap().children = children_of_node;
873
874                // We are done, the parent can continue.
875                stack.pop();
876                continue;
877            }
878        }
879    }
880}
881
882impl<'tcx> LocationTree {
883    /// Returns the smallest exposed tag, if any, that is a transitive child of `root`.
884    fn get_min_exposed_child(root: UniIndex, nodes: &UniValMap<Node>) -> Option<BorTag> {
885        // We cannot use the wildcard datastructure to improve this lookup. This is because
886        // the datastructure only tracks enabled nodes and we need to also consider disabled ones.
887        let mut stack = vec![root];
888        let mut min_tag = None;
889        while let Some(idx) = stack.pop() {
890            let node = nodes.get(idx).unwrap();
891            if min_tag.is_some_and(|min| min < node.tag) {
892                // The minimum we found before is bigger than this tag, and therefore
893                // also bigger than all its children, so we can skip this subtree.
894                continue;
895            }
896            stack.extend_from_slice(node.children.as_slice());
897            if node.is_exposed {
898                min_tag = match min_tag {
899                    Some(prev) if prev < node.tag => Some(prev),
900                    _ => Some(node.tag),
901                };
902            }
903        }
904        min_tag
905    }
906
907    /// Performs an access on this location.
908    /// * `access_source`: The index, if any, where the access came from.
909    /// * `visit_children`: Whether to skip updating the children of `access_source`.
910    /// * `min_exposed_child`: The tag of the smallest exposed (transitive) child of the accessed node.
911    ///   This is only used with `visit_children == SkipChildrenOfAccessed`, where we need to skip children
912    ///   of the accessed node.
913    fn perform_access(
914        &mut self,
915        roots: impl Iterator<Item = UniIndex>,
916        nodes: &mut UniValMap<Node>,
917        access_source: Option<UniIndex>,
918        access_kind: AccessKind,
919        global: &GlobalState,
920        visit_children: ChildrenVisitMode,
921        diagnostics: &DiagnosticInfo,
922        min_exposed_child: Option<BorTag>,
923    ) -> InterpResult<'tcx> {
924        let accessed_root = if let Some(idx) = access_source {
925            Some(self.perform_normal_access(
926                idx,
927                nodes,
928                access_kind,
929                global,
930                visit_children,
931                diagnostics,
932            )?)
933        } else {
934            // `SkipChildrenOfAccessed` only gets set on protector release, which only
935            // occurs on a known node.
936            assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed));
937            None
938        };
939
940        let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag);
941        for (i, root) in roots.enumerate() {
942            let tag = nodes.get(root).unwrap().tag;
943            // On a protector release access we have to skip the children of the accessed tag.
944            // However, if the tag has exposed children then some of the wildcard subtrees could
945            // also be children of the accessed node and would also need to be skipped. We can
946            // narrow down which wildcard trees might be children by comparing their root tag to the
947            // minimum exposed child of the accessed node. As the parent tag is always smaller
948            // than the child tag this means we only need to skip subtrees with a root tag larger
949            // than `min_exposed_child`. Once we find such a root, we can leave the loop because roots
950            // are sorted by tag.
951            if matches!(visit_children, ChildrenVisitMode::SkipChildrenOfAccessed)
952                && let Some(min_exposed_child) = min_exposed_child
953                && tag > min_exposed_child
954            {
955                break;
956            }
957            // We don't perform a wildcard access on the tree we already performed a
958            // normal access on.
959            if Some(root) == accessed_root {
960                continue;
961            }
962            // The choice of `max_local_tag` requires some thought.
963            // This can only be a local access for nodes that are a parent of the accessed node
964            // and are therefore smaller, so the accessed node itself is a valid choice for `max_local_tag`.
965            // However, using `accessed_root` is better since that will be smaller. It is still a valid choice
966            // because for nodes *in other trees*, if they are a parent of the accessed node then they
967            // are a parent of `accessed_root`.
968            //
969            // As a consequence of this, since the root of the main tree is the smallest tag in the entire
970            // allocation, if the access occurred in the main tree then other subtrees will only see foreign accesses.
971            self.perform_wildcard_access(
972                root,
973                access_source,
974                /*max_local_tag*/ accessed_root_tag,
975                nodes,
976                access_kind,
977                global,
978                diagnostics,
979                /*is_wildcard_tree*/ i != 0,
980            )?;
981        }
982        interp_ok(())
983    }
984
985    /// Performs a normal access on the tree containing `access_source`.
986    ///
987    /// Returns the root index of this tree.
988    /// * `access_source`: The index of the tag being accessed.
989    /// * `visit_children`: Whether to skip the children of `access_source`
990    ///   during the access. Used for protector end access.
991    fn perform_normal_access(
992        &mut self,
993        access_source: UniIndex,
994        nodes: &mut UniValMap<Node>,
995        access_kind: AccessKind,
996        global: &GlobalState,
997        visit_children: ChildrenVisitMode,
998        diagnostics: &DiagnosticInfo,
999    ) -> InterpResult<'tcx, UniIndex> {
1000        // Performs the per-node work:
1001        // - insert the permission if it does not exist
1002        // - perform the access
1003        // - record the transition
1004        // to which some optimizations are added:
1005        // - skip the traversal of the children in some cases
1006        // - do not record noop transitions
1007        //
1008        // `loc_range` is only for diagnostics (it is the range of
1009        // the `RangeMap` on which we are currently working).
1010        let node_skipper = |args: &NodeAppArgs<'_, LocationTree>| -> ContinueTraversal {
1011            let node = args.nodes.get(args.idx).unwrap();
1012            let perm = args.data.perms.get(args.idx);
1013
1014            let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
1015            old_state.skip_if_known_noop(access_kind, args.rel_pos)
1016        };
1017        let node_app = |args: NodeAppArgs<'_, LocationTree>| {
1018            let node = args.nodes.get_mut(args.idx).unwrap();
1019            let mut perm = args.data.perms.entry(args.idx);
1020
1021            let state = perm.or_insert(node.default_location_state());
1022
1023            let protected = global.borrow().protected_tags.contains_key(&node.tag);
1024            state
1025                .perform_transition(
1026                    args.idx,
1027                    args.nodes,
1028                    &mut args.data.exposed_cache,
1029                    access_kind,
1030                    args.rel_pos,
1031                    protected,
1032                    diagnostics,
1033                )
1034                .map_err(|error_kind| {
1035                    TbError {
1036                        error_kind,
1037                        access_info: diagnostics,
1038                        conflicting_node_info: &args.nodes.get(args.idx).unwrap().debug_info,
1039                        accessed_node_info: Some(
1040                            &args.nodes.get(access_source).unwrap().debug_info,
1041                        ),
1042                    }
1043                    .build()
1044                })
1045        };
1046
1047        let visitor = TreeVisitor { nodes, data: self };
1048        match visit_children {
1049            ChildrenVisitMode::VisitChildrenOfAccessed =>
1050                visitor.traverse_this_parents_children_other(access_source, node_skipper, node_app),
1051            ChildrenVisitMode::SkipChildrenOfAccessed =>
1052                visitor.traverse_nonchildren(access_source, node_skipper, node_app),
1053        }
1054        .into()
1055    }
1056
1057    /// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness`
1058    /// for each node from the `WildcardState` datastructure.
1059    /// * `root`: Root of the tree being accessed.
1060    /// * `access_source`: the index of the accessed tag, if any.
1061    ///   This is only used for printing the correct tag on errors.
1062    /// * `max_local_tag`: The access can only be local for nodes whose tag is
1063    ///   at most `max_local_tag`.
1064    fn perform_wildcard_access(
1065        &mut self,
1066        root: UniIndex,
1067        access_source: Option<UniIndex>,
1068        max_local_tag: Option<BorTag>,
1069        nodes: &mut UniValMap<Node>,
1070        access_kind: AccessKind,
1071        global: &GlobalState,
1072        diagnostics: &DiagnosticInfo,
1073        is_wildcard_tree: bool,
1074    ) -> InterpResult<'tcx> {
1075        let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| {
1076            // If the tag is larger than `max_local_tag` then the access can only be foreign.
1077            let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag);
1078            loc.exposed_cache.access_relatedness(
1079                root,
1080                idx,
1081                access_kind,
1082                is_wildcard_tree,
1083                only_foreign,
1084            )
1085        };
1086
1087        // Whether there is an exposed node in this tree that allows this access.
1088        let mut has_valid_exposed = false;
1089
1090        // This does a traversal across the tree updating children before their parents. The
1091        // difference to `perform_normal_access` is that we take the access relatedness from
1092        // the wildcard tracking state of the node instead of from the visitor itself.
1093        //
1094        // Unlike for a normal access, the iteration order is important for improving the
1095        // accuracy of wildcard accesses if `max_local_tag` is `Some`: processing the effects of this
1096        // access further down the tree can cause exposed nodes to lose permissions, thus updating
1097        // the wildcard data structure, which will be taken into account when processing the parent
1098        // nodes. Also see the test `cross_tree_update_older_invalid_exposed2.rs`
1099        // (Doing accesses in the opposite order cannot help with precision but the reasons are complicated;
1100        // see <https://github.com/rust-lang/miri/pull/4707#discussion_r2581661123>.)
1101        //
1102        // Note, however, that this is an approximation: there can be situations where a node is
1103        // marked as having an exposed foreign node, but actually that foreign node cannot be
1104        // the source of the access due to `max_local_tag`. The wildcard tracking cannot know
1105        // about `max_local_tag` so we will incorrectly assume that this might be a foreign access.
1106        TreeVisitor { data: self, nodes }.traverse_children_this(
1107            root,
1108            |args| -> ContinueTraversal {
1109                let node = args.nodes.get(args.idx).unwrap();
1110                let perm = args.data.perms.get(args.idx);
1111
1112                let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
1113                // If we know where, relative to this node, the wildcard access occurs,
1114                // then check if we can skip the entire subtree.
1115                if let Some(relatedness) = get_relatedness(args.idx, node, args.data)
1116                    && let Some(relatedness) = relatedness.to_relatedness()
1117                {
1118                    // We can use the usual SIFA machinery to skip nodes.
1119                    old_state.skip_if_known_noop(access_kind, relatedness)
1120                } else {
1121                    ContinueTraversal::Recurse
1122                }
1123            },
1124            |args| {
1125                let node = args.nodes.get_mut(args.idx).unwrap();
1126
1127                let protected = global.borrow().protected_tags.contains_key(&node.tag);
1128
1129                let Some(wildcard_relatedness) = get_relatedness(args.idx, node, args.data) else {
1130                    // There doesn't exist a valid exposed reference for this access to
1131                    // happen through.
1132                    // This can only happen if `root` is the main root: We set
1133                    // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access
1134                    // is always possible on all nodes in a wildcard subtree.
1135                    return Err(no_valid_exposed_references_error(diagnostics));
1136                };
1137
1138                let mut entry = args.data.perms.entry(args.idx);
1139                let perm = entry.or_insert(node.default_location_state());
1140
1141                // We only count exposed nodes through which an access could happen.
1142                if node.is_exposed
1143                    && perm.permission.strongest_allowed_local_access(protected).allows(access_kind)
1144                    && max_local_tag.is_none_or(|max_local_tag| max_local_tag >= node.tag)
1145                {
1146                    has_valid_exposed = true;
1147                }
1148
1149                let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
1150                    // If the access type is Either, then we do not apply any transition
1151                    // to this node, but we still update each of its children.
1152                    // This is an imprecision! In the future, maybe we can still do some sort
1153                    // of best-effort update here.
1154                    return Ok(());
1155                };
1156
1157                // We know the exact relatedness, so we can actually do precise checks.
1158                perm.perform_transition(
1159                    args.idx,
1160                    args.nodes,
1161                    &mut args.data.exposed_cache,
1162                    access_kind,
1163                    relatedness,
1164                    protected,
1165                    diagnostics,
1166                )
1167                .map_err(|trans| {
1168                    let node = args.nodes.get(args.idx).unwrap();
1169                    TbError {
1170                        error_kind: trans,
1171                        access_info: diagnostics,
1172                        conflicting_node_info: &node.debug_info,
1173                        accessed_node_info: access_source
1174                            .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
1175                    }
1176                    .build()
1177                })
1178            },
1179        )?;
1180        // If there is no exposed node in this tree that allows this access, then the access *must*
1181        // be foreign to the entire subtree. Foreign accesses are only possible on wildcard subtrees
1182        // as there are no ancestors to the main root. So if we do not find a valid exposed node in
1183        // the main tree then this access is UB.
1184        if !has_valid_exposed && !is_wildcard_tree {
1185            return Err(no_valid_exposed_references_error(diagnostics)).into();
1186        }
1187        interp_ok(())
1188    }
1189}
1190
1191impl Node {
1192    pub fn default_location_state(&self) -> LocationState {
1193        LocationState::new_non_accessed(
1194            self.default_initial_perm,
1195            self.default_initial_idempotent_foreign_access,
1196        )
1197    }
1198}
1199
1200impl VisitProvenance for Tree {
1201    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1202        // To ensure that the roots never get removed, we visit them.
1203        // FIXME: it should be possible to GC wildcard tree roots.
1204        for id in self.roots.iter().copied() {
1205            visit(None, Some(self.nodes.get(id).unwrap().tag));
1206        }
1207        // We also need to keep around any exposed tags through which
1208        // an access could still happen.
1209        for (_id, node) in self.nodes.iter() {
1210            if node.is_exposed {
1211                visit(None, Some(node.tag))
1212            }
1213        }
1214    }
1215}
1216
1217/// Relative position of the access
1218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1219pub enum AccessRelatedness {
1220    /// The access happened either through the node itself or one of
1221    /// its transitive children.
1222    LocalAccess,
1223    /// The access happened through this nodes ancestor or through
1224    /// a sibling/cousin/uncle/etc.
1225    ForeignAccess,
1226}
1227
1228impl AccessRelatedness {
1229    /// Check that access is either Ancestor or Distant, i.e. not
1230    /// a transitive child (initial pointer included).
1231    pub fn is_foreign(self) -> bool {
1232        matches!(self, AccessRelatedness::ForeignAccess)
1233    }
1234}