]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | //! Propagate `Qualif`s between locals and query the results. |
2 | //! | |
3 | //! This contains the dataflow analysis used to track `Qualif`s on complex control-flow graphs. | |
4 | ||
5 | use rustc::mir::visit::Visitor; | |
6 | use rustc::mir::{self, BasicBlock, Local, Location}; | |
7 | use rustc_index::bit_set::BitSet; | |
8 | ||
9 | use std::marker::PhantomData; | |
10 | ||
11 | use crate::dataflow::{self as old_dataflow, generic as dataflow}; | |
12 | use super::{Item, Qualif}; | |
13 | ||
14 | /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of | |
15 | /// `FlowSensitiveAnalysis`. | |
16 | /// | |
17 | /// This transfer does nothing when encountering an indirect assignment. Consumers should rely on | |
18 | /// the `IndirectlyMutableLocals` dataflow pass to see if a `Local` may have become qualified via | |
19 | /// an indirect assignment or function call. | |
20 | struct TransferFunction<'a, 'mir, 'tcx, Q> { | |
21 | item: &'a Item<'mir, 'tcx>, | |
22 | qualifs_per_local: &'a mut BitSet<Local>, | |
23 | ||
24 | _qualif: PhantomData<Q>, | |
25 | } | |
26 | ||
27 | impl<Q> TransferFunction<'a, 'mir, 'tcx, Q> | |
28 | where | |
29 | Q: Qualif, | |
30 | { | |
31 | fn new( | |
32 | item: &'a Item<'mir, 'tcx>, | |
33 | qualifs_per_local: &'a mut BitSet<Local>, | |
34 | ) -> Self { | |
35 | TransferFunction { | |
36 | item, | |
37 | qualifs_per_local, | |
38 | _qualif: PhantomData, | |
39 | } | |
40 | } | |
41 | ||
42 | fn initialize_state(&mut self) { | |
43 | self.qualifs_per_local.clear(); | |
44 | ||
45 | for arg in self.item.body.args_iter() { | |
46 | let arg_ty = self.item.body.local_decls[arg].ty; | |
47 | if Q::in_any_value_of_ty(self.item, arg_ty) { | |
48 | self.qualifs_per_local.insert(arg); | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
53 | fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) { | |
54 | debug_assert!(!place.is_indirect()); | |
55 | ||
56 | match (value, place.as_ref()) { | |
57 | (true, mir::PlaceRef { base: &mir::PlaceBase::Local(local), .. }) => { | |
58 | self.qualifs_per_local.insert(local); | |
59 | } | |
60 | ||
61 | // For now, we do not clear the qualif if a local is overwritten in full by | |
62 | // an unqualified rvalue (e.g. `y = 5`). This is to be consistent | |
63 | // with aggregates where we overwrite all fields with assignments, which would not | |
64 | // get this feature. | |
65 | (false, mir::PlaceRef { base: &mir::PlaceBase::Local(_local), projection: &[] }) => { | |
66 | // self.qualifs_per_local.remove(*local); | |
67 | } | |
68 | ||
69 | _ => {} | |
70 | } | |
71 | } | |
72 | ||
73 | fn apply_call_return_effect( | |
74 | &mut self, | |
75 | _block: BasicBlock, | |
76 | func: &mir::Operand<'tcx>, | |
77 | args: &[mir::Operand<'tcx>], | |
78 | return_place: &mir::Place<'tcx>, | |
79 | ) { | |
60c5eb7d | 80 | let return_ty = return_place.ty(*self.item.body, self.item.tcx).ty; |
e74abb32 XL |
81 | let qualif = Q::in_call( |
82 | self.item, | |
83 | &|l| self.qualifs_per_local.contains(l), | |
84 | func, | |
85 | args, | |
86 | return_ty, | |
87 | ); | |
88 | if !return_place.is_indirect() { | |
89 | self.assign_qualif_direct(return_place, qualif); | |
90 | } | |
91 | } | |
92 | } | |
93 | ||
94 | impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q> | |
95 | where | |
96 | Q: Qualif, | |
97 | { | |
98 | fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { | |
99 | self.super_operand(operand, location); | |
100 | ||
101 | if !Q::IS_CLEARED_ON_MOVE { | |
102 | return; | |
103 | } | |
104 | ||
105 | // If a local with no projections is moved from (e.g. `x` in `y = x`), record that | |
106 | // it no longer needs to be dropped. | |
107 | if let mir::Operand::Move(place) = operand { | |
108 | if let Some(local) = place.as_local() { | |
109 | self.qualifs_per_local.remove(local); | |
110 | } | |
111 | } | |
112 | } | |
113 | ||
114 | fn visit_assign( | |
115 | &mut self, | |
116 | place: &mir::Place<'tcx>, | |
117 | rvalue: &mir::Rvalue<'tcx>, | |
118 | location: Location, | |
119 | ) { | |
120 | let qualif = Q::in_rvalue(self.item, &|l| self.qualifs_per_local.contains(l), rvalue); | |
121 | if !place.is_indirect() { | |
122 | self.assign_qualif_direct(place, qualif); | |
123 | } | |
124 | ||
125 | // We need to assign qualifs to the left-hand side before visiting `rvalue` since | |
126 | // qualifs can be cleared on move. | |
127 | self.super_assign(place, rvalue, location); | |
128 | } | |
129 | ||
130 | fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { | |
131 | // The effect of assignment to the return place in `TerminatorKind::Call` is not applied | |
132 | // here; that occurs in `apply_call_return_effect`. | |
133 | ||
134 | if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { | |
135 | let qualif = Q::in_operand(self.item, &|l| self.qualifs_per_local.contains(l), value); | |
136 | if !dest.is_indirect() { | |
137 | self.assign_qualif_direct(dest, qualif); | |
138 | } | |
139 | } | |
140 | ||
141 | // We need to assign qualifs to the dropped location before visiting the operand that | |
142 | // replaces it since qualifs can be cleared on move. | |
143 | self.super_terminator_kind(kind, location); | |
144 | } | |
145 | } | |
146 | ||
147 | /// The dataflow analysis used to propagate qualifs on arbitrary CFGs. | |
148 | pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> { | |
149 | item: &'a Item<'mir, 'tcx>, | |
150 | _qualif: PhantomData<Q>, | |
151 | } | |
152 | ||
153 | impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> | |
154 | where | |
155 | Q: Qualif, | |
156 | { | |
157 | pub(super) fn new(_: Q, item: &'a Item<'mir, 'tcx>) -> Self { | |
158 | FlowSensitiveAnalysis { | |
159 | item, | |
160 | _qualif: PhantomData, | |
161 | } | |
162 | } | |
163 | ||
164 | fn transfer_function( | |
165 | &self, | |
166 | state: &'a mut BitSet<Local>, | |
167 | ) -> TransferFunction<'a, 'mir, 'tcx, Q> { | |
168 | TransferFunction::<Q>::new(self.item, state) | |
169 | } | |
170 | } | |
171 | ||
172 | impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { | |
173 | const BOTTOM_VALUE: bool = false; | |
174 | } | |
175 | ||
176 | impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> | |
177 | where | |
178 | Q: Qualif, | |
179 | { | |
180 | type Idx = Local; | |
181 | ||
182 | const NAME: &'static str = Q::ANALYSIS_NAME; | |
183 | ||
184 | fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { | |
185 | body.local_decls.len() | |
186 | } | |
187 | ||
188 | fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { | |
189 | self.transfer_function(state).initialize_state(); | |
190 | } | |
191 | ||
192 | fn apply_statement_effect( | |
193 | &self, | |
194 | state: &mut BitSet<Self::Idx>, | |
195 | statement: &mir::Statement<'tcx>, | |
196 | location: Location, | |
197 | ) { | |
198 | self.transfer_function(state).visit_statement(statement, location); | |
199 | } | |
200 | ||
201 | fn apply_terminator_effect( | |
202 | &self, | |
203 | state: &mut BitSet<Self::Idx>, | |
204 | terminator: &mir::Terminator<'tcx>, | |
205 | location: Location, | |
206 | ) { | |
207 | self.transfer_function(state).visit_terminator(terminator, location); | |
208 | } | |
209 | ||
210 | fn apply_call_return_effect( | |
211 | &self, | |
212 | state: &mut BitSet<Self::Idx>, | |
213 | block: BasicBlock, | |
214 | func: &mir::Operand<'tcx>, | |
215 | args: &[mir::Operand<'tcx>], | |
216 | return_place: &mir::Place<'tcx>, | |
217 | ) { | |
218 | self.transfer_function(state).apply_call_return_effect(block, func, args, return_place) | |
219 | } | |
220 | } |