rustc_mir_dataflow/framework/
cursor.rs1use std::borrow::Cow;
4use std::cmp::Ordering;
5use std::ops::{Deref, DerefMut};
6
7#[cfg(debug_assertions)]
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::mir::{self, BasicBlock, Location};
10
11use super::{Analysis, Direction, Effect, EffectIndex, Results};
12
13enum CowMut<'a, T> {
17 BorrowedMut(&'a mut T),
18 Owned(T),
19}
20
21impl<T> Deref for CowMut<'_, T> {
22 type Target = T;
23
24 fn deref(&self) -> &T {
25 match self {
26 CowMut::BorrowedMut(borrowed) => borrowed,
27 CowMut::Owned(owned) => owned,
28 }
29 }
30}
31
32impl<T> DerefMut for CowMut<'_, T> {
33 fn deref_mut(&mut self) -> &mut T {
34 match self {
35 CowMut::BorrowedMut(borrowed) => borrowed,
36 CowMut::Owned(owned) => owned,
37 }
38 }
39}
40
41pub struct ResultsCursor<'mir, 'tcx, A>
52where
53 A: Analysis<'tcx>,
54{
55 body: &'mir mir::Body<'tcx>,
56 analysis: CowMut<'mir, A>,
57 results: Cow<'mir, Results<A::Domain>>,
58 state: A::Domain,
59
60 pos: CursorPosition,
61
62 state_needs_reset: bool,
66
67 #[cfg(debug_assertions)]
68 reachable_blocks: DenseBitSet<BasicBlock>,
69}
70
71impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
72where
73 A: Analysis<'tcx>,
74{
75 pub fn get(&self) -> &A::Domain {
77 &self.state
78 }
79
80 pub fn body(&self) -> &'mir mir::Body<'tcx> {
82 self.body
83 }
84
85 fn new(
86 body: &'mir mir::Body<'tcx>,
87 analysis: CowMut<'mir, A>,
88 results: Cow<'mir, Results<A::Domain>>,
89 ) -> Self {
90 let bottom_value = analysis.bottom_value(body);
91 ResultsCursor {
92 body,
93 analysis,
94 results,
95
96 state_needs_reset: true,
100 state: bottom_value,
101 pos: CursorPosition::block_entry(mir::START_BLOCK),
102
103 #[cfg(debug_assertions)]
104 reachable_blocks: mir::traversal::reachable_as_bitset(body),
105 }
106 }
107
108 pub fn new_owning(
110 body: &'mir mir::Body<'tcx>,
111 analysis: A,
112 results: Results<A::Domain>,
113 ) -> Self {
114 Self::new(body, CowMut::Owned(analysis), Cow::Owned(results))
115 }
116
117 pub fn new_borrowing(
119 body: &'mir mir::Body<'tcx>,
120 analysis: &'mir mut A,
121 results: &'mir Results<A::Domain>,
122 ) -> Self {
123 Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results))
124 }
125
126 #[cfg(test)]
128 pub(crate) fn allow_unreachable(&mut self) {
129 #[cfg(debug_assertions)]
130 self.reachable_blocks.insert_all()
131 }
132
133 pub fn analysis(&self) -> &A {
135 &self.analysis
136 }
137
138 pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
144 #[cfg(debug_assertions)]
145 assert!(self.reachable_blocks.contains(block));
146
147 self.state.clone_from(&self.results[block]);
148 self.pos = CursorPosition::block_entry(block);
149 self.state_needs_reset = false;
150 }
151
152 pub fn seek_to_block_start(&mut self, block: BasicBlock) {
159 if A::Direction::IS_FORWARD {
160 self.seek_to_block_entry(block)
161 } else {
162 self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
163 }
164 }
165
166 pub fn seek_to_block_end(&mut self, block: BasicBlock) {
173 if A::Direction::IS_BACKWARD {
174 self.seek_to_block_entry(block)
175 } else {
176 self.seek_after(self.body.terminator_loc(block), Effect::Primary)
177 }
178 }
179
180 pub fn seek_before_primary_effect(&mut self, target: Location) {
185 self.seek_after(target, Effect::Early)
186 }
187
188 pub fn seek_after_primary_effect(&mut self, target: Location) {
193 self.seek_after(target, Effect::Primary)
194 }
195
196 fn seek_after(&mut self, target: Location, effect: Effect) {
197 assert!(target <= self.body.terminator_loc(target.block));
198
199 if self.state_needs_reset || self.pos.block != target.block {
204 self.seek_to_block_entry(target.block);
205 } else if let Some(curr_effect) = self.pos.curr_effect_index {
206 let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
207 if A::Direction::IS_BACKWARD {
208 ord = ord.reverse()
209 }
210
211 match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
212 Ordering::Equal => return,
213 Ordering::Greater => self.seek_to_block_entry(target.block),
214 Ordering::Less => {}
215 }
216 }
217
218 debug_assert_eq!(target.block, self.pos.block);
221
222 let block_data = &self.body[target.block];
223 #[rustfmt::skip]
224 let next_effect = if A::Direction::IS_FORWARD {
225 self.pos.curr_effect_index.map_or_else(
226 || Effect::Early.at_index(0),
227 EffectIndex::next_in_forward_order,
228 )
229 } else {
230 self.pos.curr_effect_index.map_or_else(
231 || Effect::Early.at_index(block_data.statements.len()),
232 EffectIndex::next_in_backward_order,
233 )
234 };
235
236 let target_effect_index = effect.at_index(target.statement_index);
237
238 A::Direction::apply_effects_in_range(
239 &mut *self.analysis,
240 &mut self.state,
241 target.block,
242 block_data,
243 next_effect..=target_effect_index,
244 );
245
246 self.pos =
247 CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
248 }
249
250 pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
255 f(&mut self.analysis, &mut self.state);
256 self.state_needs_reset = true;
257 }
258}
259
260#[derive(Clone, Copy, Debug)]
261struct CursorPosition {
262 block: BasicBlock,
263 curr_effect_index: Option<EffectIndex>,
264}
265
266impl CursorPosition {
267 fn block_entry(block: BasicBlock) -> CursorPosition {
268 CursorPosition { block, curr_effect_index: None }
269 }
270}