]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / generator_interior / drop_ranges / record_consumed_borrow.rs
1 use super::TrackedValue;
2 use crate::{
3 check::FnCtxt,
4 expr_use_visitor::{self, ExprUseVisitor},
5 };
6 use hir::{def_id::DefId, Body, HirId, HirIdMap};
7 use rustc_data_structures::stable_set::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 if !self.places.consumed.contains_key(&consumer) {
76 self.places.consumed.insert(consumer, <_>::default());
77 }
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 parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) {
140 Some(parent) => parent,
141 None => place_with_id.hir_id,
142 };
143 debug!(
144 "consume {:?}; diag_expr_id={:?}, using parent {:?}",
145 place_with_id, diag_expr_id, parent
146 );
147 place_with_id
148 .try_into()
149 .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value));
150 }
151
152 fn borrow(
153 &mut self,
154 place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
155 diag_expr_id: HirId,
156 bk: rustc_middle::ty::BorrowKind,
157 ) {
158 debug!(
159 "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \
160 borrow_kind={bk:?}"
161 );
162
163 self.borrow_place(place_with_id);
164 }
165
166 fn copy(
167 &mut self,
168 place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
169 _diag_expr_id: HirId,
170 ) {
171 debug!("copy: place_with_id = {place_with_id:?}");
172
173 self.places
174 .borrowed
175 .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
176
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.
179 }
180
181 fn mutate(
182 &mut self,
183 assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
184 diag_expr_id: HirId,
185 ) {
186 debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
187
188 if assignee_place.place.base == PlaceBase::Rvalue
189 && assignee_place.place.projections.is_empty()
190 {
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.
193 return;
194 }
195
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) {
199 self.places
200 .borrowed
201 .insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
202 }
203 }
204
205 fn bind(
206 &mut self,
207 binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
208 diag_expr_id: HirId,
209 ) {
210 debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
211 }
212
213 fn fake_read(
214 &mut self,
215 place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
216 cause: rustc_middle::mir::FakeReadCause,
217 diag_expr_id: HirId,
218 ) {
219 debug!(
220 "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}"
221 );
222
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);
228 }
229 }