]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/borrow_check/type_check/liveness/trace.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_mir / borrow_check / type_check / liveness / trace.rs
CommitLineData
e74abb32 1use rustc_data_structures::fx::{FxHashMap, FxHashSet};
dfeec247 2use rustc_index::bit_set::HybridBitSet;
74b04a01 3use rustc_infer::infer::canonical::QueryRegionConstraints;
f9f354fc 4use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
ba9703b0
XL
5use rustc_middle::ty::{Ty, TypeFoldable};
6use rustc_trait_selection::traits::query::dropck_outlives::DropckOutlivesResult;
7use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
8use rustc_trait_selection::traits::query::type_op::TypeOp;
b7449926 9use std::rc::Rc;
b7449926 10
f9f354fc 11use crate::dataflow::impls::MaybeInitializedPlaces;
60c5eb7d 12use crate::dataflow::indexes::MovePathIndex;
74b04a01 13use crate::dataflow::move_paths::{HasMoveData, MoveData};
ba9703b0 14use crate::dataflow::ResultsCursor;
60c5eb7d
XL
15
16use crate::borrow_check::{
17 region_infer::values::{self, PointIndex, RegionValueElements},
18 type_check::liveness::local_use_map::LocalUseMap,
19 type_check::liveness::polonius,
20 type_check::NormalizeLocation,
21 type_check::TypeChecker,
22};
23
b7449926
XL
24/// This is the heart of the liveness computation. For each variable X
25/// that requires a liveness computation, it walks over all the uses
26/// of X and does a reverse depth-first search ("trace") through the
27/// MIR. This search stops when we find a definition of that variable.
28/// The points visited in this search is the USE-LIVE set for the variable;
29/// of those points is added to all the regions that appear in the variable's
30/// type.
31///
32/// We then also walks through each *drop* of those variables and does
33/// another search, stopping when we reach a use or definition. This
34/// is the DROP-LIVE set of points. Each of the points in the
35/// DROP-LIVE set are to the liveness sets for regions found in the
36/// `dropck_outlives` result of the variable's type (in particular,
37/// this respects `#[may_dangle]` annotations).
38pub(super) fn trace(
dc9dc135 39 typeck: &mut TypeChecker<'_, 'tcx>,
f9f354fc 40 body: &Body<'tcx>,
b7449926 41 elements: &Rc<RegionValueElements>,
74b04a01 42 flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
b7449926 43 move_data: &MoveData<'tcx>,
532ac7d7 44 live_locals: Vec<Local>,
e74abb32 45 polonius_drop_used: Option<Vec<(Local, Location)>>,
b7449926
XL
46) {
47 debug!("trace()");
48
dc9dc135 49 let local_use_map = &LocalUseMap::build(&live_locals, elements, body);
b7449926
XL
50
51 let cx = LivenessContext {
52 typeck,
dc9dc135 53 body,
b7449926
XL
54 flow_inits,
55 elements,
56 local_use_map,
57 move_data,
b7449926
XL
58 drop_data: FxHashMap::default(),
59 };
60
e74abb32
XL
61 let mut results = LivenessResults::new(cx);
62
63 if let Some(drop_used) = polonius_drop_used {
64 results.add_extra_drop_facts(drop_used, live_locals.iter().copied().collect())
65 }
66
67 results.compute_for_all_locals(live_locals);
b7449926
XL
68}
69
70/// Contextual state for the type-liveness generator.
dc9dc135 71struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
b7449926 72 /// Current type-checker, giving us our inference context etc.
dc9dc135 73 typeck: &'me mut TypeChecker<'typeck, 'tcx>,
b7449926
XL
74
75 /// Defines the `PointIndex` mapping
76 elements: &'me RegionValueElements,
77
78 /// MIR we are analyzing.
f9f354fc 79 body: &'me Body<'tcx>,
b7449926
XL
80
81 /// Mapping to/from the various indices used for initialization tracking.
82 move_data: &'me MoveData<'tcx>,
83
84 /// Cache for the results of `dropck_outlives` query.
85 drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
86
87 /// Results of dataflow tracking which variables (and paths) have been
88 /// initialized.
74b04a01 89 flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'flow, 'tcx>>,
b7449926
XL
90
91 /// Index indicating where each variable is assigned, used, or
92 /// dropped.
532ac7d7 93 local_use_map: &'me LocalUseMap,
b7449926
XL
94}
95
96struct DropData<'tcx> {
97 dropck_result: DropckOutlivesResult<'tcx>,
dc9dc135 98 region_constraint_data: Option<Rc<QueryRegionConstraints<'tcx>>>,
b7449926
XL
99}
100
dc9dc135
XL
101struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
102 cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>,
b7449926
XL
103
104 /// Set of points that define the current local.
0bf4aa26 105 defs: HybridBitSet<PointIndex>,
b7449926
XL
106
107 /// Points where the current variable is "use live" -- meaning
108 /// that there is a future "full use" that may use its value.
0bf4aa26 109 use_live_at: HybridBitSet<PointIndex>,
b7449926
XL
110
111 /// Points where the current variable is "drop live" -- meaning
112 /// that there is no future "full use" that may use its value, but
113 /// there is a future drop.
0bf4aa26 114 drop_live_at: HybridBitSet<PointIndex>,
b7449926
XL
115
116 /// Locations where drops may occur.
117 drop_locations: Vec<Location>,
118
119 /// Stack used when doing (reverse) DFS.
120 stack: Vec<PointIndex>,
121}
122
dc9dc135
XL
123impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
124 fn new(cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>) -> Self {
b7449926
XL
125 let num_points = cx.elements.num_points();
126 LivenessResults {
127 cx,
0bf4aa26
XL
128 defs: HybridBitSet::new_empty(num_points),
129 use_live_at: HybridBitSet::new_empty(num_points),
130 drop_live_at: HybridBitSet::new_empty(num_points),
b7449926
XL
131 drop_locations: vec![],
132 stack: vec![],
133 }
134 }
135
532ac7d7
XL
136 fn compute_for_all_locals(&mut self, live_locals: Vec<Local>) {
137 for local in live_locals {
b7449926 138 self.reset_local_state();
532ac7d7
XL
139 self.add_defs_for(local);
140 self.compute_use_live_points_for(local);
141 self.compute_drop_live_points_for(local);
b7449926 142
dc9dc135 143 let local_ty = self.cx.body.local_decls[local].ty;
b7449926
XL
144
145 if !self.use_live_at.is_empty() {
146 self.cx.add_use_live_facts_for(local_ty, &self.use_live_at);
147 }
148
149 if !self.drop_live_at.is_empty() {
150 self.cx.add_drop_live_facts_for(
151 local,
152 local_ty,
153 &self.drop_locations,
154 &self.drop_live_at,
155 );
156 }
157 }
158 }
159
e74abb32
XL
160 /// Add extra drop facts needed for Polonius.
161 ///
162 /// Add facts for all locals with free regions, since regions may outlive
163 /// the function body only at certain nodes in the CFG.
164 fn add_extra_drop_facts(
165 &mut self,
166 drop_used: Vec<(Local, Location)>,
167 live_locals: FxHashSet<Local>,
168 ) {
169 let locations = HybridBitSet::new_empty(self.cx.elements.num_points());
170
171 for (local, location) in drop_used {
172 if !live_locals.contains(&local) {
173 let local_ty = self.cx.body.local_decls[local].ty;
174 if local_ty.has_free_regions() {
dfeec247 175 self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations);
e74abb32
XL
176 }
177 }
178 }
179 }
180
b7449926
XL
181 /// Clear the value of fields that are "per local variable".
182 fn reset_local_state(&mut self) {
183 self.defs.clear();
184 self.use_live_at.clear();
185 self.drop_live_at.clear();
186 self.drop_locations.clear();
187 assert!(self.stack.is_empty());
188 }
189
190 /// Adds the definitions of `local` into `self.defs`.
532ac7d7
XL
191 fn add_defs_for(&mut self, local: Local) {
192 for def in self.cx.local_use_map.defs(local) {
b7449926
XL
193 debug!("- defined at {:?}", def);
194 self.defs.insert(def);
195 }
196 }
197
9fa01778 198 /// Computes all points where local is "use live" -- meaning its
b7449926 199 /// current value may be used later (except by a drop). This is
532ac7d7 200 /// done by walking backwards from each use of `local` until we
b7449926
XL
201 /// find a `def` of local.
202 ///
532ac7d7
XL
203 /// Requires `add_defs_for(local)` to have been executed.
204 fn compute_use_live_points_for(&mut self, local: Local) {
205 debug!("compute_use_live_points_for(local={:?})", local);
b7449926 206
532ac7d7 207 self.stack.extend(self.cx.local_use_map.uses(local));
b7449926
XL
208 while let Some(p) = self.stack.pop() {
209 if self.defs.contains(p) {
210 continue;
211 }
212
213 if self.use_live_at.insert(p) {
416331ca 214 self.cx.elements.push_predecessors(self.cx.body, p, &mut self.stack)
b7449926
XL
215 }
216 }
217 }
218
9fa01778 219 /// Computes all points where local is "drop live" -- meaning its
b7449926
XL
220 /// current value may be dropped later (but not used). This is
221 /// done by iterating over the drops of `local` where `local` (or
222 /// some subpart of `local`) is initialized. For each such drop,
223 /// we walk backwards until we find a point where `local` is
224 /// either defined or use-live.
225 ///
226 /// Requires `compute_use_live_points_for` and `add_defs_for` to
227 /// have been executed.
532ac7d7
XL
228 fn compute_drop_live_points_for(&mut self, local: Local) {
229 debug!("compute_drop_live_points_for(local={:?})", local);
b7449926 230
b7449926
XL
231 let mpi = self.cx.move_data.rev_lookup.find_local(local);
232 debug!("compute_drop_live_points_for: mpi = {:?}", mpi);
233
234 // Find the drops where `local` is initialized.
532ac7d7 235 for drop_point in self.cx.local_use_map.drops(local) {
b7449926 236 let location = self.cx.elements.to_location(drop_point);
dc9dc135 237 debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);
b7449926
XL
238
239 if self.cx.initialized_at_terminator(location.block, mpi) {
240 if self.drop_live_at.insert(drop_point) {
241 self.drop_locations.push(location);
242 self.stack.push(drop_point);
243 }
244 }
245 }
246
416331ca 247 debug!("compute_drop_live_points_for: drop_locations={:?}", self.drop_locations);
b7449926
XL
248
249 // Reverse DFS. But for drops, we do it a bit differently.
250 // The stack only ever stores *terminators of blocks*. Within
251 // a block, we walk back the statements in an inner loop.
60c5eb7d 252 while let Some(term_point) = self.stack.pop() {
b7449926
XL
253 self.compute_drop_live_points_for_block(mpi, term_point);
254 }
255 }
256
257 /// Executes one iteration of the drop-live analysis loop.
258 ///
259 /// The parameter `mpi` is the `MovePathIndex` of the local variable
260 /// we are currently analyzing.
261 ///
262 /// The point `term_point` represents some terminator in the MIR,
263 /// where the local `mpi` is drop-live on entry to that terminator.
264 ///
265 /// This method adds all drop-live points within the block and --
266 /// where applicable -- pushes the terminators of preceding blocks
267 /// onto `self.stack`.
268 fn compute_drop_live_points_for_block(&mut self, mpi: MovePathIndex, term_point: PointIndex) {
269 debug!(
270 "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})",
271 self.cx.move_data.move_paths[mpi].place,
272 self.cx.elements.to_location(term_point),
273 );
274
275 // We are only invoked with terminators where `mpi` is
276 // drop-live on entry.
277 debug_assert!(self.drop_live_at.contains(term_point));
278
279 // Otherwise, scan backwards through the statements in the
280 // block. One of them may be either a definition or use
281 // live point.
282 let term_location = self.cx.elements.to_location(term_point);
416331ca 283 debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
b7449926
XL
284 let block = term_location.block;
285 let entry_point = self.cx.elements.entry_point(term_location.block);
286 for p in (entry_point..term_point).rev() {
416331ca 287 debug!("compute_drop_live_points_for_block: p = {:?}", self.cx.elements.to_location(p));
b7449926
XL
288
289 if self.defs.contains(p) {
290 debug!("compute_drop_live_points_for_block: def site");
291 return;
292 }
293
294 if self.use_live_at.contains(p) {
295 debug!("compute_drop_live_points_for_block: use-live at {:?}", p);
296 return;
297 }
298
299 if !self.drop_live_at.insert(p) {
300 debug!("compute_drop_live_points_for_block: already drop-live");
301 return;
302 }
303 }
304
60c5eb7d 305 let body = self.cx.body;
f9f354fc 306 for &pred_block in body.predecessors()[block].iter() {
416331ca 307 debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
b7449926
XL
308
309 // Check whether the variable is (at least partially)
310 // initialized at the exit of this predecessor. If so, we
311 // want to enqueue it on our list. If not, go check the
312 // next block.
313 //
314 // Note that we only need to check whether `live_local`
315 // became de-initialized at basic block boundaries. If it
316 // were to become de-initialized within the block, that
317 // would have been a "use-live" transition in the earlier
318 // loop, and we'd have returned already.
319 //
320 // NB. It's possible that the pred-block ends in a call
321 // which stores to the variable; in that case, the
322 // variable may be uninitialized "at exit" because this
323 // call only considers the *unconditional effects* of the
324 // terminator. *But*, in that case, the terminator is also
325 // a *definition* of the variable, in which case we want
326 // to stop the search anyhow. (But see Note 1 below.)
327 if !self.cx.initialized_at_exit(pred_block, mpi) {
328 debug!("compute_drop_live_points_for_block: not initialized");
329 continue;
330 }
331
dc9dc135 332 let pred_term_loc = self.cx.body.terminator_loc(pred_block);
b7449926
XL
333 let pred_term_point = self.cx.elements.point_from_location(pred_term_loc);
334
335 // If the terminator of this predecessor either *assigns*
336 // our value or is a "normal use", then stop.
337 if self.defs.contains(pred_term_point) {
416331ca 338 debug!("compute_drop_live_points_for_block: defined at {:?}", pred_term_loc);
b7449926
XL
339 continue;
340 }
341
342 if self.use_live_at.contains(pred_term_point) {
416331ca 343 debug!("compute_drop_live_points_for_block: use-live at {:?}", pred_term_loc);
b7449926
XL
344 continue;
345 }
346
347 // Otherwise, we are drop-live on entry to the terminator,
348 // so walk it.
349 if self.drop_live_at.insert(pred_term_point) {
350 debug!("compute_drop_live_points_for_block: pushed to stack");
351 self.stack.push(pred_term_point);
352 }
353 }
354
355 // Note 1. There is a weird scenario that you might imagine
356 // being problematic here, but which actually cannot happen.
357 // The problem would be if we had a variable that *is* initialized
358 // (but dead) on entry to the terminator, and where the current value
359 // will be dropped in the case of unwind. In that case, we ought to
360 // consider `X` to be drop-live in between the last use and call.
361 // Here is the example:
362 //
363 // ```
364 // BB0 {
365 // X = ...
366 // use(X); // last use
367 // ... // <-- X ought to be drop-live here
368 // X = call() goto BB1 unwind BB2
369 // }
370 //
371 // BB1 {
372 // DROP(X)
373 // }
374 //
375 // BB2 {
376 // DROP(X)
377 // }
378 // ```
379 //
380 // However, the current code would, when walking back from BB2,
381 // simply stop and never explore BB0. This seems bad! But it turns
382 // out this code is flawed anyway -- note that the existing value of
383 // `X` would leak in the case where unwinding did *not* occur.
384 //
385 // What we *actually* generate is a store to a temporary
386 // for the call (`TMP = call()...`) and then a
387 // `DropAndReplace` to swap that with `X`
388 // (`DropAndReplace` has very particular semantics).
389 }
390}
391
dc9dc135 392impl LivenessContext<'_, '_, '_, 'tcx> {
74b04a01
XL
393 /// Returns `true` if the local variable (or some part of it) is initialized at the current
394 /// cursor position. Callers should call one of the `seek` methods immediately before to point
395 /// the cursor to the desired location.
396 fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
397 let state = self.flow_inits.get();
398 if state.contains(mpi) {
399 return true;
400 }
401
402 let move_paths = &self.flow_inits.analysis().move_data().move_paths;
403 move_paths[mpi].find_descendant(&move_paths, |mpi| state.contains(mpi)).is_some()
404 }
405
9fa01778 406 /// Returns `true` if the local variable (or some part of it) is initialized in
b7449926
XL
407 /// the terminator of `block`. We need to check this to determine if a
408 /// DROP of some local variable will have an effect -- note that
409 /// drops, as they may unwind, are always terminators.
410 fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
f9f354fc 411 self.flow_inits.seek_before_primary_effect(self.body.terminator_loc(block));
74b04a01 412 self.initialized_at_curr_loc(mpi)
b7449926
XL
413 }
414
9fa01778 415 /// Returns `true` if the path `mpi` (or some part of it) is initialized at
b7449926
XL
416 /// the exit of `block`.
417 ///
418 /// **Warning:** Does not account for the result of `Call`
419 /// instructions.
420 fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
f9f354fc 421 self.flow_inits.seek_after_primary_effect(self.body.terminator_loc(block));
74b04a01 422 self.initialized_at_curr_loc(mpi)
b7449926
XL
423 }
424
9fa01778 425 /// Stores the result that all regions in `value` are live for the
b7449926
XL
426 /// points `live_at`.
427 fn add_use_live_facts_for(
428 &mut self,
429 value: impl TypeFoldable<'tcx>,
0bf4aa26 430 live_at: &HybridBitSet<PointIndex>,
b7449926
XL
431 ) {
432 debug!("add_use_live_facts_for(value={:?})", value);
433
e1599b0c 434 Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
b7449926
XL
435 }
436
437 /// Some variable with type `live_ty` is "drop live" at `location`
438 /// -- i.e., it may be dropped later. This means that *some* of
439 /// the regions in its type must be live at `location`. The
440 /// precise set will depend on the dropck constraints, and in
441 /// particular this takes `#[may_dangle]` into account.
442 fn add_drop_live_facts_for(
443 &mut self,
444 dropped_local: Local,
445 dropped_ty: Ty<'tcx>,
446 drop_locations: &[Location],
0bf4aa26 447 live_at: &HybridBitSet<PointIndex>,
b7449926
XL
448 ) {
449 debug!(
450 "add_drop_live_constraint(\
451 dropped_local={:?}, \
452 dropped_ty={:?}, \
453 drop_locations={:?}, \
454 live_at={:?})",
455 dropped_local,
456 dropped_ty,
457 drop_locations,
458 values::location_set_str(self.elements, live_at.iter()),
459 );
460
461 let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
462 let typeck = &mut self.typeck;
463 move || Self::compute_drop_data(typeck, dropped_ty)
464 });
465
466 if let Some(data) = &drop_data.region_constraint_data {
467 for &drop_location in drop_locations {
416331ca
XL
468 self.typeck.push_region_constraints(
469 drop_location.to_locations(),
470 ConstraintCategory::Boring,
471 data,
472 );
b7449926
XL
473 }
474 }
475
476 drop_data.dropck_result.report_overflows(
477 self.typeck.infcx.tcx,
dc9dc135 478 self.body.source_info(*drop_locations.first().unwrap()).span,
b7449926
XL
479 dropped_ty,
480 );
481
482 // All things in the `outlives` array may be touched by
483 // the destructor and must be live at this point.
484 for &kind in &drop_data.dropck_result.kinds {
e1599b0c 485 Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
416331ca 486
74b04a01 487 polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind);
b7449926
XL
488 }
489 }
490
491 fn make_all_regions_live(
492 elements: &RegionValueElements,
dc9dc135 493 typeck: &mut TypeChecker<'_, 'tcx>,
b7449926 494 value: impl TypeFoldable<'tcx>,
0bf4aa26 495 live_at: &HybridBitSet<PointIndex>,
b7449926
XL
496 ) {
497 debug!("make_all_regions_live(value={:?})", value);
498 debug!(
499 "make_all_regions_live: live_at={}",
500 values::location_set_str(elements, live_at.iter()),
501 );
502
503 let tcx = typeck.tcx();
504 tcx.for_each_free_region(&value, |live_region| {
416331ca
XL
505 let live_region_vid =
506 typeck.borrowck_context.universal_regions.to_region_vid(live_region);
507 typeck
508 .borrowck_context
b7449926
XL
509 .constraints
510 .liveness_constraints
511 .add_elements(live_region_vid, live_at);
b7449926
XL
512 });
513 }
514
515 fn compute_drop_data(
dc9dc135 516 typeck: &mut TypeChecker<'_, 'tcx>,
b7449926
XL
517 dropped_ty: Ty<'tcx>,
518 ) -> DropData<'tcx> {
519 debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
520
521 let param_env = typeck.param_env;
416331ca
XL
522 let (dropck_result, region_constraint_data) =
523 param_env.and(DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx).unwrap();
524
525 DropData { dropck_result, region_constraint_data }
b7449926
XL
526 }
527}