rustc_borrowck/region_infer/
values.rs

1use std::fmt::Debug;
2use std::rc::Rc;
3
4use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
5use rustc_index::Idx;
6use rustc_index::bit_set::SparseBitMatrix;
7use rustc_index::interval::{IntervalSet, SparseIntervalMatrix};
8use rustc_middle::mir::{BasicBlock, Location};
9use rustc_middle::ty::{self, RegionVid};
10use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
11use tracing::debug;
12
13use crate::BorrowIndex;
14use crate::polonius::LiveLoans;
15
16rustc_index::newtype_index! {
17    /// A single integer representing a `ty::Placeholder`.
18    #[debug_format = "PlaceholderIndex({})"]
19    pub(crate) struct PlaceholderIndex {}
20}
21
22/// An individual element in a region value -- the value of a
23/// particular region variable consists of a set of these elements.
24#[derive(Debug, Clone)]
25pub(crate) enum RegionElement {
26    /// A point in the control-flow graph.
27    Location(Location),
28
29    /// A universally quantified region from the root universe (e.g.,
30    /// a lifetime parameter).
31    RootUniversalRegion(RegionVid),
32
33    /// A placeholder (e.g., instantiated from a `for<'a> fn(&'a u32)`
34    /// type).
35    PlaceholderRegion(ty::PlaceholderRegion),
36}
37
38/// Records the CFG locations where each region is live. When we initially compute liveness, we use
39/// an interval matrix storing liveness ranges for each region-vid.
40pub(crate) struct LivenessValues {
41    /// The map from locations to points.
42    location_map: Rc<DenseLocationMap>,
43
44    /// Which regions are live. This is exclusive with the fine-grained tracking in `points`, and
45    /// currently only used for validating promoteds (which don't care about more precise tracking).
46    live_regions: Option<FxHashSet<RegionVid>>,
47
48    /// For each region: the points where it is live.
49    ///
50    /// This is not initialized for promoteds, because we don't care *where* within a promoted a
51    /// region is live, only that it is.
52    points: Option<SparseIntervalMatrix<RegionVid, PointIndex>>,
53
54    /// When using `-Zpolonius=next`, the set of loans that are live at a given point in the CFG.
55    live_loans: Option<LiveLoans>,
56}
57
58impl LivenessValues {
59    /// Create an empty map of regions to locations where they're live.
60    pub(crate) fn with_specific_points(location_map: Rc<DenseLocationMap>) -> Self {
61        LivenessValues {
62            live_regions: None,
63            points: Some(SparseIntervalMatrix::new(location_map.num_points())),
64            location_map,
65            live_loans: None,
66        }
67    }
68
69    /// Create an empty map of regions to locations where they're live.
70    ///
71    /// Unlike `with_specific_points`, does not track exact locations where something is live, only
72    /// which regions are live.
73    pub(crate) fn without_specific_points(location_map: Rc<DenseLocationMap>) -> Self {
74        LivenessValues {
75            live_regions: Some(Default::default()),
76            points: None,
77            location_map,
78            live_loans: None,
79        }
80    }
81
82    /// Returns the liveness matrix of points where each region is live. Panics if the liveness
83    /// values have been created without any per-point data (that is, for promoteds).
84    pub(crate) fn points(&self) -> &SparseIntervalMatrix<RegionVid, PointIndex> {
85        self.points
86            .as_ref()
87            .expect("this `LivenessValues` wasn't created using `with_specific_points`")
88    }
89
90    /// Iterate through each region that has a value in this set.
91    pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
92        self.points.as_ref().expect("use with_specific_points").rows()
93    }
94
95    /// Iterate through each region that has a value in this set.
96    // We are passing query instability implications to the caller.
97    #[rustc_lint_query_instability]
98    #[allow(rustc::potential_query_instability)]
99    pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> + '_ {
100        self.live_regions.as_ref().unwrap().iter().copied()
101    }
102
103    /// Records `region` as being live at the given `location`.
104    pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) {
105        let point = self.location_map.point_from_location(location);
106        debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location);
107        if let Some(points) = &mut self.points {
108            points.insert(region, point);
109        } else if self.location_map.point_in_range(point) {
110            self.live_regions.as_mut().unwrap().insert(region);
111        }
112    }
113
114    /// Records `region` as being live at all the given `points`.
115    pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet<PointIndex>) {
116        debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points);
117        if let Some(this) = &mut self.points {
118            this.union_row(region, points);
119        } else if points.iter().any(|point| self.location_map.point_in_range(point)) {
120            self.live_regions.as_mut().unwrap().insert(region);
121        }
122    }
123
124    /// Records `region` as being live at all the control-flow points.
125    pub(crate) fn add_all_points(&mut self, region: RegionVid) {
126        if let Some(points) = &mut self.points {
127            points.insert_all_into_row(region);
128        } else {
129            self.live_regions.as_mut().unwrap().insert(region);
130        }
131    }
132
133    /// Returns whether `region` is marked live at the given `location`.
134    pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool {
135        let point = self.location_map.point_from_location(location);
136        if let Some(points) = &self.points {
137            points.row(region).is_some_and(|r| r.contains(point))
138        } else {
139            unreachable!(
140                "Should be using LivenessValues::with_specific_points to ask whether live at a location"
141            )
142        }
143    }
144
145    /// Returns an iterator of all the points where `region` is live.
146    fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ {
147        let Some(points) = &self.points else {
148            unreachable!(
149                "Should be using LivenessValues::with_specific_points to ask whether live at a location"
150            )
151        };
152        points
153            .row(region)
154            .into_iter()
155            .flat_map(|set| set.iter())
156            .take_while(|&p| self.location_map.point_in_range(p))
157    }
158
159    /// For debugging purposes, returns a pretty-printed string of the points where the `region` is
160    /// live.
161    pub(crate) fn pretty_print_live_points(&self, region: RegionVid) -> String {
162        pretty_print_region_elements(
163            self.live_points(region)
164                .map(|p| RegionElement::Location(self.location_map.to_location(p))),
165        )
166    }
167
168    #[inline]
169    pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
170        self.location_map.point_from_location(location)
171    }
172
173    #[inline]
174    pub(crate) fn location_from_point(&self, point: PointIndex) -> Location {
175        self.location_map.to_location(point)
176    }
177
178    /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active
179    /// loans dataflow computations.
180    pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) {
181        self.live_loans = Some(live_loans);
182    }
183
184    /// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
185    pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
186        self.live_loans
187            .as_ref()
188            .expect("Accessing live loans requires `-Zpolonius=next`")
189            .contains(point, loan_idx)
190    }
191}
192
193/// Maps from `ty::PlaceholderRegion` values that are used in the rest of
194/// rustc to the internal `PlaceholderIndex` values that are used in
195/// NLL.
196#[derive(Debug, Default)]
197pub(crate) struct PlaceholderIndices {
198    indices: FxIndexSet<ty::PlaceholderRegion>,
199}
200
201impl PlaceholderIndices {
202    /// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion`
203    pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
204        let (index, _) = self.indices.insert_full(placeholder);
205        index.into()
206    }
207
208    pub(crate) fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
209        self.indices.get_index_of(&placeholder).unwrap().into()
210    }
211
212    pub(crate) fn lookup_placeholder(
213        &self,
214        placeholder: PlaceholderIndex,
215    ) -> ty::PlaceholderRegion {
216        self.indices[placeholder.index()]
217    }
218
219    pub(crate) fn len(&self) -> usize {
220        self.indices.len()
221    }
222}
223
224/// Stores the full values for a set of regions (in contrast to
225/// `LivenessValues`, which only stores those points in the where a
226/// region is live). The full value for a region may contain points in
227/// the CFG, but also free regions as well as bound universe
228/// placeholders.
229///
230/// Example:
231///
232/// ```text
233/// fn foo(x: &'a u32) -> &'a u32 {
234///    let y: &'0 u32 = x; // let's call this `'0`
235///    y
236/// }
237/// ```
238///
239/// Here, the variable `'0` would contain the free region `'a`,
240/// because (since it is returned) it must live for at least `'a`. But
241/// it would also contain various points from within the function.
242pub(crate) struct RegionValues<N: Idx> {
243    location_map: Rc<DenseLocationMap>,
244    placeholder_indices: PlaceholderIndices,
245    points: SparseIntervalMatrix<N, PointIndex>,
246    free_regions: SparseBitMatrix<N, RegionVid>,
247
248    /// Placeholders represent bound regions -- so something like `'a`
249    /// in `for<'a> fn(&'a u32)`.
250    placeholders: SparseBitMatrix<N, PlaceholderIndex>,
251}
252
253impl<N: Idx> RegionValues<N> {
254    /// Creates a new set of "region values" that tracks causal information.
255    /// Each of the regions in num_region_variables will be initialized with an
256    /// empty set of points and no causal information.
257    pub(crate) fn new(
258        location_map: Rc<DenseLocationMap>,
259        num_universal_regions: usize,
260        placeholder_indices: PlaceholderIndices,
261    ) -> Self {
262        let num_points = location_map.num_points();
263        let num_placeholders = placeholder_indices.len();
264        Self {
265            location_map,
266            points: SparseIntervalMatrix::new(num_points),
267            placeholder_indices,
268            free_regions: SparseBitMatrix::new(num_universal_regions),
269            placeholders: SparseBitMatrix::new(num_placeholders),
270        }
271    }
272
273    /// Adds the given element to the value for the given region. Returns whether
274    /// the element is newly added (i.e., was not already present).
275    pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool {
276        debug!("add(r={:?}, elem={:?})", r, elem);
277        elem.add_to_row(self, r)
278    }
279
280    /// Adds all the control-flow points to the values for `r`.
281    pub(crate) fn add_all_points(&mut self, r: N) {
282        self.points.insert_all_into_row(r);
283    }
284
285    /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to:
286    /// r_from`).
287    pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool {
288        self.points.union_rows(r_from, r_to)
289            | self.free_regions.union_rows(r_from, r_to)
290            | self.placeholders.union_rows(r_from, r_to)
291    }
292
293    /// Returns `true` if the region `r` contains the given element.
294    pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex) -> bool {
295        elem.contained_in_row(self, r)
296    }
297
298    /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
299    pub(crate) fn first_non_contained_inclusive(
300        &self,
301        r: N,
302        block: BasicBlock,
303        start: usize,
304        end: usize,
305    ) -> Option<usize> {
306        let row = self.points.row(r)?;
307        let block = self.location_map.entry_point(block);
308        let start = block.plus(start);
309        let end = block.plus(end);
310        let first_unset = row.first_unset_in(start..=end)?;
311        Some(first_unset.index() - block.index())
312    }
313
314    /// `self[to] |= values[from]`, essentially: that is, take all the
315    /// elements for the region `from` from `values` and add them to
316    /// the region `to` in `self`.
317    pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) {
318        let Some(value_points) = &values.points else {
319            panic!("LivenessValues must track specific points for use in merge_liveness");
320        };
321        if let Some(set) = value_points.row(from) {
322            self.points.union_row(to, set);
323        }
324    }
325
326    /// Returns `true` if `sup_region` contains all the CFG points that
327    /// `sub_region` contains. Ignores universal regions.
328    pub(crate) fn contains_points(&self, sup_region: N, sub_region: N) -> bool {
329        if let Some(sub_row) = self.points.row(sub_region) {
330            if let Some(sup_row) = self.points.row(sup_region) {
331                sup_row.superset(sub_row)
332            } else {
333                // sup row is empty, so sub row must be empty
334                sub_row.is_empty()
335            }
336        } else {
337            // sub row is empty, always true
338            true
339        }
340    }
341
342    /// Returns the locations contained within a given region `r`.
343    pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
344        self.points.row(r).into_iter().flat_map(move |set| {
345            set.iter()
346                .take_while(move |&p| self.location_map.point_in_range(p))
347                .map(move |p| self.location_map.to_location(p))
348        })
349    }
350
351    /// Returns just the universal regions that are contained in a given region's value.
352    pub(crate) fn universal_regions_outlived_by<'a>(
353        &'a self,
354        r: N,
355    ) -> impl Iterator<Item = RegionVid> + 'a {
356        self.free_regions.row(r).into_iter().flat_map(|set| set.iter())
357    }
358
359    /// Returns all the elements contained in a given region's value.
360    pub(crate) fn placeholders_contained_in<'a>(
361        &'a self,
362        r: N,
363    ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
364        self.placeholders
365            .row(r)
366            .into_iter()
367            .flat_map(|set| set.iter())
368            .map(move |p| self.placeholder_indices.lookup_placeholder(p))
369    }
370
371    /// Returns all the elements contained in a given region's value.
372    pub(crate) fn elements_contained_in<'a>(
373        &'a self,
374        r: N,
375    ) -> impl Iterator<Item = RegionElement> + 'a {
376        let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
377
378        let free_regions_iter =
379            self.universal_regions_outlived_by(r).map(RegionElement::RootUniversalRegion);
380
381        let placeholder_universes_iter =
382            self.placeholders_contained_in(r).map(RegionElement::PlaceholderRegion);
383
384        points_iter.chain(free_regions_iter).chain(placeholder_universes_iter)
385    }
386
387    /// Returns a "pretty" string value of the region. Meant for debugging.
388    pub(crate) fn region_value_str(&self, r: N) -> String {
389        pretty_print_region_elements(self.elements_contained_in(r))
390    }
391}
392
393pub(crate) trait ToElementIndex: Debug + Copy {
394    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool;
395
396    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool;
397}
398
399impl ToElementIndex for Location {
400    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
401        let index = values.location_map.point_from_location(self);
402        values.points.insert(row, index)
403    }
404
405    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
406        let index = values.location_map.point_from_location(self);
407        values.points.contains(row, index)
408    }
409}
410
411impl ToElementIndex for RegionVid {
412    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
413        values.free_regions.insert(row, self)
414    }
415
416    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
417        values.free_regions.contains(row, self)
418    }
419}
420
421impl ToElementIndex for ty::PlaceholderRegion {
422    fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
423        let index = values.placeholder_indices.lookup_index(self);
424        values.placeholders.insert(row, index)
425    }
426
427    fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
428        let index = values.placeholder_indices.lookup_index(self);
429        values.placeholders.contains(row, index)
430    }
431}
432
433/// For debugging purposes, returns a pretty-printed string of the given points.
434pub(crate) fn pretty_print_points(
435    location_map: &DenseLocationMap,
436    points: impl IntoIterator<Item = PointIndex>,
437) -> String {
438    pretty_print_region_elements(
439        points
440            .into_iter()
441            .take_while(|&p| location_map.point_in_range(p))
442            .map(|p| location_map.to_location(p))
443            .map(RegionElement::Location),
444    )
445}
446
447/// For debugging purposes, returns a pretty-printed string of the given region elements.
448fn pretty_print_region_elements(elements: impl IntoIterator<Item = RegionElement>) -> String {
449    let mut result = String::new();
450    result.push('{');
451
452    // Set to Some(l1, l2) when we have observed all the locations
453    // from l1..=l2 (inclusive) but not yet printed them. This
454    // gets extended if we then see l3 where l3 is the successor
455    // to l2.
456    let mut open_location: Option<(Location, Location)> = None;
457
458    let mut sep = "";
459    let mut push_sep = |s: &mut String| {
460        s.push_str(sep);
461        sep = ", ";
462    };
463
464    for element in elements {
465        match element {
466            RegionElement::Location(l) => {
467                if let Some((location1, location2)) = open_location {
468                    if location2.block == l.block
469                        && location2.statement_index == l.statement_index - 1
470                    {
471                        open_location = Some((location1, l));
472                        continue;
473                    }
474
475                    push_sep(&mut result);
476                    push_location_range(&mut result, location1, location2);
477                }
478
479                open_location = Some((l, l));
480            }
481
482            RegionElement::RootUniversalRegion(fr) => {
483                if let Some((location1, location2)) = open_location {
484                    push_sep(&mut result);
485                    push_location_range(&mut result, location1, location2);
486                    open_location = None;
487                }
488
489                push_sep(&mut result);
490                result.push_str(&format!("{fr:?}"));
491            }
492
493            RegionElement::PlaceholderRegion(placeholder) => {
494                if let Some((location1, location2)) = open_location {
495                    push_sep(&mut result);
496                    push_location_range(&mut result, location1, location2);
497                    open_location = None;
498                }
499
500                push_sep(&mut result);
501                result.push_str(&format!("{placeholder:?}"));
502            }
503        }
504    }
505
506    if let Some((location1, location2)) = open_location {
507        push_sep(&mut result);
508        push_location_range(&mut result, location1, location2);
509    }
510
511    result.push('}');
512
513    return result;
514
515    fn push_location_range(s: &mut String, location1: Location, location2: Location) {
516        if location1 == location2 {
517            s.push_str(&format!("{location1:?}"));
518        } else {
519            assert_eq!(location1.block, location2.block);
520            s.push_str(&format!(
521                "{:?}[{}..={}]",
522                location1.block, location1.statement_index, location2.statement_index
523            ));
524        }
525    }
526}