1 //! This module handles the relationships between "free regions", i.e., lifetime parameters.
2 //! Ordinarily, free regions are unrelated to one another, but they can be related via implied
3 //! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type,
4 //! and use that to decide when one free region outlives another, and so forth.
6 use rustc_data_structures::transitive_relation::TransitiveRelation;
7 use rustc_hir::def_id::DefId;
8 use rustc_middle::ty::{self, Lift, Region, TyCtxt};
10 /// Combines a `FreeRegionMap` and a `TyCtxt`.
12 /// This stuff is a bit convoluted and should be refactored, but as we
13 /// transition to NLL, it'll all go away anyhow.
14 pub(crate) struct RegionRelations<'a, 'tcx> {
15 pub tcx: TyCtxt<'tcx>,
17 /// The context used for debug messages
20 /// Free-region relationships.
21 pub free_regions: &'a FreeRegionMap<'tcx>,
24 impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
25 pub fn new(tcx: TyCtxt<'tcx>, context: DefId, free_regions: &'a FreeRegionMap<'tcx>) -> Self {
26 Self { tcx, context, free_regions }
29 pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
30 self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
34 #[derive(Clone, Debug, Default)]
35 pub struct FreeRegionMap<'tcx> {
36 // Stores the relation `a < b`, where `a` and `b` are regions.
38 // Invariant: only free regions like `'x` or `'static` are stored
39 // in this relation, not scopes.
40 relation: TransitiveRelation<Region<'tcx>>,
43 impl<'tcx> FreeRegionMap<'tcx> {
44 pub fn elements(&self) -> impl Iterator<Item = Region<'tcx>> + '_ {
45 self.relation.elements().copied()
48 pub fn is_empty(&self) -> bool {
49 self.relation.is_empty()
52 // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
53 // (with the exception that `'static: 'x` is not notable)
54 pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
55 debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
56 if self.is_free_or_static(sub) && self.is_free(sup) {
57 self.relation.add(sub, sup)
61 /// Tests whether `r_a <= r_b`.
63 /// Both regions must meet `is_free_or_static`.
65 /// Subtle: one tricky case that this code gets correct is as
66 /// follows. If we know that `r_b: 'static`, then this function
67 /// will return true, even though we don't know anything that
68 /// directly relates `r_a` and `r_b`.
69 pub fn sub_free_regions(
75 assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
76 let re_static = tcx.lifetimes.re_static;
77 if self.check_relation(re_static, r_b) {
78 // `'a <= 'static` is always true, and not stored in the
79 // relation explicitly, so check if `'b` is `'static` (or
83 self.check_relation(r_a, r_b)
87 /// Check whether `r_a <= r_b` is found in the relation.
88 fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
89 r_a == r_b || self.relation.contains(r_a, r_b)
92 /// True for free regions other than `'static`.
93 pub fn is_free(&self, r: Region<'_>) -> bool {
94 matches!(*r, ty::ReEarlyBound(_) | ty::ReFree(_))
97 /// True if `r` is a free region or static of the sort that this
98 /// free region map can be used with.
99 pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
101 ty::ReStatic => true,
102 _ => self.is_free(r),
106 /// Computes the least-upper-bound of two free regions. In some
107 /// cases, this is more conservative than necessary, in order to
108 /// avoid making arbitrary choices. See
109 /// `TransitiveRelation::postdom_upper_bound` for more details.
110 pub fn lub_free_regions(
116 debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
117 assert!(self.is_free(r_a));
118 assert!(self.is_free(r_b));
119 let result = if r_a == r_b {
122 match self.relation.postdom_upper_bound(r_a, r_b) {
123 None => tcx.lifetimes.re_static,
127 debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
132 impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
133 type Lifted = FreeRegionMap<'tcx>;
134 fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
135 self.relation.maybe_map(|fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation })