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