rustc_mir_dataflow/framework/
cursor.rs1use std::cmp::Ordering;
4use std::ops::Deref;
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
12enum SimpleCow<'a, T> {
15 Borrowed(&'a T),
16 Owned(T),
17}
18
19impl<T> Deref for SimpleCow<'_, T> {
20 type Target = T;
21
22 fn deref(&self) -> &T {
23 match self {
24 SimpleCow::Borrowed(borrowed) => borrowed,
25 SimpleCow::Owned(owned) => owned,
26 }
27 }
28}
29
30pub struct ResultsCursor<'mir, 'tcx, A>
41where
42 A: Analysis<'tcx>,
43{
44 body: &'mir mir::Body<'tcx>,
45 results: SimpleCow<'mir, Results<'tcx, A>>,
46 state: A::Domain,
47
48 pos: CursorPosition,
49
50 state_needs_reset: bool,
54
55 #[cfg(debug_assertions)]
56 reachable_blocks: DenseBitSet<BasicBlock>,
57}
58
59impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
60where
61 A: Analysis<'tcx>,
62{
63 pub fn get(&self) -> &A::Domain {
65 &self.state
66 }
67
68 pub fn body(&self) -> &'mir mir::Body<'tcx> {
70 self.body
71 }
72
73 fn new(body: &'mir mir::Body<'tcx>, results: SimpleCow<'mir, Results<'tcx, A>>) -> Self {
74 let bottom_value = results.analysis.bottom_value(body);
75 ResultsCursor {
76 body,
77 results,
78
79 state_needs_reset: true,
83 state: bottom_value,
84 pos: CursorPosition::block_entry(mir::START_BLOCK),
85
86 #[cfg(debug_assertions)]
87 reachable_blocks: mir::traversal::reachable_as_bitset(body),
88 }
89 }
90
91 pub fn new_owning(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
93 Self::new(body, SimpleCow::Owned(results))
94 }
95
96 pub fn new_borrowing(body: &'mir mir::Body<'tcx>, results: &'mir Results<'tcx, A>) -> Self {
98 Self::new(body, SimpleCow::Borrowed(results))
99 }
100
101 #[cfg(test)]
103 pub(crate) fn allow_unreachable(&mut self) {
104 #[cfg(debug_assertions)]
105 self.reachable_blocks.insert_all()
106 }
107
108 pub fn analysis(&self) -> &A {
110 &self.results.analysis
111 }
112
113 pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
119 #[cfg(debug_assertions)]
120 assert!(self.reachable_blocks.contains(block));
121
122 self.state.clone_from(&self.results.entry_states[block]);
123 self.pos = CursorPosition::block_entry(block);
124 self.state_needs_reset = false;
125 }
126
127 pub fn seek_to_block_start(&mut self, block: BasicBlock) {
134 if A::Direction::IS_FORWARD {
135 self.seek_to_block_entry(block)
136 } else {
137 self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
138 }
139 }
140
141 pub fn seek_to_block_end(&mut self, block: BasicBlock) {
148 if A::Direction::IS_BACKWARD {
149 self.seek_to_block_entry(block)
150 } else {
151 self.seek_after(self.body.terminator_loc(block), Effect::Primary)
152 }
153 }
154
155 pub fn seek_before_primary_effect(&mut self, target: Location) {
160 self.seek_after(target, Effect::Early)
161 }
162
163 pub fn seek_after_primary_effect(&mut self, target: Location) {
168 self.seek_after(target, Effect::Primary)
169 }
170
171 fn seek_after(&mut self, target: Location, effect: Effect) {
172 assert!(target <= self.body.terminator_loc(target.block));
173
174 if self.state_needs_reset || self.pos.block != target.block {
179 self.seek_to_block_entry(target.block);
180 } else if let Some(curr_effect) = self.pos.curr_effect_index {
181 let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
182 if A::Direction::IS_BACKWARD {
183 ord = ord.reverse()
184 }
185
186 match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
187 Ordering::Equal => return,
188 Ordering::Greater => self.seek_to_block_entry(target.block),
189 Ordering::Less => {}
190 }
191 }
192
193 debug_assert_eq!(target.block, self.pos.block);
196
197 let block_data = &self.body[target.block];
198 #[rustfmt::skip]
199 let next_effect = if A::Direction::IS_FORWARD {
200 self.pos.curr_effect_index.map_or_else(
201 || Effect::Early.at_index(0),
202 EffectIndex::next_in_forward_order,
203 )
204 } else {
205 self.pos.curr_effect_index.map_or_else(
206 || Effect::Early.at_index(block_data.statements.len()),
207 EffectIndex::next_in_backward_order,
208 )
209 };
210
211 let target_effect_index = effect.at_index(target.statement_index);
212
213 A::Direction::apply_effects_in_range(
214 &self.results.analysis,
215 &mut self.state,
216 target.block,
217 block_data,
218 next_effect..=target_effect_index,
219 );
220
221 self.pos =
222 CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
223 }
224
225 pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
230 f(&self.results.analysis, &mut self.state);
231 self.state_needs_reset = true;
232 }
233}
234
235#[derive(Clone, Copy, Debug)]
236struct CursorPosition {
237 block: BasicBlock,
238 curr_effect_index: Option<EffectIndex>,
239}
240
241impl CursorPosition {
242 fn block_entry(block: BasicBlock) -> CursorPosition {
243 CursorPosition { block, curr_effect_index: None }
244 }
245}