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