]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
Remove `in_band_lifetimes` from `rustc_mir_dataflow`
[rust.git] / compiler / rustc_mir_dataflow / src / impls / borrowed_locals.rs
1 use super::*;
2
3 use crate::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
4 use rustc_middle::mir::visit::Visitor;
5 use rustc_middle::mir::*;
6
7 /// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
8 /// to a given local.
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 generators.
13 pub struct MaybeBorrowedLocals {
14     ignore_borrow_on_drop: bool,
15 }
16
17 impl MaybeBorrowedLocals {
18     /// A dataflow analysis that records whether a pointer or reference exists that may alias the
19     /// given local.
20     pub fn all_borrows() -> Self {
21         MaybeBorrowedLocals { ignore_borrow_on_drop: false }
22     }
23 }
24
25 impl MaybeBorrowedLocals {
26     /// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
27     ///
28     /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
29     /// parameter. In the general case, a drop impl could launder that reference into the
30     /// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the
31     /// dropped local. We are not yet willing to declare this particular case UB, so we must treat
32     /// all dropped locals as mutably borrowed for now. See discussion on [#61069].
33     ///
34     /// In some contexts, we know that this borrow will never occur. For example, during
35     /// const-eval, custom drop glue cannot be run. Code that calls this should document the
36     /// assumptions that justify ignoring `Drop` terminators in this way.
37     ///
38     /// [#61069]: https://github.com/rust-lang/rust/pull/61069
39     pub fn unsound_ignore_borrow_on_drop(self) -> Self {
40         MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
41     }
42
43     fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
44         TransferFunction { trans, ignore_borrow_on_drop: self.ignore_borrow_on_drop }
45     }
46 }
47
48 impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
49     type Domain = BitSet<Local>;
50     const NAME: &'static str = "maybe_borrowed_locals";
51
52     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
53         // bottom = unborrowed
54         BitSet::new_empty(body.local_decls().len())
55     }
56
57     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
58         // No locals are aliased on function entry
59     }
60 }
61
62 impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
63     type Idx = Local;
64
65     fn statement_effect(
66         &self,
67         trans: &mut impl GenKill<Self::Idx>,
68         statement: &mir::Statement<'tcx>,
69         location: Location,
70     ) {
71         self.transfer_function(trans).visit_statement(statement, location);
72     }
73
74     fn terminator_effect(
75         &self,
76         trans: &mut impl GenKill<Self::Idx>,
77         terminator: &mir::Terminator<'tcx>,
78         location: Location,
79     ) {
80         self.transfer_function(trans).visit_terminator(terminator, location);
81     }
82
83     fn call_return_effect(
84         &self,
85         _trans: &mut impl GenKill<Self::Idx>,
86         _block: mir::BasicBlock,
87         _return_places: CallReturnPlaces<'_, 'tcx>,
88     ) {
89     }
90 }
91
92 /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
93 struct TransferFunction<'a, T> {
94     trans: &'a mut T,
95     ignore_borrow_on_drop: bool,
96 }
97
98 impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
99 where
100     T: GenKill<Local>,
101 {
102     fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
103         self.super_statement(stmt, location);
104
105         // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
106         // are now invalid.
107         if let StatementKind::StorageDead(local) = stmt.kind {
108             self.trans.kill(local);
109         }
110     }
111
112     fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
113         self.super_rvalue(rvalue, location);
114
115         match rvalue {
116             mir::Rvalue::AddressOf(_mt, borrowed_place) => {
117                 if !borrowed_place.is_indirect() {
118                     self.trans.gen(borrowed_place.local);
119                 }
120             }
121
122             mir::Rvalue::Ref(_, _kind, borrowed_place) => {
123                 if !borrowed_place.is_indirect() {
124                     self.trans.gen(borrowed_place.local);
125                 }
126             }
127
128             mir::Rvalue::Cast(..)
129             | mir::Rvalue::ShallowInitBox(..)
130             | mir::Rvalue::Use(..)
131             | mir::Rvalue::ThreadLocalRef(..)
132             | mir::Rvalue::Repeat(..)
133             | mir::Rvalue::Len(..)
134             | mir::Rvalue::BinaryOp(..)
135             | mir::Rvalue::CheckedBinaryOp(..)
136             | mir::Rvalue::NullaryOp(..)
137             | mir::Rvalue::UnaryOp(..)
138             | mir::Rvalue::Discriminant(..)
139             | mir::Rvalue::Aggregate(..) => {}
140         }
141     }
142
143     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
144         self.super_terminator(terminator, location);
145
146         match terminator.kind {
147             mir::TerminatorKind::Drop { place: dropped_place, .. }
148             | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
149                 // See documentation for `unsound_ignore_borrow_on_drop` for an explanation.
150                 if !self.ignore_borrow_on_drop {
151                     self.trans.gen(dropped_place.local);
152                 }
153             }
154
155             TerminatorKind::Abort
156             | TerminatorKind::Assert { .. }
157             | TerminatorKind::Call { .. }
158             | TerminatorKind::FalseEdge { .. }
159             | TerminatorKind::FalseUnwind { .. }
160             | TerminatorKind::GeneratorDrop
161             | TerminatorKind::Goto { .. }
162             | TerminatorKind::InlineAsm { .. }
163             | TerminatorKind::Resume
164             | TerminatorKind::Return
165             | TerminatorKind::SwitchInt { .. }
166             | TerminatorKind::Unreachable
167             | TerminatorKind::Yield { .. } => {}
168         }
169     }
170 }