|
1 |
| -use super::possible_origin::PossibleOriginVisitor; |
| 1 | +use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; |
2 | 2 | use crate::ty::is_copy;
|
3 |
| -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; |
| 3 | +use rustc_data_structures::fx::FxHashMap; |
4 | 4 | use rustc_index::bit_set::{BitSet, HybridBitSet};
|
5 | 5 | use rustc_lint::LateContext;
|
6 |
| -use rustc_middle::mir::{ |
7 |
| - self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, |
8 |
| -}; |
9 |
| -use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; |
10 |
| -use rustc_mir_dataflow::{ |
11 |
| - fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, |
12 |
| - CallReturnPlaces, ResultsCursor, |
13 |
| -}; |
| 6 | +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; |
| 7 | +use rustc_middle::ty::{self, visit::TypeVisitor}; |
| 8 | +use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; |
14 | 9 | use std::borrow::Cow;
|
15 | 10 | use std::ops::ControlFlow;
|
16 | 11 |
|
17 | 12 | /// Collects the possible borrowers of each local.
|
18 | 13 | /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
|
19 | 14 | /// possible borrowers of `a`.
|
20 | 15 | #[allow(clippy::module_name_repetitions)]
|
21 |
| -struct PossibleBorrowerAnalysis<'b, 'tcx> { |
22 |
| - tcx: TyCtxt<'tcx>, |
| 16 | +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { |
| 17 | + possible_borrower: TransitiveRelation, |
23 | 18 | body: &'b mir::Body<'tcx>,
|
| 19 | + cx: &'a LateContext<'tcx>, |
24 | 20 | possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
|
25 | 21 | }
|
26 | 22 |
|
27 |
| -#[derive(Clone, Debug, Eq, PartialEq)] |
28 |
| -struct PossibleBorrowerState { |
29 |
| - map: FxIndexMap<Local, BitSet<Local>>, |
30 |
| - domain_size: usize, |
31 |
| -} |
32 |
| - |
33 |
| -impl PossibleBorrowerState { |
34 |
| - fn new(domain_size: usize) -> Self { |
| 23 | +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { |
| 24 | + fn new( |
| 25 | + cx: &'a LateContext<'tcx>, |
| 26 | + body: &'b mir::Body<'tcx>, |
| 27 | + possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>, |
| 28 | + ) -> Self { |
35 | 29 | Self {
|
36 |
| - map: FxIndexMap::default(), |
37 |
| - domain_size, |
| 30 | + possible_borrower: TransitiveRelation::default(), |
| 31 | + cx, |
| 32 | + body, |
| 33 | + possible_origin, |
38 | 34 | }
|
39 | 35 | }
|
40 | 36 |
|
41 |
| - #[allow(clippy::similar_names)] |
42 |
| - fn add(&mut self, borrowed: Local, borrower: Local) { |
43 |
| - self.map |
44 |
| - .entry(borrowed) |
45 |
| - .or_insert(BitSet::new_empty(self.domain_size)) |
46 |
| - .insert(borrower); |
47 |
| - } |
48 |
| -} |
49 |
| - |
50 |
| -impl<C> DebugWithContext<C> for PossibleBorrowerState { |
51 |
| - fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
52 |
| - <_ as std::fmt::Debug>::fmt(self, f) |
53 |
| - } |
54 |
| - fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
55 |
| - unimplemented!() |
56 |
| - } |
57 |
| -} |
| 37 | + fn into_map( |
| 38 | + self, |
| 39 | + cx: &'a LateContext<'tcx>, |
| 40 | + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, |
| 41 | + ) -> PossibleBorrowerMap<'b, 'tcx> { |
| 42 | + let mut map = FxHashMap::default(); |
| 43 | + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { |
| 44 | + if is_copy(cx, self.body.local_decls[row].ty) { |
| 45 | + continue; |
| 46 | + } |
58 | 47 |
|
59 |
| -impl JoinSemiLattice for PossibleBorrowerState { |
60 |
| - fn join(&mut self, other: &Self) -> bool { |
61 |
| - let mut changed = false; |
62 |
| - for (&borrowed, borrowers) in other.map.iter() { |
| 48 | + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); |
| 49 | + borrowers.remove(mir::Local::from_usize(0)); |
63 | 50 | if !borrowers.is_empty() {
|
64 |
| - changed |= self |
65 |
| - .map |
66 |
| - .entry(borrowed) |
67 |
| - .or_insert(BitSet::new_empty(self.domain_size)) |
68 |
| - .union(borrowers); |
| 51 | + map.insert(row, borrowers); |
69 | 52 | }
|
70 | 53 | }
|
71 |
| - changed |
72 |
| - } |
73 |
| -} |
74 |
| - |
75 |
| -impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { |
76 |
| - type Domain = PossibleBorrowerState; |
77 |
| - |
78 |
| - const NAME: &'static str = "possible_borrower"; |
79 |
| - |
80 |
| - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { |
81 |
| - PossibleBorrowerState::new(body.local_decls.len()) |
82 |
| - } |
83 |
| - |
84 |
| - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} |
85 |
| -} |
86 | 54 |
|
87 |
| -impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { |
88 |
| - fn new( |
89 |
| - tcx: TyCtxt<'tcx>, |
90 |
| - body: &'b mir::Body<'tcx>, |
91 |
| - possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>, |
92 |
| - ) -> Self { |
93 |
| - Self { |
94 |
| - tcx, |
95 |
| - body, |
96 |
| - possible_origin, |
| 55 | + let bs = BitSet::new_empty(self.body.local_decls.len()); |
| 56 | + PossibleBorrowerMap { |
| 57 | + map, |
| 58 | + maybe_live, |
| 59 | + bitset: (bs.clone(), bs), |
97 | 60 | }
|
98 | 61 | }
|
99 | 62 | }
|
100 | 63 |
|
101 |
| -impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { |
102 |
| - fn apply_call_return_effect( |
103 |
| - &self, |
104 |
| - _state: &mut Self::Domain, |
105 |
| - _block: BasicBlock, |
106 |
| - _return_places: CallReturnPlaces<'_, 'tcx>, |
107 |
| - ) { |
108 |
| - } |
109 |
| - |
110 |
| - fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { |
111 |
| - if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { |
112 |
| - let lhs = place.local; |
113 |
| - match rvalue { |
114 |
| - mir::Rvalue::Ref(_, _, borrowed) => { |
115 |
| - state.add(borrowed.local, lhs); |
116 |
| - }, |
117 |
| - other => { |
118 |
| - if ContainsRegion |
119 |
| - .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) |
120 |
| - .is_continue() |
121 |
| - { |
122 |
| - return; |
| 64 | +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { |
| 65 | + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { |
| 66 | + let lhs = place.local; |
| 67 | + match rvalue { |
| 68 | + mir::Rvalue::Ref(_, _, borrowed) => { |
| 69 | + self.possible_borrower.add(borrowed.local, lhs); |
| 70 | + }, |
| 71 | + other => { |
| 72 | + if ContainsRegion |
| 73 | + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) |
| 74 | + .is_continue() |
| 75 | + { |
| 76 | + return; |
| 77 | + } |
| 78 | + rvalue_locals(other, |rhs| { |
| 79 | + if lhs != rhs { |
| 80 | + self.possible_borrower.add(rhs, lhs); |
123 | 81 | }
|
124 |
| - rvalue_locals(other, |rhs| { |
125 |
| - if lhs != rhs { |
126 |
| - state.add(rhs, lhs); |
127 |
| - } |
128 |
| - }); |
129 |
| - }, |
130 |
| - } |
| 82 | + }); |
| 83 | + }, |
131 | 84 | }
|
132 | 85 | }
|
133 | 86 |
|
134 |
| - fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { |
| 87 | + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { |
135 | 88 | if let mir::TerminatorKind::Call {
|
136 | 89 | args,
|
137 | 90 | destination: mir::Place { local: dest, .. },
|
@@ -171,10 +124,10 @@ impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> {
|
171 | 124 |
|
172 | 125 | for y in mutable_variables {
|
173 | 126 | for x in &immutable_borrowers {
|
174 |
| - state.add(*x, y); |
| 127 | + self.possible_borrower.add(*x, y); |
175 | 128 | }
|
176 | 129 | for x in &mutable_borrowers {
|
177 |
| - state.add(*x, y); |
| 130 | + self.possible_borrower.add(*x, y); |
178 | 131 | }
|
179 | 132 | }
|
180 | 133 | }
|
@@ -210,98 +163,73 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
|
210 | 163 | }
|
211 | 164 | }
|
212 | 165 |
|
213 |
| -/// Result of `PossibleBorrowerAnalysis`. |
| 166 | +/// Result of `PossibleBorrowerVisitor`. |
214 | 167 | #[allow(clippy::module_name_repetitions)]
|
215 | 168 | pub struct PossibleBorrowerMap<'b, 'tcx> {
|
216 |
| - body: &'b mir::Body<'tcx>, |
217 |
| - possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, |
218 |
| - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, |
219 |
| - pushed: BitSet<Local>, |
220 |
| - stack: Vec<Local>, |
| 169 | + /// Mapping `Local -> its possible borrowers` |
| 170 | + pub map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>, |
| 171 | + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, |
| 172 | + // Caches to avoid allocation of `BitSet` on every query |
| 173 | + pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>), |
221 | 174 | }
|
222 | 175 |
|
223 |
| -impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { |
224 |
| - pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { |
| 176 | +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { |
| 177 | + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { |
225 | 178 | let possible_origin = {
|
226 | 179 | let mut vis = PossibleOriginVisitor::new(mir);
|
227 | 180 | vis.visit_body(mir);
|
228 | 181 | vis.into_map(cx)
|
229 | 182 | };
|
230 |
| - let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) |
| 183 | + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) |
231 | 184 | .into_engine(cx.tcx, mir)
|
232 |
| - .pass_name("possible_borrower") |
| 185 | + .pass_name("redundant_clone") |
233 | 186 | .iterate_to_fixpoint()
|
234 | 187 | .into_results_cursor(mir);
|
235 |
| - let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) |
236 |
| - .into_engine(cx.tcx, mir) |
237 |
| - .pass_name("possible_borrower") |
238 |
| - .iterate_to_fixpoint() |
239 |
| - .into_results_cursor(mir); |
240 |
| - PossibleBorrowerMap { |
241 |
| - body: mir, |
242 |
| - possible_borrower, |
243 |
| - maybe_live, |
244 |
| - pushed: BitSet::new_empty(mir.local_decls.len()), |
245 |
| - stack: Vec::with_capacity(mir.local_decls.len()), |
246 |
| - } |
| 188 | + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); |
| 189 | + vis.visit_body(mir); |
| 190 | + vis.into_map(cx, maybe_storage_live_result) |
247 | 191 | }
|
248 | 192 |
|
249 |
| - /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than |
250 |
| - /// `borrowers`. |
251 |
| - /// Notes: |
252 |
| - /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` |
253 |
| - /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` |
254 |
| - /// `Dereferencing`, which outlives any `LateContext`. |
255 |
| - /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two |
256 |
| - /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If |
257 |
| - /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be |
258 |
| - /// adjusted. |
259 |
| - pub fn at_most_borrowers( |
| 193 | + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. |
| 194 | + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { |
| 195 | + self.bounded_borrowers(borrowers, borrowers, borrowed, at) |
| 196 | + } |
| 197 | + |
| 198 | + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` |
| 199 | + /// but no more than `above`. |
| 200 | + pub fn bounded_borrowers( |
260 | 201 | &mut self,
|
261 |
| - cx: &LateContext<'tcx>, |
262 |
| - borrowers: &[mir::Local], |
| 202 | + below: &[mir::Local], |
| 203 | + above: &[mir::Local], |
263 | 204 | borrowed: mir::Local,
|
264 | 205 | at: mir::Location,
|
265 | 206 | ) -> bool {
|
266 |
| - if is_copy(cx, self.body.local_decls[borrowed].ty) { |
267 |
| - return true; |
268 |
| - } |
269 |
| - |
270 |
| - self.possible_borrower.seek_before_primary_effect(at); |
271 |
| - self.maybe_live.seek_before_primary_effect(at); |
272 |
| - |
273 |
| - let possible_borrower = &self.possible_borrower.get().map; |
274 |
| - let maybe_live = &self.maybe_live; |
275 |
| - |
276 |
| - self.pushed.clear(); |
277 |
| - self.stack.clear(); |
| 207 | + self.maybe_live.seek_after_primary_effect(at); |
278 | 208 |
|
279 |
| - if let Some(borrowers) = possible_borrower.get(&borrowed) { |
280 |
| - for b in borrowers.iter() { |
281 |
| - if self.pushed.insert(b) { |
282 |
| - self.stack.push(b); |
283 |
| - } |
| 209 | + self.bitset.0.clear(); |
| 210 | + let maybe_live = &mut self.maybe_live; |
| 211 | + if let Some(bitset) = self.map.get(&borrowed) { |
| 212 | + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { |
| 213 | + self.bitset.0.insert(b); |
284 | 214 | }
|
285 | 215 | } else {
|
286 |
| - // Nothing borrows `borrowed` at `at`. |
287 |
| - return true; |
| 216 | + return false; |
288 | 217 | }
|
289 | 218 |
|
290 |
| - while let Some(borrower) = self.stack.pop() { |
291 |
| - if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { |
292 |
| - return false; |
293 |
| - } |
| 219 | + self.bitset.1.clear(); |
| 220 | + for b in below { |
| 221 | + self.bitset.1.insert(*b); |
| 222 | + } |
294 | 223 |
|
295 |
| - if let Some(borrowers) = possible_borrower.get(&borrower) { |
296 |
| - for b in borrowers.iter() { |
297 |
| - if self.pushed.insert(b) { |
298 |
| - self.stack.push(b); |
299 |
| - } |
300 |
| - } |
301 |
| - } |
| 224 | + if !self.bitset.0.superset(&self.bitset.1) { |
| 225 | + return false; |
| 226 | + } |
| 227 | + |
| 228 | + for b in above { |
| 229 | + self.bitset.0.remove(*b); |
302 | 230 | }
|
303 | 231 |
|
304 |
| - true |
| 232 | + self.bitset.0.is_empty() |
305 | 233 | }
|
306 | 234 |
|
307 | 235 | pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
|
|
0 commit comments