Skip to main content

rustc_borrowck/polonius/
liveness.rs

1use rustc_index::IndexVec;
2use rustc_index::interval::{IntervalSet, SparseIntervalMatrix};
3use rustc_middle::mir::Local;
4use rustc_middle::ty::{GenericArg, RegionVid, Ty, TyCtxt};
5use rustc_mir_dataflow::points::PointIndex;
6
7use crate::polonius::{ConstraintDirection, LiveRegionVariances};
8use crate::region_infer::values::LivenessValues;
9use crate::type_check::liveness::LivenessComputation;
10use crate::universal_regions::UniversalRegions;
11
12/// The source of liveness information for a given region.
13pub(super) trait LivenessSource {
14    fn liveness_for_region(&mut self, region: RegionVid) -> RegionLiveness<'_>;
15}
16
17/// For a given region, the relevant liveness and variance information.
18pub(super) struct RegionLiveness<'a> {
19    pub(super) direction: ConstraintDirection,
20    live_points: Option<&'a IntervalSet<PointIndex>>,
21}
22
23impl<'a> RegionLiveness<'a> {
24    #[inline]
25    pub(super) fn new<'tcx>(
26        region: RegionVid,
27        live_region_variances: &LiveRegionVariances,
28        universal_regions: &UniversalRegions<'tcx>,
29        live_points: &'a SparseIntervalMatrix<RegionVid, PointIndex>,
30    ) -> Self {
31        // Universal regions propagate loans along the CFG, i.e. forwards only.
32        let is_universal_region = universal_regions.is_universal_region(region);
33
34        // Note: there currently are cases related to promoted and const generics, where we don't yet
35        // have variance information (possibly about temporary regions created when typeck sanitizes the
36        // promoteds). Until that is done, we conservatively fallback to maximizing reachability by
37        // adding a bidirectional edge here. This will not limit traversal whatsoever, and thus
38        // propagate liveness when needed.
39        //
40        // FIXME: add the missing variance information and remove this fallback bidirectional edge.
41        let direction = if is_universal_region {
42            ConstraintDirection::Forward
43        } else {
44            live_region_variances
45                .get(region)
46                .copied()
47                .flatten()
48                .unwrap_or(ConstraintDirection::Bidirectional)
49        };
50        let live_points = live_points.row(region);
51        Self { direction, live_points }
52    }
53
54    pub(super) fn is_live_at(&self, point: PointIndex) -> bool {
55        self.live_points.map_or(false, |points| points.contains(point))
56    }
57}
58
59/// The data needed to compute region liveness on-demand while traversing the localized outlives
60/// constraint graph to compute loan liveness.
61#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for DeferredLocals<'tcx> {
    #[inline]
    fn default() -> Self {
        Self {
            by_region: ::core::default::Default::default(),
            drop_args_by_local: ::core::default::Default::default(),
        }
    }
}Default)]
62pub(crate) struct DeferredLocals<'tcx> {
63    /// For each region, the local whose liveness is deferred.
64    ///
65    /// Importantly, because of MIR renumbering, this will always be a 1:1 relationship.
66    by_region: IndexVec<RegionVid, Option<Local>>,
67
68    /// For each deferred local, gets the regions contained within that local at use and drop.
69    drop_args_by_local: IndexVec<Local, Option<Vec<GenericArg<'tcx>>>>,
70}
71
72impl<'tcx> DeferredLocals<'tcx> {
73    pub(crate) fn defer_local(
74        &mut self,
75        tcx: TyCtxt<'tcx>,
76        universal_regions: &UniversalRegions<'tcx>,
77        local: Local,
78        local_ty: Ty<'tcx>,
79        dropck_kinds: &[GenericArg<'tcx>],
80    ) {
81        // We already have drop data for this local, because we need to register
82        // region constraints eagerly. So, we'll store this so we don't need to
83        // recompute.
84        self.drop_args_by_local.insert(local, dropck_kinds.to_vec());
85
86        // Then, we want to map all the regions contained within this local to
87        // the local itself. Later, when asked for liveness of a given region,
88        // we can trace liveness for the local containing it.
89        let by_region = &mut self.by_region;
90        tcx.for_each_free_region(&local_ty, |region| {
91            // See note in `VarianceExtractor::record_variance`.
92            if region.is_bound() || region.is_erased() {
93                return;
94            }
95            let vid = universal_regions.to_region_vid(region);
96            // Because of MIR renumbering, we should always have a 1:1 mapping
97            // between a region and a local.
98            let previous = by_region.insert(vid, local);
99            if true {
    if !previous.is_none() {
        {
            ::core::panicking::panic_fmt(format_args!("{0:?} is in the type of both {1:?} and {2:?}, but MIR renumbering should ensure that this is impossible.",
                    vid, previous, local));
        }
    };
};debug_assert!(
100                previous.is_none(),
101                "{vid:?} is in the type of both {previous:?} and {local:?}, but \
102                MIR renumbering should ensure that this is impossible.",
103            );
104        });
105    }
106
107    /// For a given region, compute the liveness for the local containing it, if it is deferred.
108    #[inline]
109    pub(crate) fn compute_deferred_local(
110        &mut self,
111        region: RegionVid,
112        universal_regions: &UniversalRegions<'tcx>,
113        liveness: &mut LivenessValues,
114        live_region_variances: &mut LiveRegionVariances,
115        comp: &mut LivenessComputation<'_, 'tcx>,
116    ) {
117        let Some(local) = self.by_region.remove(region) else {
118            return;
119        };
120        let Some(drop_args) = self.drop_args_by_local.remove(local) else {
121            return;
122        };
123
124        comp.compute(local, universal_regions, Some(live_region_variances), liveness, || {
125            &drop_args
126        });
127    }
128}