rustc_borrowck/region_infer/opaque_types/
member_constraints.rs

1use rustc_data_structures::fx::FxHashMap;
2use rustc_hir::def_id::DefId;
3use rustc_middle::bug;
4use rustc_middle::ty::{
5    self, GenericArgsRef, Region, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
6    TypeVisitor,
7};
8use tracing::{debug, instrument};
9
10use super::DefiningUse;
11use super::region_ctxt::RegionCtxt;
12use crate::constraints::ConstraintSccIndex;
13
14pub(super) fn apply_member_constraints<'tcx>(
15    rcx: &mut RegionCtxt<'_, 'tcx>,
16    defining_uses: &[DefiningUse<'tcx>],
17) {
18    // Start by collecting the member constraints of all defining uses.
19    //
20    // Applying member constraints can influence other member constraints,
21    // so we first collect and then apply them.
22    let mut member_constraints = Default::default();
23    for defining_use in defining_uses {
24        let mut visitor = CollectMemberConstraintsVisitor {
25            rcx,
26            defining_use,
27            member_constraints: &mut member_constraints,
28        };
29        defining_use.hidden_type.ty.visit_with(&mut visitor);
30    }
31
32    // Now walk over the region graph, visiting the smallest regions first and then all
33    // regions which have to outlive that one.
34    //
35    // Whenever we encounter a member region, we mutate the value of this SCC. This is
36    // as if we'd introduce new outlives constraints. However, we discard these region
37    // values after we've inferred the hidden types of opaques and apply the region
38    // constraints by simply equating the actual hidden type with the inferred one.
39    debug!(?member_constraints);
40    for scc_a in rcx.constraint_sccs.all_sccs() {
41        debug!(?scc_a);
42        // Start by  adding the region values required by outlives constraints. This
43        // matches how we compute the final region values in `fn compute_regions`.
44        //
45        // We need to do this here to get a lower bound when applying member constraints.
46        // This propagates the region values added by previous member constraints.
47        for &scc_b in rcx.constraint_sccs.successors(scc_a) {
48            debug!(?scc_b);
49            rcx.scc_values.add_region(scc_a, scc_b);
50        }
51
52        for defining_use in member_constraints.get(&scc_a).into_iter().flatten() {
53            apply_member_constraint(rcx, scc_a, &defining_use.arg_regions);
54        }
55    }
56}
57
58#[instrument(level = "debug", skip(rcx))]
59fn apply_member_constraint<'tcx>(
60    rcx: &mut RegionCtxt<'_, 'tcx>,
61    member: ConstraintSccIndex,
62    arg_regions: &[RegionVid],
63) {
64    // If the member region lives in a higher universe, we currently choose
65    // the most conservative option by leaving it unchanged.
66    if !rcx.max_placeholder_universe_reached(member).is_root() {
67        return;
68    }
69
70    // The existing value of `'member` is a lower-bound. If its is already larger than
71    // some universal region, we cannot equate it with that region. Said differently, we
72    // ignore choice regions which are smaller than this member region.
73    let mut choice_regions = arg_regions
74        .iter()
75        .copied()
76        .map(|r| rcx.representative(r).rvid())
77        .filter(|&choice_region| {
78            rcx.scc_values.universal_regions_outlived_by(member).all(|lower_bound| {
79                rcx.universal_region_relations.outlives(choice_region, lower_bound)
80            })
81        })
82        .collect::<Vec<_>>();
83    debug!(?choice_regions, "after enforcing lower-bound");
84
85    // Now find all the *upper bounds* -- that is, each UB is a
86    // free region that must outlive the member region `R0` (`UB:
87    // R0`). Therefore, we need only keep an option `O` if `UB: O`
88    // for all UB.
89    //
90    // If we have a requirement `'upper_bound: 'member`, equating `'member`
91    // with some region `'choice` means we now also require `'upper_bound: 'choice`.
92    // Avoid choice regions for which this does not hold.
93    for ub in rcx.rev_scc_graph.upper_bounds(member) {
94        choice_regions
95            .retain(|&choice_region| rcx.universal_region_relations.outlives(ub, choice_region));
96    }
97    debug!(?choice_regions, "after enforcing upper-bound");
98
99    // At this point we can pick any member of `choice_regions` and would like to choose
100    // it to be a small as possible. To avoid potential non-determinism we will pick the
101    // smallest such choice.
102    //
103    // Because universal regions are only partially ordered (i.e, not every two regions are
104    // comparable), we will ignore any region that doesn't compare to all others when picking
105    // the minimum choice.
106    //
107    // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
108    // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
109    // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
110    let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
111        choice_regions.iter().all(|&r2| {
112            rcx.universal_region_relations.outlives(r1, r2)
113                || rcx.universal_region_relations.outlives(r2, r1)
114        })
115    });
116    // Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
117    let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
118        let r1_outlives_r2 = rcx.universal_region_relations.outlives(r1, r2);
119        let r2_outlives_r1 = rcx.universal_region_relations.outlives(r2, r1);
120        match (r1_outlives_r2, r2_outlives_r1) {
121            (true, true) => r1.min(r2),
122            (true, false) => r2,
123            (false, true) => r1,
124            (false, false) => bug!("incomparable regions in total order"),
125        }
126    }) else {
127        debug!("no unique minimum choice");
128        return;
129    };
130
131    debug!(?min_choice);
132    // Lift the member region to be at least as large as this `min_choice` by directly
133    // mutating the `scc_values` as we compute it. This acts as if we've added a
134    // `'member: 'min_choice` while not recomputing sccs. This means different sccs
135    // may now actually be equal.
136    let min_choice_scc = rcx.constraint_sccs.scc(min_choice);
137    rcx.scc_values.add_region(member, min_choice_scc);
138}
139
140struct CollectMemberConstraintsVisitor<'a, 'b, 'tcx> {
141    rcx: &'a RegionCtxt<'a, 'tcx>,
142    defining_use: &'b DefiningUse<'tcx>,
143    member_constraints: &'a mut FxHashMap<ConstraintSccIndex, Vec<&'b DefiningUse<'tcx>>>,
144}
145impl<'tcx> CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
146    fn cx(&self) -> TyCtxt<'tcx> {
147        self.rcx.infcx.tcx
148    }
149    fn visit_closure_args(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) {
150        let generics = self.cx().generics_of(def_id);
151        for arg in args.iter().skip(generics.parent_count) {
152            arg.visit_with(self);
153        }
154    }
155}
156impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectMemberConstraintsVisitor<'_, '_, 'tcx> {
157    fn visit_region(&mut self, r: Region<'tcx>) {
158        match r.kind() {
159            ty::ReBound(..) => return,
160            ty::ReVar(vid) => {
161                let scc = self.rcx.constraint_sccs.scc(vid);
162                self.member_constraints.entry(scc).or_default().push(self.defining_use);
163            }
164            _ => unreachable!(),
165        }
166    }
167
168    fn visit_ty(&mut self, ty: Ty<'tcx>) {
169        if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
170            return;
171        }
172
173        match *ty.kind() {
174            ty::Closure(def_id, args)
175            | ty::CoroutineClosure(def_id, args)
176            | ty::Coroutine(def_id, args) => self.visit_closure_args(def_id, args),
177
178            ty::Alias(kind, ty::AliasTy { def_id, args, .. })
179                if let Some(variances) = self.cx().opt_alias_variances(kind, def_id) =>
180            {
181                // Skip lifetime parameters that are not captured, since they do
182                // not need member constraints registered for them; we'll erase
183                // them (and hopefully in the future replace them with placeholders).
184                for (&v, arg) in std::iter::zip(variances, args.iter()) {
185                    if v != ty::Bivariant {
186                        arg.visit_with(self)
187                    }
188                }
189            }
190
191            _ => ty.super_visit_with(self),
192        }
193    }
194}