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