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.is_some_and(|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. Each region comes from a local's type, so this holds
61/// mostly the locals and drop-liveness specifics about their type, to be used as inputs to the
62/// `LivenessComputation` giving us the region liveness.
63#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for DeferredRegionLiveness<'tcx> {
    #[inline]
    fn default() -> Self {
        DeferredRegionLiveness {
            by_region: ::core::default::Default::default(),
            drop_args_by_local: ::core::default::Default::default(),
        }
    }
}Default)]
64pub(crate) struct DeferredRegionLiveness<'tcx> {
65    /// For each region, the local whose liveness is deferred.
66    ///
67    /// Importantly, because of MIR renumbering, this will always be a 1:1 relationship.
68    by_region: IndexVec<RegionVid, Option<Local>>,
69
70    /// For each deferred local, gets the regions contained within that local at use and drop.
71    drop_args_by_local: IndexVec<Local, Option<Vec<GenericArg<'tcx>>>>,
72}
73
74impl<'tcx> DeferredRegionLiveness<'tcx> {
75    /// We want to defer computing the liveness of the region in the local's type, so we store the
76    /// necessary inputs to do that later.
77    pub(crate) fn defer_local(
78        &mut self,
79        tcx: TyCtxt<'tcx>,
80        universal_regions: &UniversalRegions<'tcx>,
81        local: Local,
82        local_ty: Ty<'tcx>,
83        dropck_kinds: &[GenericArg<'tcx>],
84    ) {
85        // We already have drop data for this local, because we need to register
86        // region constraints eagerly. So, we'll store this so we don't need to
87        // recompute.
88        self.drop_args_by_local.insert(local, dropck_kinds.to_vec());
89
90        // Then, we want to map all the regions contained within this local to
91        // the local itself. Later, when asked for liveness of a given region,
92        // we can trace liveness for the local containing it.
93        tcx.for_each_free_region(&local_ty, |region| {
94            // See note in `VarianceExtractor::record_variance`.
95            if region.is_bound() || region.is_erased() {
96                return;
97            }
98            let vid = universal_regions.to_region_vid(region);
99            // Because of MIR renumbering, we should always have a 1:1 mapping
100            // between a region and a local.
101            let previous = self.by_region.insert(vid, local);
102            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!(
103                previous.is_none(),
104                "{vid:?} is in the type of both {previous:?} and {local:?}, but \
105                MIR renumbering should ensure that this is impossible.",
106            );
107        });
108    }
109
110    /// For a given region, compute the liveness for the local containing it, if it is deferred.
111    #[inline]
112    pub(crate) fn ensure_deferred_liveness(
113        &mut self,
114        region: RegionVid,
115        universal_regions: &UniversalRegions<'tcx>,
116        liveness: &mut LivenessValues,
117        live_region_variances: &mut LiveRegionVariances,
118        comp: &mut LivenessComputation<'_, 'tcx>,
119    ) {
120        let Some(local) = self.by_region.remove(region) else {
121            return;
122        };
123        let Some(drop_args) = self.drop_args_by_local.remove(local) else {
124            return;
125        };
126
127        comp.compute(local, universal_regions, Some(live_region_variances), liveness, || {
128            &drop_args
129        });
130    }
131}