]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_mir_dataflow / src / impls / borrowed_locals.rs
CommitLineData
c295e0f8 1use super::*;
2c00a5a8 2
a2a8927a 3use crate::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
ba9703b0
XL
4use rustc_middle::mir::visit::Visitor;
5use rustc_middle::mir::*;
74b04a01
XL
6
7/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
8/// to a given local.
9///
74b04a01
XL
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
3c0e092e 12/// immovable generators.
5e7ed085 13pub struct MaybeBorrowedLocals;
2c00a5a8 14
3c0e092e 15impl MaybeBorrowedLocals {
3c0e092e 16 fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> {
5e7ed085 17 TransferFunction { trans }
2c00a5a8
XL
18 }
19}
20
a2a8927a 21impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals {
1b1a35ee 22 type Domain = BitSet<Local>;
3c0e092e 23 const NAME: &'static str = "maybe_borrowed_locals";
74b04a01 24
1b1a35ee
XL
25 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
26 // bottom = unborrowed
27 BitSet::new_empty(body.local_decls().len())
74b04a01
XL
28 }
29
1b1a35ee 30 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
74b04a01
XL
31 // No locals are aliased on function entry
32 }
33}
34
a2a8927a 35impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
1b1a35ee
XL
36 type Idx = Local;
37
74b04a01
XL
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);
dfeec247 45 }
74b04a01
XL
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);
2c00a5a8
XL
54 }
55
74b04a01
XL
56 fn call_return_effect(
57 &self,
58 _trans: &mut impl GenKill<Self::Idx>,
59 _block: mir::BasicBlock,
a2a8927a 60 _return_places: CallReturnPlaces<'_, 'tcx>,
74b04a01 61 ) {
2c00a5a8 62 }
74b04a01
XL
63}
64
74b04a01 65/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
3c0e092e 66struct TransferFunction<'a, T> {
74b04a01 67 trans: &'a mut T,
74b04a01 68}
b7449926 69
a2a8927a 70impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
74b04a01
XL
71where
72 T: GenKill<Local>,
74b04a01
XL
73{
74 fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
75 self.super_statement(stmt, location);
b7449926 76
74b04a01
XL
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);
b7449926 81 }
2c00a5a8
XL
82 }
83
74b04a01
XL
84 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
85 self.super_rvalue(rvalue, location);
86
87 match rvalue {
923072b8 88 mir::Rvalue::AddressOf(_, borrowed_place) | mir::Rvalue::Ref(_, _, borrowed_place) => {
3c0e092e 89 if !borrowed_place.is_indirect() {
74b04a01
XL
90 self.trans.gen(borrowed_place.local);
91 }
92 }
93
94 mir::Rvalue::Cast(..)
c295e0f8 95 | mir::Rvalue::ShallowInitBox(..)
74b04a01 96 | mir::Rvalue::Use(..)
f9f354fc 97 | mir::Rvalue::ThreadLocalRef(..)
74b04a01
XL
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(..)
064997fb
FG
105 | mir::Rvalue::Aggregate(..)
106 | mir::Rvalue::CopyForDeref(..) => {}
dc9dc135 107 }
2c00a5a8
XL
108 }
109
74b04a01
XL
110 fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
111 self.super_terminator(terminator, location);
112
113 match terminator.kind {
f035d41b
XL
114 mir::TerminatorKind::Drop { place: dropped_place, .. }
115 | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
5e7ed085
FG
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);
74b04a01
XL
125 }
126
127 TerminatorKind::Abort
128 | TerminatorKind::Assert { .. }
129 | TerminatorKind::Call { .. }
f035d41b 130 | TerminatorKind::FalseEdge { .. }
74b04a01
XL
131 | TerminatorKind::FalseUnwind { .. }
132 | TerminatorKind::GeneratorDrop
133 | TerminatorKind::Goto { .. }
f9f354fc 134 | TerminatorKind::InlineAsm { .. }
74b04a01
XL
135 | TerminatorKind::Resume
136 | TerminatorKind::Return
137 | TerminatorKind::SwitchInt { .. }
138 | TerminatorKind::Unreachable
139 | TerminatorKind::Yield { .. } => {}
140 }
2c00a5a8
XL
141 }
142}
923072b8
FG
143
144/// The set of locals that are borrowed at some point in the MIR body.
145pub 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}