rustc_mir_dataflow/impls/
borrowed_locals.rs

1use rustc_index::bit_set::DenseBitSet;
2use rustc_middle::mir::visit::Visitor;
3use rustc_middle::mir::*;
4
5use crate::{Analysis, GenKill};
6
7/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
8/// to a given local. This analysis ignores fake borrows, so it should not be used by
9/// borrowck.
10///
11/// At present, this is used as a very limited form of alias analysis. For example,
12/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
13/// immovable coroutines.
14pub struct MaybeBorrowedLocals;
15
16impl MaybeBorrowedLocals {
17    pub(super) fn transfer_function<'a, T>(trans: &'a mut T) -> TransferFunction<'a, T> {
18        TransferFunction { trans }
19    }
20}
21
22impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
23    type Domain = DenseBitSet<Local>;
24    const NAME: &'static str = "maybe_borrowed_locals";
25
26    fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
27        // bottom = unborrowed
28        DenseBitSet::new_empty(body.local_decls().len())
29    }
30
31    fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
32        // No locals are aliased on function entry
33    }
34
35    fn apply_primary_statement_effect(
36        &self,
37        state: &mut Self::Domain,
38        statement: &Statement<'tcx>,
39        location: Location,
40    ) {
41        Self::transfer_function(state).visit_statement(statement, location);
42    }
43
44    fn apply_primary_terminator_effect<'mir>(
45        &self,
46        state: &mut Self::Domain,
47        terminator: &'mir Terminator<'tcx>,
48        location: Location,
49    ) -> TerminatorEdges<'mir, 'tcx> {
50        Self::transfer_function(state).visit_terminator(terminator, location);
51        terminator.edges()
52    }
53}
54
55/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
56pub(super) struct TransferFunction<'a, T> {
57    trans: &'a mut T,
58}
59
60impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
61where
62    T: GenKill<Local>,
63{
64    fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
65        self.super_statement(stmt, location);
66
67        // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
68        // are now invalid.
69        if let StatementKind::StorageDead(local) = stmt.kind {
70            self.trans.kill(local);
71        }
72    }
73
74    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
75        self.super_rvalue(rvalue, location);
76
77        match rvalue {
78            // We ignore fake borrows as these get removed after analysis and shouldn't effect
79            // the layout of generators.
80            Rvalue::RawPtr(_, borrowed_place)
81            | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => {
82                if !borrowed_place.is_indirect() {
83                    self.trans.gen_(borrowed_place.local);
84                }
85            }
86
87            Rvalue::Cast(..)
88            | Rvalue::Ref(_, BorrowKind::Fake(_), _)
89            | Rvalue::ShallowInitBox(..)
90            | Rvalue::Use(..)
91            | Rvalue::ThreadLocalRef(..)
92            | Rvalue::Repeat(..)
93            | Rvalue::BinaryOp(..)
94            | Rvalue::NullaryOp(..)
95            | Rvalue::UnaryOp(..)
96            | Rvalue::Discriminant(..)
97            | Rvalue::Aggregate(..)
98            | Rvalue::CopyForDeref(..)
99            | Rvalue::WrapUnsafeBinder(..) => {}
100        }
101    }
102
103    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
104        self.super_terminator(terminator, location);
105
106        match terminator.kind {
107            TerminatorKind::Drop { place: dropped_place, .. } => {
108                // Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut
109                // self` as a parameter. In the general case, a drop impl could launder that
110                // reference into the surrounding environment through a raw pointer, thus creating
111                // a valid `*mut` pointing to the dropped local. We are not yet willing to declare
112                // this particular case UB, so we must treat all dropped locals as mutably borrowed
113                // for now. See discussion on [#61069].
114                //
115                // [#61069]: https://github.com/rust-lang/rust/pull/61069
116                if !dropped_place.is_indirect() {
117                    self.trans.gen_(dropped_place.local);
118                }
119            }
120
121            TerminatorKind::UnwindTerminate(_)
122            | TerminatorKind::Assert { .. }
123            | TerminatorKind::Call { .. }
124            | TerminatorKind::FalseEdge { .. }
125            | TerminatorKind::FalseUnwind { .. }
126            | TerminatorKind::CoroutineDrop
127            | TerminatorKind::Goto { .. }
128            | TerminatorKind::InlineAsm { .. }
129            | TerminatorKind::UnwindResume
130            | TerminatorKind::Return
131            | TerminatorKind::TailCall { .. }
132            | TerminatorKind::SwitchInt { .. }
133            | TerminatorKind::Unreachable
134            | TerminatorKind::Yield { .. } => {}
135        }
136    }
137}
138
139/// The set of locals that are borrowed at some point in the MIR body.
140pub fn borrowed_locals(body: &Body<'_>) -> DenseBitSet<Local> {
141    struct Borrowed(DenseBitSet<Local>);
142
143    impl GenKill<Local> for Borrowed {
144        #[inline]
145        fn gen_(&mut self, elem: Local) {
146            self.0.gen_(elem)
147        }
148        #[inline]
149        fn kill(&mut self, _: Local) {
150            // Ignore borrow invalidation.
151        }
152    }
153
154    let mut borrowed = Borrowed(DenseBitSet::new_empty(body.local_decls.len()));
155    TransferFunction { trans: &mut borrowed }.visit_body(body);
156    borrowed.0
157}