1 use super::TrackedValue
;
4 expr_use_visitor
::{self, ExprUseVisitor}
,
6 use hir
::{def_id::DefId, Body, HirId, HirIdMap}
;
7 use rustc_data_structures
::stable_set
::FxHashSet
;
9 use rustc_middle
::hir
::place
::{PlaceBase, Projection, ProjectionKind}
;
10 use rustc_middle
::ty
::{ParamEnv, TyCtxt}
;
12 pub(super) fn find_consumed_and_borrowed
<'a
, 'tcx
>(
13 fcx
: &'a FnCtxt
<'a
, 'tcx
>,
15 body
: &'tcx Body
<'tcx
>,
16 ) -> ConsumedAndBorrowedPlaces
{
17 let mut expr_use_visitor
= ExprUseDelegate
::new(fcx
.tcx
, fcx
.param_env
);
18 expr_use_visitor
.consume_body(fcx
, def_id
, body
);
19 expr_use_visitor
.places
22 pub(super) struct ConsumedAndBorrowedPlaces
{
23 /// Records the variables/expressions that are dropped by a given expression.
25 /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables
26 /// or values that are consumed by that expression.
28 /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is
29 /// not considered a drop of `x`, although it would be a drop of `x.y`.
30 pub(super) consumed
: HirIdMap
<FxHashSet
<TrackedValue
>>,
32 /// A set of hir-ids of values or variables that are borrowed at some point within the body.
33 pub(super) borrowed
: FxHashSet
<TrackedValue
>,
35 /// A set of hir-ids of values or variables that are borrowed at some point within the body.
36 pub(super) borrowed_temporaries
: FxHashSet
<HirId
>,
39 /// Works with ExprUseVisitor to find interesting values for the drop range analysis.
41 /// Interesting values are those that are either dropped or borrowed. For dropped values, we also
42 /// record the parent expression, which is the point where the drop actually takes place.
43 struct ExprUseDelegate
<'tcx
> {
45 param_env
: ParamEnv
<'tcx
>,
46 places
: ConsumedAndBorrowedPlaces
,
49 impl<'tcx
> ExprUseDelegate
<'tcx
> {
50 fn new(tcx
: TyCtxt
<'tcx
>, param_env
: ParamEnv
<'tcx
>) -> Self {
54 places
: ConsumedAndBorrowedPlaces
{
55 consumed
: <_
>::default(),
56 borrowed
: <_
>::default(),
57 borrowed_temporaries
: <_
>::default(),
62 fn consume_body(&mut self, fcx
: &'_ FnCtxt
<'_
, 'tcx
>, def_id
: DefId
, body
: &'tcx Body
<'tcx
>) {
63 // Run ExprUseVisitor to find where values are consumed.
67 def_id
.expect_local(),
69 &fcx
.typeck_results
.borrow(),
74 fn mark_consumed(&mut self, consumer
: HirId
, target
: TrackedValue
) {
75 if !self.places
.consumed
.contains_key(&consumer
) {
76 self.places
.consumed
.insert(consumer
, <_
>::default());
78 self.places
.consumed
.get_mut(&consumer
).map(|places
| places
.insert(target
));
81 fn borrow_place(&mut self, place_with_id
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>) {
84 .insert(TrackedValue
::from_place_with_projections_allowed(place_with_id
));
86 // Ordinarily a value is consumed by it's parent, but in the special case of a
87 // borrowed RValue, we create a reference that lives as long as the temporary scope
88 // for that expression (typically, the innermost statement, but sometimes the enclosing
89 // block). We record this fact here so that later in generator_interior
90 // we can use the correct scope.
92 // We special case borrows through a dereference (`&*x`, `&mut *x` where `x` is
93 // some rvalue expression), since these are essentially a copy of a pointer.
94 // In other words, this borrow does not refer to the
95 // temporary (`*x`), but to the referent (whatever `x` is a borrow of).
97 // We were considering that we might encounter problems down the line if somehow,
98 // some part of the compiler were to look at this result and try to use it to
99 // drive a borrowck-like analysis (this does not currently happen, as of this writing).
100 // But even this should be fine, because the lifetime of the dereferenced reference
101 // found in the rvalue is only significant as an intermediate 'link' to the value we
102 // are producing, and we separately track whether that value is live over a yield.
106 // fn identity<T>(x: &mut T) -> &mut T { x }
108 // let y: &'y mut A = &mut *identity(&'a mut a);
109 // ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about
112 // The expression `*identity(...)` is a deref of an rvalue,
113 // where the `identity(...)` (the rvalue) produces a return type
114 // of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
115 // `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
116 // `a` will be considered borrowed). Other parts of the code will ensure
117 // that if `y` is live over a yield, `&'y mut A` appears in the generator
118 // state. If `'y` is live, then any sound region analysis must conclude
119 // that `'a` is also live. So if this causes a bug, blame some other
121 let is_deref
= place_with_id
125 .any(|Projection { kind, .. }
| *kind
== ProjectionKind
::Deref
);
127 if let (false, PlaceBase
::Rvalue
) = (is_deref
, place_with_id
.place
.base
) {
128 self.places
.borrowed_temporaries
.insert(place_with_id
.hir_id
);
133 impl<'tcx
> expr_use_visitor
::Delegate
<'tcx
> for ExprUseDelegate
<'tcx
> {
136 place_with_id
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
139 let parent
= match self.tcx
.hir().find_parent_node(place_with_id
.hir_id
) {
140 Some(parent
) => parent
,
141 None
=> place_with_id
.hir_id
,
144 "consume {:?}; diag_expr_id={:?}, using parent {:?}",
145 place_with_id
, diag_expr_id
, parent
149 .map_or((), |tracked_value
| self.mark_consumed(parent
, tracked_value
));
154 place_with_id
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
156 bk
: rustc_middle
::ty
::BorrowKind
,
159 "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \
163 self.borrow_place(place_with_id
);
168 place_with_id
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
169 _diag_expr_id
: HirId
,
171 debug
!("copy: place_with_id = {place_with_id:?}");
175 .insert(TrackedValue
::from_place_with_projections_allowed(place_with_id
));
177 // For copied we treat this mostly like a borrow except that we don't add the place
178 // to borrowed_temporaries because the copy is consumed.
183 assignee_place
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
186 debug
!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
188 if assignee_place
.place
.base
== PlaceBase
::Rvalue
189 && assignee_place
.place
.projections
.is_empty()
191 // Assigning to an Rvalue is illegal unless done through a dereference. We would have
192 // already gotten a type error, so we will just return here.
196 // If the type being assigned needs dropped, then the mutation counts as a borrow
197 // since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
198 if assignee_place
.place
.base_ty
.needs_drop(self.tcx
, self.param_env
) {
201 .insert(TrackedValue
::from_place_with_projections_allowed(assignee_place
));
207 binding_place
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
210 debug
!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
215 place_with_id
: &expr_use_visitor
::PlaceWithHirId
<'tcx
>,
216 cause
: rustc_middle
::mir
::FakeReadCause
,
220 "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}"
223 // fake reads happen in places like the scrutinee of a match expression.
224 // we treat those as a borrow, much like a copy: the idea is that we are
225 // transiently creating a `&T` ref that we can read from to observe the current
226 // value (this `&T` is immediately dropped afterwards).
227 self.borrow_place(place_with_id
);