rustc_borrowck/
place_ext.rs

1use rustc_hir as hir;
2use rustc_macros::extension;
3use rustc_middle::mir::{Body, Mutability, Place, ProjectionElem};
4use rustc_middle::ty::{self, TyCtxt};
5use tracing::debug;
6
7use crate::borrow_set::LocalsStateAtExit;
8
9#[extension(pub trait PlaceExt<'tcx>)]
10impl<'tcx> Place<'tcx> {
11    /// Returns `true` if we can safely ignore borrows of this place.
12    /// This is true whenever there is no action that the user can do
13    /// to the place `self` that would invalidate the borrow. This is true
14    /// for borrows of raw pointer dereferents as well as shared references.
15    fn ignore_borrow(
16        &self,
17        tcx: TyCtxt<'tcx>,
18        body: &Body<'tcx>,
19        locals_state_at_exit: &LocalsStateAtExit,
20    ) -> bool {
21        // If a local variable is immutable, then we only need to track borrows to guard
22        // against two kinds of errors:
23        // * The variable being dropped while still borrowed (e.g., because the fn returns
24        //   a reference to a local variable)
25        // * The variable being moved while still borrowed
26        //
27        // In particular, the variable cannot be mutated -- the "access checks" will fail --
28        // so we don't have to worry about mutation while borrowed.
29        if let LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } =
30            locals_state_at_exit
31        {
32            let ignore = !has_storage_dead_or_moved.contains(self.local)
33                && body.local_decls[self.local].mutability == Mutability::Not;
34            debug!("ignore_borrow: local {:?} => {:?}", self.local, ignore);
35            if ignore {
36                return true;
37            }
38        }
39
40        for (i, (proj_base, elem)) in self.iter_projections().enumerate() {
41            if elem == ProjectionElem::Deref {
42                let ty = proj_base.ty(body, tcx).ty;
43                match ty.kind() {
44                    ty::Ref(_, _, hir::Mutability::Not) if i == 0 => {
45                        // For references to thread-local statics, we do need
46                        // to track the borrow.
47                        if body.local_decls[self.local].is_ref_to_thread_local() {
48                            continue;
49                        }
50                        return true;
51                    }
52                    ty::RawPtr(..) | ty::Ref(_, _, hir::Mutability::Not) => {
53                        // For both derefs of raw pointers and `&T`
54                        // references, the original path is `Copy` and
55                        // therefore not significant. In particular,
56                        // there is nothing the user can do to the
57                        // original path that would invalidate the
58                        // newly created reference -- and if there
59                        // were, then the user could have copied the
60                        // original path into a new variable and
61                        // borrowed *that* one, leaving the original
62                        // path unborrowed.
63                        return true;
64                    }
65                    _ => {}
66                }
67            }
68        }
69
70        false
71    }
72}