1 //! Propagate `Qualif`s between locals and query the results.
3 //! This contains the dataflow analysis used to track `Qualif`s on complex control-flow graphs.
5 use rustc_index
::bit_set
::BitSet
;
6 use rustc_middle
::mir
::visit
::Visitor
;
7 use rustc_middle
::mir
::{self, BasicBlock, Local, Location}
;
9 use std
::marker
::PhantomData
;
11 use super::{qualifs, Item, Qualif}
;
14 /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
15 /// `FlowSensitiveAnalysis`.
17 /// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
18 /// the `MaybeMutBorrowedLocals` 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
>,
24 _qualif
: PhantomData
<Q
>,
27 impl<Q
> TransferFunction
<'a
, 'mir
, 'tcx
, Q
>
31 fn new(item
: &'a Item
<'mir
, 'tcx
>, qualifs_per_local
: &'a
mut BitSet
<Local
>) -> Self {
32 TransferFunction { item, qualifs_per_local, _qualif: PhantomData }
35 fn initialize_state(&mut self) {
36 self.qualifs_per_local
.clear();
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
);
46 fn assign_qualif_direct(&mut self, place
: &mir
::Place
<'tcx
>, value
: bool
) {
47 debug_assert
!(!place
.is_indirect());
49 match (value
, place
.as_ref()) {
50 (true, mir
::PlaceRef { local, .. }
) => {
51 self.qualifs_per_local
.insert(local
);
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
58 (false, mir
::PlaceRef { local: _, projection: &[] }
) => {
59 // self.qualifs_per_local.remove(*local);
66 fn apply_call_return_effect(
69 _func
: &mir
::Operand
<'tcx
>,
70 _args
: &[mir
::Operand
<'tcx
>],
71 return_place
: mir
::Place
<'tcx
>,
73 // We cannot reason about another function's internals, so use conservative type-based
74 // qualification for the result of a function call.
75 let return_ty
= return_place
.ty(*self.item
.body
, self.item
.tcx
).ty
;
76 let qualif
= Q
::in_any_value_of_ty(self.item
, return_ty
);
78 if !return_place
.is_indirect() {
79 self.assign_qualif_direct(&return_place
, qualif
);
84 impl<Q
> Visitor
<'tcx
> for TransferFunction
<'_
, '_
, 'tcx
, Q
>
88 fn visit_operand(&mut self, operand
: &mir
::Operand
<'tcx
>, location
: Location
) {
89 self.super_operand(operand
, location
);
91 if !Q
::IS_CLEARED_ON_MOVE
{
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
);
106 place
: &mir
::Place
<'tcx
>,
107 rvalue
: &mir
::Rvalue
<'tcx
>,
110 let qualif
= qualifs
::in_rvalue
::<Q
, _
>(
112 &mut |l
| self.qualifs_per_local
.contains(l
),
115 if !place
.is_indirect() {
116 self.assign_qualif_direct(place
, qualif
);
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
);
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`.
128 if let mir
::TerminatorKind
::DropAndReplace { value, location: dest, .. }
= kind
{
129 let qualif
= qualifs
::in_operand
::<Q
, _
>(
131 &mut |l
| self.qualifs_per_local
.contains(l
),
135 if !dest
.is_indirect() {
136 self.assign_qualif_direct(dest
, qualif
);
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
);
146 /// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
147 pub(super) struct FlowSensitiveAnalysis
<'a
, 'mir
, 'tcx
, Q
> {
148 item
: &'a Item
<'mir
, 'tcx
>,
149 _qualif
: PhantomData
<Q
>,
152 impl<'a
, 'mir
, 'tcx
, Q
> FlowSensitiveAnalysis
<'a
, 'mir
, 'tcx
, Q
>
156 pub(super) fn new(_
: Q
, item
: &'a Item
<'mir
, 'tcx
>) -> Self {
157 FlowSensitiveAnalysis { item, _qualif: PhantomData }
160 fn transfer_function(
162 state
: &'a
mut BitSet
<Local
>,
163 ) -> TransferFunction
<'a
, 'mir
, 'tcx
, Q
> {
164 TransferFunction
::<Q
>::new(self.item
, state
)
168 impl<Q
> dataflow
::BottomValue
for FlowSensitiveAnalysis
<'_
, '_
, '_
, Q
> {
169 const BOTTOM_VALUE
: bool
= false;
172 impl<Q
> dataflow
::AnalysisDomain
<'tcx
> for FlowSensitiveAnalysis
<'_
, '_
, 'tcx
, Q
>
178 const NAME
: &'
static str = Q
::ANALYSIS_NAME
;
180 fn bits_per_block(&self, body
: &mir
::Body
<'tcx
>) -> usize {
181 body
.local_decls
.len()
184 fn initialize_start_block(&self, _body
: &mir
::Body
<'tcx
>, state
: &mut BitSet
<Self::Idx
>) {
185 self.transfer_function(state
).initialize_state();
189 impl<Q
> dataflow
::Analysis
<'tcx
> for FlowSensitiveAnalysis
<'_
, '_
, 'tcx
, Q
>
193 fn apply_statement_effect(
195 state
: &mut BitSet
<Self::Idx
>,
196 statement
: &mir
::Statement
<'tcx
>,
199 self.transfer_function(state
).visit_statement(statement
, location
);
202 fn apply_terminator_effect(
204 state
: &mut BitSet
<Self::Idx
>,
205 terminator
: &mir
::Terminator
<'tcx
>,
208 self.transfer_function(state
).visit_terminator(terminator
, location
);
211 fn apply_call_return_effect(
213 state
: &mut BitSet
<Self::Idx
>,
215 func
: &mir
::Operand
<'tcx
>,
216 args
: &[mir
::Operand
<'tcx
>],
217 return_place
: mir
::Place
<'tcx
>,
219 self.transfer_function(state
).apply_call_return_effect(block
, func
, args
, return_place
)