Skip to main content

rustc_mir_dataflow/impls/
borrowed_locals.rs

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