1 use rustc_index
::bit_set
::BitSet
;
2 use rustc_middle
::mir
::visit
::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}
;
3 use rustc_middle
::mir
::{self, Local, Location}
;
5 use crate::{AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis}
;
7 /// A [live-variable dataflow analysis][liveness].
9 /// This analysis considers references as being used only at the point of the
10 /// borrow. In other words, this analysis does not track uses because of references that already
11 /// exist. See [this `mir-dataflow` test][flow-test] for an example. You almost never want to use
12 /// this analysis without also looking at the results of [`MaybeBorrowedLocals`].
14 /// ## Field-(in)sensitivity
16 /// As the name suggests, this analysis is field insensitive. If a projection of a variable `x` is
17 /// assigned to (e.g. `x.0 = 42`), it does not "define" `x` as far as liveness is concerned. In fact,
18 /// such an assignment is currently marked as a "use" of `x` in an attempt to be maximally
21 /// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
22 /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
23 /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
24 pub struct MaybeLiveLocals
;
26 impl MaybeLiveLocals
{
27 fn transfer_function
<'a
, T
>(&self, trans
: &'a
mut T
) -> TransferFunction
<'a
, T
> {
28 TransferFunction(trans
)
32 impl<'tcx
> AnalysisDomain
<'tcx
> for MaybeLiveLocals
{
33 type Domain
= BitSet
<Local
>;
34 type Direction
= Backward
;
36 const NAME
: &'
static str = "liveness";
38 fn bottom_value(&self, body
: &mir
::Body
<'tcx
>) -> Self::Domain
{
40 BitSet
::new_empty(body
.local_decls
.len())
43 fn initialize_start_block(&self, _
: &mir
::Body
<'tcx
>, _
: &mut Self::Domain
) {
44 // No variables are live until we observe a use
48 impl<'tcx
> GenKillAnalysis
<'tcx
> for MaybeLiveLocals
{
53 trans
: &mut impl GenKill
<Self::Idx
>,
54 statement
: &mir
::Statement
<'tcx
>,
57 self.transfer_function(trans
).visit_statement(statement
, location
);
62 trans
: &mut impl GenKill
<Self::Idx
>,
63 terminator
: &mir
::Terminator
<'tcx
>,
66 self.transfer_function(trans
).visit_terminator(terminator
, location
);
69 fn call_return_effect(
71 trans
: &mut impl GenKill
<Self::Idx
>,
72 _block
: mir
::BasicBlock
,
73 return_places
: CallReturnPlaces
<'_
, 'tcx
>,
75 return_places
.for_each(|place
| {
76 if let Some(local
) = place
.as_local() {
82 fn yield_resume_effect(
84 trans
: &mut impl GenKill
<Self::Idx
>,
85 _resume_block
: mir
::BasicBlock
,
86 resume_place
: mir
::Place
<'tcx
>,
88 if let Some(local
) = resume_place
.as_local() {
94 struct TransferFunction
<'a
, T
>(&'a
mut T
);
96 impl<'tcx
, T
> Visitor
<'tcx
> for TransferFunction
<'_
, T
>
100 fn visit_place(&mut self, place
: &mir
::Place
<'tcx
>, context
: PlaceContext
, location
: Location
) {
101 let mir
::Place { projection, local }
= *place
;
103 // We purposefully do not call `super_place` here to avoid calling `visit_local` for this
104 // place with one of the `Projection` variants of `PlaceContext`.
105 self.visit_projection(place
.as_ref(), context
, location
);
107 match DefUse
::for_place(context
) {
108 // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
109 Some(_
) if place
.is_indirect() => self.0.gen(local
),
111 Some(DefUse
::Def
) if projection
.is_empty() => self.0.kill(local
),
112 Some(DefUse
::Use
) => self.0.gen(local
),
117 fn visit_local(&mut self, &local
: &Local
, context
: PlaceContext
, _
: Location
) {
118 // Because we do not call `super_place` above, `visit_local` is only called for locals that
119 // do not appear as part of a `Place` in the MIR. This handles cases like the implicit use
120 // of the return place in a `Return` terminator or the index in an `Index` projection.
121 match DefUse
::for_place(context
) {
122 Some(DefUse
::Def
) => self.0.kill(local
),
123 Some(DefUse
::Use
) => self.0.gen(local
),
129 #[derive(Eq, PartialEq, Clone)]
136 fn for_place(context
: PlaceContext
) -> Option
<DefUse
> {
138 PlaceContext
::NonUse(_
) => None
,
140 PlaceContext
::MutatingUse(MutatingUseContext
::Store
| MutatingUseContext
::Deinit
) => {
144 // Setting the discriminant is not a use because it does no reading, but it is also not
145 // a def because it does not overwrite the whole place
146 PlaceContext
::MutatingUse(MutatingUseContext
::SetDiscriminant
) => None
,
148 // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the
149 // destination place for a `Call` return or `Yield` resume respectively. Since this is
150 // only a `Def` when the function returns successfully, we handle this case separately
151 // in `call_return_effect` above.
152 PlaceContext
::MutatingUse(
153 MutatingUseContext
::Call
154 | MutatingUseContext
::AsmOutput
155 | MutatingUseContext
::Yield
,
158 // All other contexts are uses...
159 PlaceContext
::MutatingUse(
160 MutatingUseContext
::AddressOf
161 | MutatingUseContext
::Borrow
162 | MutatingUseContext
::Drop
163 | MutatingUseContext
::Retag
,
165 | PlaceContext
::NonMutatingUse(
166 NonMutatingUseContext
::AddressOf
167 | NonMutatingUseContext
::Copy
168 | NonMutatingUseContext
::Inspect
169 | NonMutatingUseContext
::Move
170 | NonMutatingUseContext
::ShallowBorrow
171 | NonMutatingUseContext
::SharedBorrow
172 | NonMutatingUseContext
::UniqueBorrow
,
173 ) => Some(DefUse
::Use
),
175 PlaceContext
::MutatingUse(MutatingUseContext
::Projection
)
176 | PlaceContext
::NonMutatingUse(NonMutatingUseContext
::Projection
) => {
177 unreachable
!("A projection could be a def or a use and must be handled separately")