]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
Rollup merge of #107719 - WaffleLapkin:de-arena-allocates-you-UwU, r=cjgillot
[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
15 impl MaybeBorrowedLocals {
16     fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
17         TransferFunction { trans }
18     }
19 }
20
21 impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
22     type Domain = BitSet<Local>;
23     const NAME: &'static str = "maybe_borrowed_locals";
24
25     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
26         // bottom = unborrowed
27         BitSet::new_empty(body.local_decls().len())
28     }
29
30     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
31         // No locals are aliased on function entry
32     }
33 }
34
35 impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
36     type Idx = Local;
37
38     fn statement_effect(
39         &self,
40         trans: &mut impl GenKill<Self::Idx>,
41         statement: &mir::Statement<'tcx>,
42         location: Location,
43     ) {
44         self.transfer_function(trans).visit_statement(statement, location);
45     }
46
47     fn terminator_effect(
48         &self,
49         trans: &mut impl GenKill<Self::Idx>,
50         terminator: &mir::Terminator<'tcx>,
51         location: Location,
52     ) {
53         self.transfer_function(trans).visit_terminator(terminator, location);
54     }
55
56     fn call_return_effect(
57         &self,
58         _trans: &mut impl GenKill<Self::Idx>,
59         _block: mir::BasicBlock,
60         _return_places: CallReturnPlaces<'_, 'tcx>,
61     ) {
62     }
63 }
64
65 /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
66 struct TransferFunction<'a, T> {
67     trans: &'a mut T,
68 }
69
70 impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
71 where
72     T: GenKill<Local>,
73 {
74     fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
75         self.super_statement(stmt, location);
76
77         // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
78         // are now invalid.
79         if let StatementKind::StorageDead(local) = stmt.kind {
80             self.trans.kill(local);
81         }
82     }
83
84     fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
85         self.super_rvalue(rvalue, location);
86
87         match rvalue {
88             mir::Rvalue::AddressOf(_, borrowed_place) | mir::Rvalue::Ref(_, _, borrowed_place) => {
89                 if !borrowed_place.is_indirect() {
90                     self.trans.gen(borrowed_place.local);
91                 }
92             }
93
94             mir::Rvalue::Cast(..)
95             | mir::Rvalue::ShallowInitBox(..)
96             | mir::Rvalue::Use(..)
97             | mir::Rvalue::ThreadLocalRef(..)
98             | mir::Rvalue::Repeat(..)
99             | mir::Rvalue::Len(..)
100             | mir::Rvalue::BinaryOp(..)
101             | mir::Rvalue::CheckedBinaryOp(..)
102             | mir::Rvalue::NullaryOp(..)
103             | mir::Rvalue::UnaryOp(..)
104             | mir::Rvalue::Discriminant(..)
105             | mir::Rvalue::Aggregate(..)
106             | mir::Rvalue::CopyForDeref(..) => {}
107         }
108     }
109
110     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
111         self.super_terminator(terminator, location);
112
113         match terminator.kind {
114             mir::TerminatorKind::Drop { place: dropped_place, .. }
115             | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
116                 // Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut
117                 // self` as a parameter. In the general case, a drop impl could launder that
118                 // reference into the surrounding environment through a raw pointer, thus creating
119                 // a valid `*mut` pointing to the dropped local. We are not yet willing to declare
120                 // this particular case UB, so we must treat all dropped locals as mutably borrowed
121                 // for now. See discussion on [#61069].
122                 //
123                 // [#61069]: https://github.com/rust-lang/rust/pull/61069
124                 self.trans.gen(dropped_place.local);
125             }
126
127             TerminatorKind::Abort
128             | TerminatorKind::Assert { .. }
129             | TerminatorKind::Call { .. }
130             | TerminatorKind::FalseEdge { .. }
131             | TerminatorKind::FalseUnwind { .. }
132             | TerminatorKind::GeneratorDrop
133             | TerminatorKind::Goto { .. }
134             | TerminatorKind::InlineAsm { .. }
135             | TerminatorKind::Resume
136             | TerminatorKind::Return
137             | TerminatorKind::SwitchInt { .. }
138             | TerminatorKind::Unreachable
139             | TerminatorKind::Yield { .. } => {}
140         }
141     }
142 }
143
144 /// The set of locals that are borrowed at some point in the MIR body.
145 pub fn borrowed_locals(body: &Body<'_>) -> BitSet<Local> {
146     struct Borrowed(BitSet<Local>);
147
148     impl GenKill<Local> for Borrowed {
149         #[inline]
150         fn gen(&mut self, elem: Local) {
151             self.0.gen(elem)
152         }
153         #[inline]
154         fn kill(&mut self, _: Local) {
155             // Ignore borrow invalidation.
156         }
157     }
158
159     let mut borrowed = Borrowed(BitSet::new_empty(body.local_decls.len()));
160     TransferFunction { trans: &mut borrowed }.visit_body(body);
161     borrowed.0
162 }