]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
rustc_hir_typeck: fix paths and partially mv files
[rust.git] / compiler / rustc_hir_typeck / src / generator_interior / drop_ranges / record_consumed_borrow.rs
1 use super::TrackedValue;
2 use crate::{
3     expr_use_visitor::{self, ExprUseVisitor},
4     FnCtxt,
5 };
6 use hir::{def_id::DefId, Body, HirId, HirIdMap};
7 use rustc_data_structures::fx::FxHashSet;
8 use rustc_hir as hir;
9 use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
10 use rustc_middle::ty::{ParamEnv, TyCtxt};
11
12 pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
13     fcx: &'a FnCtxt<'a, 'tcx>,
14     def_id: DefId,
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
20 }
21
22 pub(super) struct ConsumedAndBorrowedPlaces {
23     /// Records the variables/expressions that are dropped by a given expression.
24     ///
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.
27     ///
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>>,
31
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>,
34
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>,
37 }
38
39 /// Works with ExprUseVisitor to find interesting values for the drop range analysis.
40 ///
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> {
44     tcx: TyCtxt<'tcx>,
45     param_env: ParamEnv<'tcx>,
46     places: ConsumedAndBorrowedPlaces,
47 }
48
49 impl<'tcx> ExprUseDelegate<'tcx> {
50     fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
51         Self {
52             tcx,
53             param_env,
54             places: ConsumedAndBorrowedPlaces {
55                 consumed: <_>::default(),
56                 borrowed: <_>::default(),
57                 borrowed_temporaries: <_>::default(),
58             },
59         }
60     }
61
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.
64         ExprUseVisitor::new(
65             self,
66             &fcx.infcx,
67             def_id.expect_local(),
68             fcx.param_env,
69             &fcx.typeck_results.borrow(),
70         )
71         .consume_body(body);
72     }
73
74     fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) {
75         self.places.consumed.entry(consumer).or_insert_with(|| <_>::default());
76
77         debug!(?consumer, ?target, "mark_consumed");
78         self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
79     }
80
81     fn borrow_place(&mut self, place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>) {
82         self.places
83             .borrowed
84             .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
85
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.
91         //
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).
96         //
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.
103         // Example:
104         //
105         // ```notrust
106         // fn identity<T>(x: &mut T) -> &mut T { x }
107         // let a: A = ...;
108         // let y: &'y mut A = &mut *identity(&'a mut a);
109         //                    ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about
110         // ```
111         //
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
120         // part of the code!
121         let is_deref = place_with_id
122             .place
123             .projections
124             .iter()
125             .any(|Projection { kind, .. }| *kind == ProjectionKind::Deref);
126
127         if let (false, PlaceBase::Rvalue) = (is_deref, place_with_id.place.base) {
128             self.places.borrowed_temporaries.insert(place_with_id.hir_id);
129         }
130     }
131 }
132
133 impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
134     fn consume(
135         &mut self,
136         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
137         diag_expr_id: HirId,
138     ) {
139         let hir = self.tcx.hir();
140         let parent = match hir.find_parent_node(place_with_id.hir_id) {
141             Some(parent) => parent,
142             None => place_with_id.hir_id,
143         };
144         debug!(
145             "consume {:?}; diag_expr_id={}, using parent {}",
146             place_with_id,
147             hir.node_to_string(diag_expr_id),
148             hir.node_to_string(parent)
149         );
150         place_with_id
151             .try_into()
152             .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value));
153     }
154
155     fn borrow(
156         &mut self,
157         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
158         diag_expr_id: HirId,
159         bk: rustc_middle::ty::BorrowKind,
160     ) {
161         debug!(
162             "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \
163             borrow_kind={bk:#?}"
164         );
165
166         self.borrow_place(place_with_id);
167     }
168
169     fn copy(
170         &mut self,
171         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
172         _diag_expr_id: HirId,
173     ) {
174         debug!("copy: place_with_id = {place_with_id:?}");
175
176         self.places
177             .borrowed
178             .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
179
180         // For copied we treat this mostly like a borrow except that we don't add the place
181         // to borrowed_temporaries because the copy is consumed.
182     }
183
184     fn mutate(
185         &mut self,
186         assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
187         diag_expr_id: HirId,
188     ) {
189         debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
190
191         if assignee_place.place.base == PlaceBase::Rvalue
192             && assignee_place.place.projections.is_empty()
193         {
194             // Assigning to an Rvalue is illegal unless done through a dereference. We would have
195             // already gotten a type error, so we will just return here.
196             return;
197         }
198
199         // If the type being assigned needs dropped, then the mutation counts as a borrow
200         // since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
201         //
202         // FIXME(drop-tracking): We need to be more responsible about inference
203         // variables here, since `needs_drop` is a "raw" type query, i.e. it
204         // basically requires types to have been fully resolved.
205         if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) {
206             self.places
207                 .borrowed
208                 .insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
209         }
210     }
211
212     fn bind(
213         &mut self,
214         binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
215         diag_expr_id: HirId,
216     ) {
217         debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
218     }
219
220     fn fake_read(
221         &mut self,
222         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
223         cause: rustc_middle::mir::FakeReadCause,
224         diag_expr_id: HirId,
225     ) {
226         debug!(
227             "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}"
228         );
229
230         // fake reads happen in places like the scrutinee of a match expression.
231         // we treat those as a borrow, much like a copy: the idea is that we are
232         // transiently creating a `&T` ref that we can read from to observe the current
233         // value (this `&T` is immediately dropped afterwards).
234         self.borrow_place(place_with_id);
235     }
236 }