rustc_borrowck/type_check/liveness/
trace.rs1use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
2use rustc_index::bit_set::DenseBitSet;
3use rustc_index::interval::IntervalSet;
4use rustc_infer::infer::canonical::QueryRegionConstraints;
5use rustc_infer::infer::outlives::for_liveness;
6use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location};
7use rustc_middle::traits::query::DropckOutlivesResult;
8use rustc_middle::ty::relate::Relate;
9use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
10use rustc_mir_dataflow::ResultsCursor;
11use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
12use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
13use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
14use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
15use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
16use rustc_trait_selection::traits::ObligationCtxt;
17use rustc_trait_selection::traits::query::dropck_outlives;
18use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
19use tracing::debug;
20
21use crate::polonius;
22use crate::region_infer::values;
23use crate::type_check::liveness::local_use_map::LocalUseMap;
24use crate::type_check::{NormalizeLocation, TypeChecker};
25
26pub(super) fn trace<'a, 'tcx>(
41 typeck: &mut TypeChecker<'_, 'tcx>,
42 location_map: &DenseLocationMap,
43 flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
44 move_data: &MoveData<'tcx>,
45 relevant_live_locals: Vec<Local>,
46 boring_locals: Vec<Local>,
47) {
48 let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, typeck.body);
49 let cx = LivenessContext {
50 typeck,
51 flow_inits,
52 location_map,
53 local_use_map,
54 move_data,
55 drop_data: FxIndexMap::default(),
56 };
57
58 let mut results = LivenessResults::new(cx);
59
60 results.add_extra_drop_facts(&relevant_live_locals);
61
62 results.compute_for_all_locals(relevant_live_locals);
63
64 results.dropck_boring_locals(boring_locals);
65}
66
67struct LivenessContext<'a, 'typeck, 'b, 'tcx> {
69 typeck: &'a mut TypeChecker<'typeck, 'tcx>,
73
74 location_map: &'a DenseLocationMap,
76
77 move_data: &'a MoveData<'tcx>,
79
80 drop_data: FxIndexMap<Ty<'tcx>, DropData<'tcx>>,
82
83 flow_inits: ResultsCursor<'b, 'tcx, MaybeInitializedPlaces<'b, 'tcx>>,
86
87 local_use_map: &'a LocalUseMap,
90}
91
92struct DropData<'tcx> {
93 dropck_result: DropckOutlivesResult<'tcx>,
94 region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
95}
96
97struct LivenessResults<'a, 'typeck, 'b, 'tcx> {
98 cx: LivenessContext<'a, 'typeck, 'b, 'tcx>,
99
100 defs: DenseBitSet<PointIndex>,
102
103 use_live_at: IntervalSet<PointIndex>,
106
107 drop_live_at: IntervalSet<PointIndex>,
111
112 drop_locations: Vec<Location>,
114
115 stack: Vec<PointIndex>,
117}
118
119impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
120 fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self {
121 let num_points = cx.location_map.num_points();
122 LivenessResults {
123 cx,
124 defs: DenseBitSet::new_empty(num_points),
125 use_live_at: IntervalSet::new(num_points),
126 drop_live_at: IntervalSet::new(num_points),
127 drop_locations: vec![],
128 stack: vec![],
129 }
130 }
131
132 fn compute_for_all_locals(&mut self, relevant_live_locals: Vec<Local>) {
133 for local in relevant_live_locals {
134 self.reset_local_state();
135 self.add_defs_for(local);
136 self.compute_use_live_points_for(local);
137 self.compute_drop_live_points_for(local);
138
139 let local_ty = self.cx.body().local_decls[local].ty;
140
141 if !self.use_live_at.is_empty() {
142 self.cx.add_use_live_facts_for(local_ty, &self.use_live_at);
143 }
144
145 if !self.drop_live_at.is_empty() {
146 self.cx.add_drop_live_facts_for(
147 local,
148 local_ty,
149 &self.drop_locations,
150 &self.drop_live_at,
151 );
152 }
153 }
154 }
155
156 fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) {
163 for local in boring_locals {
164 let local_ty = self.cx.body().local_decls[local].ty;
165 let local_span = self.cx.body().local_decls[local].source_info.span;
166 let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({
167 let typeck = &self.cx.typeck;
168 move || LivenessContext::compute_drop_data(typeck, local_ty, local_span)
169 });
170
171 drop_data.dropck_result.report_overflows(
172 self.cx.typeck.infcx.tcx,
173 self.cx.typeck.body.local_decls[local].source_info.span,
174 local_ty,
175 );
176 }
177 }
178
179 fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) {
184 let Some(facts) = self.cx.typeck.polonius_facts.as_ref() else { return };
194 let facts_to_add: Vec<_> = {
195 let relevant_live_locals: FxIndexSet<_> =
196 relevant_live_locals.iter().copied().collect();
197
198 facts
199 .var_dropped_at
200 .iter()
201 .filter_map(|&(local, location_index)| {
202 let local_ty = self.cx.body().local_decls[local].ty;
203 if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
204 return None;
205 }
206
207 let location = self.cx.typeck.location_table.to_location(location_index);
208 Some((local, local_ty, location))
209 })
210 .collect()
211 };
212
213 let live_at = IntervalSet::new(self.cx.location_map.num_points());
214 for (local, local_ty, location) in facts_to_add {
215 self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at);
216 }
217 }
218
219 fn reset_local_state(&mut self) {
221 self.defs.clear();
222 self.use_live_at.clear();
223 self.drop_live_at.clear();
224 self.drop_locations.clear();
225 assert!(self.stack.is_empty());
226 }
227
228 fn add_defs_for(&mut self, local: Local) {
230 for def in self.cx.local_use_map.defs(local) {
231 debug!("- defined at {:?}", def);
232 self.defs.insert(def);
233 }
234 }
235
236 fn compute_use_live_points_for(&mut self, local: Local) {
243 debug!("compute_use_live_points_for(local={:?})", local);
244
245 self.stack.extend(self.cx.local_use_map.uses(local));
246 while let Some(p) = self.stack.pop() {
247 let block_start = self.cx.location_map.to_block_start(p);
253 let previous_defs = self.defs.last_set_in(block_start..=p);
254 let previous_live_at = self.use_live_at.last_set_in(block_start..=p);
255
256 let exclusive_start = match (previous_defs, previous_live_at) {
257 (Some(a), Some(b)) => Some(std::cmp::max(a, b)),
258 (Some(a), None) | (None, Some(a)) => Some(a),
259 (None, None) => None,
260 };
261
262 if let Some(exclusive) = exclusive_start {
263 self.use_live_at.insert_range(exclusive + 1..=p);
264
265 continue;
268 } else {
269 self.use_live_at.insert_range(block_start..=p);
271
272 let block = self.cx.location_map.to_location(block_start).block;
277 self.stack.extend(
278 self.cx.body().basic_blocks.predecessors()[block]
279 .iter()
280 .map(|&pred_bb| self.cx.body().terminator_loc(pred_bb))
281 .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)),
282 );
283 }
284 }
285 }
286
287 fn compute_drop_live_points_for(&mut self, local: Local) {
297 debug!("compute_drop_live_points_for(local={:?})", local);
298
299 let Some(mpi) = self.cx.move_data.rev_lookup.find_local(local) else { return };
300 debug!("compute_drop_live_points_for: mpi = {:?}", mpi);
301
302 for drop_point in self.cx.local_use_map.drops(local) {
304 let location = self.cx.location_map.to_location(drop_point);
305 debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,);
306
307 if self.cx.initialized_at_terminator(location.block, mpi)
308 && self.drop_live_at.insert(drop_point)
309 {
310 self.drop_locations.push(location);
311 self.stack.push(drop_point);
312 }
313 }
314
315 debug!("compute_drop_live_points_for: drop_locations={:?}", self.drop_locations);
316
317 while let Some(term_point) = self.stack.pop() {
321 self.compute_drop_live_points_for_block(mpi, term_point);
322 }
323 }
324
325 fn compute_drop_live_points_for_block(&mut self, mpi: MovePathIndex, term_point: PointIndex) {
337 debug!(
338 "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})",
339 self.cx.move_data.move_paths[mpi].place,
340 self.cx.location_map.to_location(term_point),
341 );
342
343 debug_assert!(self.drop_live_at.contains(term_point));
346
347 let term_location = self.cx.location_map.to_location(term_point);
351 debug_assert_eq!(self.cx.body().terminator_loc(term_location.block), term_location,);
352 let block = term_location.block;
353 let entry_point = self.cx.location_map.entry_point(term_location.block);
354 for p in (entry_point..term_point).rev() {
355 debug!(
356 "compute_drop_live_points_for_block: p = {:?}",
357 self.cx.location_map.to_location(p)
358 );
359
360 if self.defs.contains(p) {
361 debug!("compute_drop_live_points_for_block: def site");
362 return;
363 }
364
365 if self.use_live_at.contains(p) {
366 debug!("compute_drop_live_points_for_block: use-live at {:?}", p);
367 return;
368 }
369
370 if !self.drop_live_at.insert(p) {
371 debug!("compute_drop_live_points_for_block: already drop-live");
372 return;
373 }
374 }
375
376 let body = self.cx.typeck.body;
377 for &pred_block in body.basic_blocks.predecessors()[block].iter() {
378 debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,);
379
380 if !self.cx.initialized_at_exit(pred_block, mpi) {
399 debug!("compute_drop_live_points_for_block: not initialized");
400 continue;
401 }
402
403 let pred_term_loc = self.cx.body().terminator_loc(pred_block);
404 let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc);
405
406 if self.defs.contains(pred_term_point) {
409 debug!("compute_drop_live_points_for_block: defined at {:?}", pred_term_loc);
410 continue;
411 }
412
413 if self.use_live_at.contains(pred_term_point) {
414 debug!("compute_drop_live_points_for_block: use-live at {:?}", pred_term_loc);
415 continue;
416 }
417
418 if self.drop_live_at.insert(pred_term_point) {
421 debug!("compute_drop_live_points_for_block: pushed to stack");
422 self.stack.push(pred_term_point);
423 }
424 }
425
426 }
460}
461
462impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
463 fn body(&self) -> &Body<'tcx> {
464 self.typeck.body
465 }
466 fn initialized_at_curr_loc(&self, mpi: MovePathIndex) -> bool {
470 let state = self.flow_inits.get();
471 if state.contains(mpi) {
472 return true;
473 }
474
475 let move_paths = &self.flow_inits.analysis().move_data().move_paths;
476 move_paths[mpi].find_descendant(move_paths, |mpi| state.contains(mpi)).is_some()
477 }
478
479 fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
484 self.flow_inits.seek_before_primary_effect(self.body().terminator_loc(block));
485 self.initialized_at_curr_loc(mpi)
486 }
487
488 fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool {
494 self.flow_inits.seek_after_primary_effect(self.body().terminator_loc(block));
495 self.initialized_at_curr_loc(mpi)
496 }
497
498 fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet<PointIndex>) {
501 debug!("add_use_live_facts_for(value={:?})", value);
502 Self::make_all_regions_live(self.location_map, self.typeck, value, live_at);
503 }
504
505 fn add_drop_live_facts_for(
511 &mut self,
512 dropped_local: Local,
513 dropped_ty: Ty<'tcx>,
514 drop_locations: &[Location],
515 live_at: &IntervalSet<PointIndex>,
516 ) {
517 debug!(
518 "add_drop_live_constraint(\
519 dropped_local={:?}, \
520 dropped_ty={:?}, \
521 drop_locations={:?}, \
522 live_at={:?})",
523 dropped_local,
524 dropped_ty,
525 drop_locations,
526 values::pretty_print_points(self.location_map, live_at.iter()),
527 );
528
529 let local_span = self.body().local_decls()[dropped_local].source_info.span;
530 let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
531 let typeck = &self.typeck;
532 move || Self::compute_drop_data(typeck, dropped_ty, local_span)
533 });
534
535 if let Some(data) = &drop_data.region_constraint_data {
536 for &drop_location in drop_locations {
537 self.typeck.push_region_constraints(
538 drop_location.to_locations(),
539 ConstraintCategory::Boring,
540 data,
541 );
542 }
543 }
544
545 drop_data.dropck_result.report_overflows(
546 self.typeck.infcx.tcx,
547 self.typeck.body.source_info(*drop_locations.first().unwrap()).span,
548 dropped_ty,
549 );
550
551 for &kind in &drop_data.dropck_result.kinds {
554 Self::make_all_regions_live(self.location_map, self.typeck, kind, live_at);
555 polonius::legacy::emit_drop_facts(
556 self.typeck.tcx(),
557 dropped_local,
558 &kind,
559 self.typeck.universal_regions,
560 self.typeck.polonius_facts,
561 );
562 }
563 }
564
565 fn make_all_regions_live(
566 location_map: &DenseLocationMap,
567 typeck: &mut TypeChecker<'_, 'tcx>,
568 value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
569 live_at: &IntervalSet<PointIndex>,
570 ) {
571 debug!("make_all_regions_live(value={:?})", value);
572 debug!(
573 "make_all_regions_live: live_at={}",
574 values::pretty_print_points(location_map, live_at.iter()),
575 );
576
577 value.visit_with(&mut for_liveness::FreeRegionsVisitor {
578 tcx: typeck.tcx(),
579 param_env: typeck.infcx.param_env,
580 op: |r| {
581 let live_region_vid = typeck.universal_regions.to_region_vid(r);
582
583 typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at);
584 },
585 });
586
587 if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() {
589 polonius_liveness.record_live_region_variance(
590 typeck.infcx.tcx,
591 typeck.universal_regions,
592 value,
593 );
594 }
595 }
596
597 fn compute_drop_data(
598 typeck: &TypeChecker<'_, 'tcx>,
599 dropped_ty: Ty<'tcx>,
600 span: Span,
601 ) -> DropData<'tcx> {
602 debug!("compute_drop_data(dropped_ty={:?})", dropped_ty);
603
604 let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty });
605
606 match op.fully_perform(typeck.infcx, DUMMY_SP) {
607 Ok(TypeOpOutput { output, constraints, .. }) => {
608 DropData { dropck_result: output, region_constraint_data: constraints }
609 }
610 Err(ErrorGuaranteed { .. }) => {
611 typeck.infcx.probe(|_| {
619 let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx);
620 let errors = match dropck_outlives::compute_dropck_outlives_with_errors(
621 &ocx, op, span,
622 ) {
623 Ok(_) => ocx.select_all_or_error(),
624 Err(e) => {
625 if e.is_empty() {
626 ocx.select_all_or_error()
627 } else {
628 e
629 }
630 }
631 };
632
633 if !errors.is_empty() {
636 typeck.infcx.err_ctxt().report_fulfillment_errors(errors);
637 }
638 });
639 DropData { dropck_result: Default::default(), region_constraint_data: None }
640 }
641 }
642 }
643}