]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
Fix invalid float literal suggestions when recovering an integer
[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::ty::{ParamEnv, TyCtxt};
10 use rustc_middle::{
11     hir::place::{PlaceBase, Projection, ProjectionKind},
12     ty::TypeVisitable,
13 };
14
15 pub(super) fn find_consumed_and_borrowed<'a, 'tcx>(
16     fcx: &'a FnCtxt<'a, 'tcx>,
17     def_id: DefId,
18     body: &'tcx Body<'tcx>,
19 ) -> ConsumedAndBorrowedPlaces {
20     let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx, fcx.param_env);
21     expr_use_visitor.consume_body(fcx, def_id, body);
22     expr_use_visitor.places
23 }
24
25 pub(super) struct ConsumedAndBorrowedPlaces {
26     /// Records the variables/expressions that are dropped by a given expression.
27     ///
28     /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables
29     /// or values that are consumed by that expression.
30     ///
31     /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is
32     /// not considered a drop of `x`, although it would be a drop of `x.y`.
33     pub(super) consumed: HirIdMap<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: FxHashSet<TrackedValue>,
37
38     /// A set of hir-ids of values or variables that are borrowed at some point within the body.
39     pub(super) borrowed_temporaries: FxHashSet<HirId>,
40 }
41
42 /// Works with ExprUseVisitor to find interesting values for the drop range analysis.
43 ///
44 /// Interesting values are those that are either dropped or borrowed. For dropped values, we also
45 /// record the parent expression, which is the point where the drop actually takes place.
46 struct ExprUseDelegate<'tcx> {
47     tcx: TyCtxt<'tcx>,
48     param_env: ParamEnv<'tcx>,
49     places: ConsumedAndBorrowedPlaces,
50 }
51
52 impl<'tcx> ExprUseDelegate<'tcx> {
53     fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
54         Self {
55             tcx,
56             param_env,
57             places: ConsumedAndBorrowedPlaces {
58                 consumed: <_>::default(),
59                 borrowed: <_>::default(),
60                 borrowed_temporaries: <_>::default(),
61             },
62         }
63     }
64
65     fn consume_body(&mut self, fcx: &'_ FnCtxt<'_, 'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) {
66         // Run ExprUseVisitor to find where values are consumed.
67         ExprUseVisitor::new(
68             self,
69             &fcx.infcx,
70             def_id.expect_local(),
71             fcx.param_env,
72             &fcx.typeck_results.borrow(),
73         )
74         .consume_body(body);
75     }
76
77     fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) {
78         self.places.consumed.entry(consumer).or_insert_with(|| <_>::default());
79
80         debug!(?consumer, ?target, "mark_consumed");
81         self.places.consumed.get_mut(&consumer).map(|places| places.insert(target));
82     }
83
84     fn borrow_place(&mut self, place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>) {
85         self.places
86             .borrowed
87             .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
88
89         // Ordinarily a value is consumed by it's parent, but in the special case of a
90         // borrowed RValue, we create a reference that lives as long as the temporary scope
91         // for that expression (typically, the innermost statement, but sometimes the enclosing
92         // block). We record this fact here so that later in generator_interior
93         // we can use the correct scope.
94         //
95         // We special case borrows through a dereference (`&*x`, `&mut *x` where `x` is
96         // some rvalue expression), since these are essentially a copy of a pointer.
97         // In other words, this borrow does not refer to the
98         // temporary (`*x`), but to the referent (whatever `x` is a borrow of).
99         //
100         // We were considering that we might encounter problems down the line if somehow,
101         // some part of the compiler were to look at this result and try to use it to
102         // drive a borrowck-like analysis (this does not currently happen, as of this writing).
103         // But even this should be fine, because the lifetime of the dereferenced reference
104         // found in the rvalue is only significant as an intermediate 'link' to the value we
105         // are producing, and we separately track whether that value is live over a yield.
106         // Example:
107         //
108         // ```notrust
109         // fn identity<T>(x: &mut T) -> &mut T { x }
110         // let a: A = ...;
111         // let y: &'y mut A = &mut *identity(&'a mut a);
112         //                    ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about
113         // ```
114         //
115         // The expression `*identity(...)` is a deref of an rvalue,
116         // where the `identity(...)` (the rvalue) produces a return type
117         // of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
118         // `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
119         // `a` will be considered borrowed).  Other parts of the code will ensure
120         // that if `y` is live over a yield, `&'y mut A` appears in the generator
121         // state. If `'y` is live, then any sound region analysis must conclude
122         // that `'a` is also live. So if this causes a bug, blame some other
123         // part of the code!
124         let is_deref = place_with_id
125             .place
126             .projections
127             .iter()
128             .any(|Projection { kind, .. }| *kind == ProjectionKind::Deref);
129
130         if let (false, PlaceBase::Rvalue) = (is_deref, place_with_id.place.base) {
131             self.places.borrowed_temporaries.insert(place_with_id.hir_id);
132         }
133     }
134 }
135
136 impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
137     fn consume(
138         &mut self,
139         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
140         diag_expr_id: HirId,
141     ) {
142         let hir = self.tcx.hir();
143         let parent = match hir.opt_parent_id(place_with_id.hir_id) {
144             Some(parent) => parent,
145             None => place_with_id.hir_id,
146         };
147         debug!(
148             "consume {:?}; diag_expr_id={}, using parent {}",
149             place_with_id,
150             hir.node_to_string(diag_expr_id),
151             hir.node_to_string(parent)
152         );
153         place_with_id
154             .try_into()
155             .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value));
156     }
157
158     fn borrow(
159         &mut self,
160         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
161         diag_expr_id: HirId,
162         bk: rustc_middle::ty::BorrowKind,
163     ) {
164         debug!(
165             "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \
166             borrow_kind={bk:#?}"
167         );
168
169         self.borrow_place(place_with_id);
170     }
171
172     fn copy(
173         &mut self,
174         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
175         _diag_expr_id: HirId,
176     ) {
177         debug!("copy: place_with_id = {place_with_id:?}");
178
179         self.places
180             .borrowed
181             .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
182
183         // For copied we treat this mostly like a borrow except that we don't add the place
184         // to borrowed_temporaries because the copy is consumed.
185     }
186
187     fn mutate(
188         &mut self,
189         assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
190         diag_expr_id: HirId,
191     ) {
192         debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}");
193
194         if assignee_place.place.base == PlaceBase::Rvalue
195             && assignee_place.place.projections.is_empty()
196         {
197             // Assigning to an Rvalue is illegal unless done through a dereference. We would have
198             // already gotten a type error, so we will just return here.
199             return;
200         }
201
202         // If the type being assigned needs dropped, then the mutation counts as a borrow
203         // since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
204         let ty = self.tcx.erase_regions(assignee_place.place.base_ty);
205         if ty.needs_infer() {
206             self.tcx.sess.delay_span_bug(
207                 self.tcx.hir().span(assignee_place.hir_id),
208                 &format!("inference variables in {ty}"),
209             );
210         } else if ty.needs_drop(self.tcx, self.param_env) {
211             self.places
212                 .borrowed
213                 .insert(TrackedValue::from_place_with_projections_allowed(assignee_place));
214         }
215     }
216
217     fn bind(
218         &mut self,
219         binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>,
220         diag_expr_id: HirId,
221     ) {
222         debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}");
223     }
224
225     fn fake_read(
226         &mut self,
227         place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>,
228         cause: rustc_middle::mir::FakeReadCause,
229         diag_expr_id: HirId,
230     ) {
231         debug!(
232             "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}"
233         );
234
235         // fake reads happen in places like the scrutinee of a match expression.
236         // we treat those as a borrow, much like a copy: the idea is that we are
237         // transiently creating a `&T` ref that we can read from to observe the current
238         // value (this `&T` is immediately dropped afterwards).
239         self.borrow_place(place_with_id);
240     }
241 }