1use std::rc::Rc;
15
16use either::Either;
17use indexical::ToIndex;
18use rustc_data_structures::{graph::Successors, work_queue::WorkQueue};
19use rustc_index::IndexVec;
20use rustc_middle::{
21 mir::{Body, Location, traversal},
22 ty::TyCtxt,
23};
24use rustc_mir_dataflow::{Analysis, Direction, JoinSemiLattice};
25use rustc_utils::{
26 BodyExt,
27 mir::location_or_arg::{
28 LocationOrArg,
29 index::{LocationOrArgDomain, LocationOrArgIndex},
30 },
31};
32
33pub struct AnalysisResults<'tcx, A: Analysis<'tcx>> {
36 pub analysis: A,
38 location_domain: Rc<LocationOrArgDomain>,
39 state: IndexVec<LocationOrArgIndex, Rc<A::Domain>>,
40}
41
42impl<'tcx, A: Analysis<'tcx>> AnalysisResults<'tcx, A> {
43 pub fn state_at(&self, location: Location) -> &A::Domain {
46 &self.state[location.to_index(&self.location_domain)]
47 }
48}
49
50pub fn iterate_to_fixpoint<'tcx, A: Analysis<'tcx>>(
54 _tcx: TyCtxt<'tcx>,
55 body: &Body<'tcx>,
56 location_domain: Rc<LocationOrArgDomain>,
57 mut analysis: A,
58) -> AnalysisResults<'tcx, A> {
59 let bottom_value = analysis.bottom_value(body);
60
61 let num_locs = body.all_locations().count();
64 let mut state = IndexVec::from_elem_n(bottom_value, num_locs);
65
66 analysis
67 .initialize_start_block(body, &mut state[Location::START.to_index(&location_domain)]);
68
69 let mut dirty_queue: WorkQueue<LocationOrArgIndex> = WorkQueue::with_none(num_locs);
70 if A::Direction::IS_FORWARD {
71 for (block, data) in traversal::reverse_postorder(body) {
72 for statement_index in 0 ..= data.statements.len() {
73 let location = Location {
74 block,
75 statement_index,
76 };
77 dirty_queue.insert(location.to_index(&location_domain));
78 }
79 }
80 }
81
82 while let Some(loc_index) = dirty_queue.pop() {
83 let LocationOrArg::Location(location) = *location_domain.value(loc_index) else {
84 unreachable!()
85 };
86 let next_locs = match body.stmt_at(location) {
87 Either::Left(statement) => {
88 analysis.apply_primary_statement_effect(
89 &mut state[loc_index],
90 statement,
91 location,
92 );
93 vec![location.successor_within_block()]
94 }
95 Either::Right(terminator) => {
96 analysis.apply_primary_terminator_effect(
97 &mut state[loc_index],
98 terminator,
99 location,
100 );
101 body
102 .basic_blocks
103 .successors(location.block)
104 .map(|block| Location {
105 block,
106 statement_index: 0,
107 })
108 .collect::<Vec<_>>()
109 }
110 };
111
112 for next_loc in next_locs {
113 let next_loc_index = location_domain.index(&LocationOrArg::Location(next_loc));
114 let (cur_state, next_state) = state.pick2_mut(loc_index, next_loc_index);
115 let changed = next_state.join(cur_state);
116 if changed {
117 dirty_queue.insert(next_loc_index);
118 }
119 }
120 }
121
122 let state = state.into_iter().map(Rc::new).collect();
123
124 AnalysisResults {
125 analysis,
126 location_domain,
127 state,
128 }
129}