]>
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 | ||
e74abb32 | 11 | use super::{Item, Qualif}; |
dfeec247 | 12 | use crate::dataflow::{self as old_dataflow, generic as 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 | |
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 | { | |
dfeec247 XL |
31 | fn new(item: &'a Item<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet<Local>) -> Self { |
32 | TransferFunction { item, qualifs_per_local, _qualif: PhantomData } | |
e74abb32 XL |
33 | } |
34 | ||
35 | fn initialize_state(&mut self) { | |
36 | self.qualifs_per_local.clear(); | |
37 | ||
38 | for arg in self.item.body.args_iter() { | |
39 | let arg_ty = self.item.body.local_decls[arg].ty; | |
40 | if Q::in_any_value_of_ty(self.item, arg_ty) { | |
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 XL |
50 | (true, mir::PlaceRef { local, .. }) => { |
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, | |
69 | func: &mir::Operand<'tcx>, | |
70 | args: &[mir::Operand<'tcx>], | |
71 | return_place: &mir::Place<'tcx>, | |
72 | ) { | |
60c5eb7d | 73 | let return_ty = return_place.ty(*self.item.body, self.item.tcx).ty; |
dfeec247 XL |
74 | let qualif = |
75 | Q::in_call(self.item, &|l| self.qualifs_per_local.contains(l), func, args, return_ty); | |
e74abb32 XL |
76 | if !return_place.is_indirect() { |
77 | self.assign_qualif_direct(return_place, qualif); | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q> | |
83 | where | |
84 | Q: Qualif, | |
85 | { | |
86 | fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { | |
87 | self.super_operand(operand, location); | |
88 | ||
89 | if !Q::IS_CLEARED_ON_MOVE { | |
90 | return; | |
91 | } | |
92 | ||
93 | // If a local with no projections is moved from (e.g. `x` in `y = x`), record that | |
94 | // it no longer needs to be dropped. | |
95 | if let mir::Operand::Move(place) = operand { | |
96 | if let Some(local) = place.as_local() { | |
97 | self.qualifs_per_local.remove(local); | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | fn visit_assign( | |
103 | &mut self, | |
104 | place: &mir::Place<'tcx>, | |
105 | rvalue: &mir::Rvalue<'tcx>, | |
106 | location: Location, | |
107 | ) { | |
108 | let qualif = Q::in_rvalue(self.item, &|l| self.qualifs_per_local.contains(l), rvalue); | |
109 | if !place.is_indirect() { | |
110 | self.assign_qualif_direct(place, qualif); | |
111 | } | |
112 | ||
113 | // We need to assign qualifs to the left-hand side before visiting `rvalue` since | |
114 | // qualifs can be cleared on move. | |
115 | self.super_assign(place, rvalue, location); | |
116 | } | |
117 | ||
118 | fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { | |
119 | // The effect of assignment to the return place in `TerminatorKind::Call` is not applied | |
120 | // here; that occurs in `apply_call_return_effect`. | |
121 | ||
122 | if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { | |
123 | let qualif = Q::in_operand(self.item, &|l| self.qualifs_per_local.contains(l), value); | |
124 | if !dest.is_indirect() { | |
125 | self.assign_qualif_direct(dest, qualif); | |
126 | } | |
127 | } | |
128 | ||
129 | // We need to assign qualifs to the dropped location before visiting the operand that | |
130 | // replaces it since qualifs can be cleared on move. | |
131 | self.super_terminator_kind(kind, location); | |
132 | } | |
133 | } | |
134 | ||
135 | /// The dataflow analysis used to propagate qualifs on arbitrary CFGs. | |
136 | pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> { | |
137 | item: &'a Item<'mir, 'tcx>, | |
138 | _qualif: PhantomData<Q>, | |
139 | } | |
140 | ||
141 | impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> | |
142 | where | |
143 | Q: Qualif, | |
144 | { | |
145 | pub(super) fn new(_: Q, item: &'a Item<'mir, 'tcx>) -> Self { | |
dfeec247 | 146 | FlowSensitiveAnalysis { item, _qualif: PhantomData } |
e74abb32 XL |
147 | } |
148 | ||
149 | fn transfer_function( | |
150 | &self, | |
151 | state: &'a mut BitSet<Local>, | |
152 | ) -> TransferFunction<'a, 'mir, 'tcx, Q> { | |
153 | TransferFunction::<Q>::new(self.item, state) | |
154 | } | |
155 | } | |
156 | ||
157 | impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> { | |
158 | const BOTTOM_VALUE: bool = false; | |
159 | } | |
160 | ||
dfeec247 | 161 | impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> |
e74abb32 XL |
162 | where |
163 | Q: Qualif, | |
164 | { | |
165 | type Idx = Local; | |
166 | ||
167 | const NAME: &'static str = Q::ANALYSIS_NAME; | |
168 | ||
169 | fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { | |
170 | body.local_decls.len() | |
171 | } | |
172 | ||
173 | fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) { | |
174 | self.transfer_function(state).initialize_state(); | |
175 | } | |
dfeec247 | 176 | } |
e74abb32 | 177 | |
dfeec247 XL |
178 | impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> |
179 | where | |
180 | Q: Qualif, | |
181 | { | |
e74abb32 XL |
182 | fn apply_statement_effect( |
183 | &self, | |
184 | state: &mut BitSet<Self::Idx>, | |
185 | statement: &mir::Statement<'tcx>, | |
186 | location: Location, | |
187 | ) { | |
188 | self.transfer_function(state).visit_statement(statement, location); | |
189 | } | |
190 | ||
191 | fn apply_terminator_effect( | |
192 | &self, | |
193 | state: &mut BitSet<Self::Idx>, | |
194 | terminator: &mir::Terminator<'tcx>, | |
195 | location: Location, | |
196 | ) { | |
197 | self.transfer_function(state).visit_terminator(terminator, location); | |
198 | } | |
199 | ||
200 | fn apply_call_return_effect( | |
201 | &self, | |
202 | state: &mut BitSet<Self::Idx>, | |
203 | block: BasicBlock, | |
204 | func: &mir::Operand<'tcx>, | |
205 | args: &[mir::Operand<'tcx>], | |
206 | return_place: &mir::Place<'tcx>, | |
207 | ) { | |
208 | self.transfer_function(state).apply_call_return_effect(block, func, args, return_place) | |
209 | } | |
210 | } |