rustc_borrowck/type_check/
free_region_relations.rs

1use rustc_data_structures::frozen::Frozen;
2use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
3use rustc_hir::def::DefKind;
4use rustc_infer::infer::canonical::QueryRegionConstraints;
5use rustc_infer::infer::outlives::env::RegionBoundPairs;
6use rustc_infer::infer::region_constraints::GenericKind;
7use rustc_infer::infer::{InferCtxt, outlives};
8use rustc_infer::traits::query::type_op::DeeplyNormalize;
9use rustc_middle::mir::ConstraintCategory;
10use rustc_middle::traits::query::OutlivesBound;
11use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
12use rustc_span::{ErrorGuaranteed, Span};
13use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
14use tracing::{debug, instrument};
15use type_op::TypeOpOutput;
16
17use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion};
18use crate::universal_regions::UniversalRegions;
19
20#[derive(Debug)]
21pub(crate) struct UniversalRegionRelations<'tcx> {
22    pub(crate) universal_regions: UniversalRegions<'tcx>,
23
24    /// Stores the outlives relations that are known to hold from the
25    /// implied bounds, in-scope where-clauses, and that sort of
26    /// thing.
27    outlives: TransitiveRelation<RegionVid>,
28
29    /// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
30    /// and we store that here. This is useful when figuring out how
31    /// to express some local region in terms of external regions our
32    /// caller will understand.
33    inverse_outlives: TransitiveRelation<RegionVid>,
34}
35
36/// As part of computing the free region relations, we also have to
37/// normalize the input-output types, which we then need later. So we
38/// return those. This vector consists of first the input types and
39/// then the output type as the last element.
40type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;
41
42pub(crate) struct CreateResult<'tcx> {
43    pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
44    pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>,
45    pub(crate) known_type_outlives_obligations: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
46    pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>,
47}
48
49pub(crate) fn create<'tcx>(
50    infcx: &InferCtxt<'tcx>,
51    param_env: ty::ParamEnv<'tcx>,
52    implicit_region_bound: ty::Region<'tcx>,
53    universal_regions: UniversalRegions<'tcx>,
54    constraints: &mut MirTypeckRegionConstraints<'tcx>,
55) -> CreateResult<'tcx> {
56    UniversalRegionRelationsBuilder {
57        infcx,
58        param_env,
59        implicit_region_bound,
60        constraints,
61        universal_regions,
62        region_bound_pairs: Default::default(),
63        outlives: Default::default(),
64        inverse_outlives: Default::default(),
65    }
66    .create()
67}
68
69impl UniversalRegionRelations<'_> {
70    /// Given two universal regions, returns the postdominating
71    /// upper-bound (effectively the least upper bound).
72    ///
73    /// (See `TransitiveRelation::postdom_upper_bound` for details on
74    /// the postdominating upper bound in general.)
75    pub(crate) fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid {
76        assert!(self.universal_regions.is_universal_region(fr1));
77        assert!(self.universal_regions.is_universal_region(fr2));
78        self.inverse_outlives
79            .postdom_upper_bound(fr1, fr2)
80            .unwrap_or(self.universal_regions.fr_static)
81    }
82
83    /// Finds an "upper bound" for `fr` that is not local. In other
84    /// words, returns the smallest (*) known region `fr1` that (a)
85    /// outlives `fr` and (b) is not local.
86    ///
87    /// (*) If there are multiple competing choices, we return all of them.
88    pub(crate) fn non_local_upper_bounds(&self, fr: RegionVid) -> Vec<RegionVid> {
89        debug!("non_local_upper_bound(fr={:?})", fr);
90        let res = self.non_local_bounds(&self.inverse_outlives, fr);
91        assert!(!res.is_empty(), "can't find an upper bound!?");
92        res
93    }
94
95    /// Finds a "lower bound" for `fr` that is not local. In other
96    /// words, returns the largest (*) known region `fr1` that (a) is
97    /// outlived by `fr` and (b) is not local.
98    ///
99    /// (*) If there are multiple competing choices, we pick the "postdominating"
100    /// one. See `TransitiveRelation::postdom_upper_bound` for details.
101    pub(crate) fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
102        debug!("non_local_lower_bound(fr={:?})", fr);
103        let lower_bounds = self.non_local_bounds(&self.outlives, fr);
104
105        // In case we find more than one, reduce to one for
106        // convenience. This is to prevent us from generating more
107        // complex constraints, but it will cause spurious errors.
108        let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
109
110        debug!("non_local_bound: post_dom={:?}", post_dom);
111
112        post_dom.and_then(|post_dom| {
113            // If the mutual immediate postdom is not local, then
114            // there is no non-local result we can return.
115            if !self.universal_regions.is_local_free_region(post_dom) {
116                Some(post_dom)
117            } else {
118                None
119            }
120        })
121    }
122
123    /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`.
124    /// Repeatedly invokes `postdom_parent` until we find something that is not
125    /// local. Returns `None` if we never do so.
126    fn non_local_bounds(
127        &self,
128        relation: &TransitiveRelation<RegionVid>,
129        fr0: RegionVid,
130    ) -> Vec<RegionVid> {
131        // This method assumes that `fr0` is one of the universally
132        // quantified region variables.
133        assert!(self.universal_regions.is_universal_region(fr0));
134
135        let mut external_parents = vec![];
136        let mut queue = vec![fr0];
137
138        // Keep expanding `fr` into its parents until we reach
139        // non-local regions.
140        while let Some(fr) = queue.pop() {
141            if !self.universal_regions.is_local_free_region(fr) {
142                external_parents.push(fr);
143                continue;
144            }
145
146            queue.extend(relation.parents(fr));
147        }
148
149        debug!("non_local_bound: external_parents={:?}", external_parents);
150
151        external_parents
152    }
153
154    /// Returns `true` if fr1 is known to outlive fr2.
155    ///
156    /// This will only ever be true for universally quantified regions.
157    pub(crate) fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
158        self.outlives.contains(fr1, fr2)
159    }
160
161    /// Returns `true` if fr1 is known to equal fr2.
162    ///
163    /// This will only ever be true for universally quantified regions.
164    pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
165        self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1)
166    }
167
168    /// Returns a vector of free regions `x` such that `fr1: x` is
169    /// known to hold.
170    pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<RegionVid> {
171        self.outlives.reachable_from(fr1)
172    }
173
174    /// Returns the _non-transitive_ set of known `outlives` constraints between free regions.
175    pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVid)> {
176        self.outlives.base_edges()
177    }
178}
179
180struct UniversalRegionRelationsBuilder<'a, 'tcx> {
181    infcx: &'a InferCtxt<'tcx>,
182    param_env: ty::ParamEnv<'tcx>,
183    universal_regions: UniversalRegions<'tcx>,
184    implicit_region_bound: ty::Region<'tcx>,
185    constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
186
187    // outputs:
188    outlives: TransitiveRelationBuilder<RegionVid>,
189    inverse_outlives: TransitiveRelationBuilder<RegionVid>,
190    region_bound_pairs: RegionBoundPairs<'tcx>,
191}
192
193impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
194    /// Records in the `outlives_relation` (and
195    /// `inverse_outlives_relation`) that `fr_a: fr_b`.
196    fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
197        debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b);
198        self.outlives.add(fr_a, fr_b);
199        self.inverse_outlives.add(fr_b, fr_a);
200    }
201
202    #[instrument(level = "debug", skip(self))]
203    pub(crate) fn create(mut self) -> CreateResult<'tcx> {
204        let tcx = self.infcx.tcx;
205        let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local();
206        let span = tcx.def_span(defining_ty_def_id);
207
208        // Insert the `'a: 'b` we know from the predicates.
209        // This does not consider the type-outlives.
210        let param_env = self.param_env;
211        self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
212
213        // - outlives is reflexive, so `'r: 'r` for every region `'r`
214        // - `'static: 'r` for every region `'r`
215        // - `'r: 'fn_body` for every (other) universally quantified
216        //   region `'r`, all of which are provided by our caller
217        let fr_static = self.universal_regions.fr_static;
218        let fr_fn_body = self.universal_regions.fr_fn_body;
219        for fr in self.universal_regions.universal_regions_iter() {
220            debug!("build: relating free region {:?} to itself and to 'static", fr);
221            self.relate_universal_regions(fr, fr);
222            self.relate_universal_regions(fr_static, fr);
223            self.relate_universal_regions(fr, fr_fn_body);
224        }
225
226        // Normalize the assumptions we use to borrowck the program.
227        let mut constraints = vec![];
228        let mut known_type_outlives_obligations = vec![];
229        for bound in param_env.caller_bounds() {
230            if let Some(outlives) = bound.as_type_outlives_clause() {
231                self.normalize_and_push_type_outlives_obligation(
232                    outlives,
233                    span,
234                    &mut known_type_outlives_obligations,
235                    &mut constraints,
236                );
237            };
238        }
239
240        let unnormalized_input_output_tys = self
241            .universal_regions
242            .unnormalized_input_tys
243            .iter()
244            .cloned()
245            .chain(Some(self.universal_regions.unnormalized_output_ty));
246
247        // For each of the input/output types:
248        // - Normalize the type. This will create some region
249        //   constraints, which we buffer up because we are
250        //   not ready to process them yet.
251        // - Then compute the implied bounds. This will adjust
252        //   the `region_bound_pairs` and so forth.
253        // - After this is done, we'll process the constraints, once
254        //   the `relations` is built.
255        let mut normalized_inputs_and_output =
256            Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
257        for ty in unnormalized_input_output_tys {
258            debug!("build: input_or_output={:?}", ty);
259            // We add implied bounds from both the unnormalized and normalized ty.
260            // See issue #87748
261            let constraints_unnorm = self.add_implied_bounds(ty, span);
262            if let Some(c) = constraints_unnorm {
263                constraints.push(c)
264            }
265            let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } =
266                param_env
267                    .and(DeeplyNormalize { value: ty })
268                    .fully_perform(self.infcx, span)
269                    .unwrap_or_else(|guar| TypeOpOutput {
270                        output: Ty::new_error(self.infcx.tcx, guar),
271                        constraints: None,
272                        error_info: None,
273                    });
274            if let Some(c) = constraints_normalize {
275                constraints.push(c)
276            }
277
278            // Note: we need this in examples like
279            // ```
280            // trait Foo {
281            //   type Bar;
282            //   fn foo(&self) -> &Self::Bar;
283            // }
284            // impl Foo for () {
285            //   type Bar = ();
286            //   fn foo(&self) ->&() {}
287            // }
288            // ```
289            // Both &Self::Bar and &() are WF
290            if ty != norm_ty {
291                let constraints_norm = self.add_implied_bounds(norm_ty, span);
292                if let Some(c) = constraints_norm {
293                    constraints.push(c)
294                }
295            }
296
297            normalized_inputs_and_output.push(norm_ty);
298        }
299
300        // Add implied bounds from impl header.
301        if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) {
302            for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
303                let result: Result<_, ErrorGuaranteed> =
304                    param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span);
305                let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else {
306                    continue;
307                };
308
309                constraints.extend(c);
310
311                // We currently add implied bounds from the normalized ty only.
312                // This is more conservative and matches wfcheck behavior.
313                let c = self.add_implied_bounds(norm_ty, span);
314                constraints.extend(c);
315            }
316        }
317
318        for c in constraints {
319            constraint_conversion::ConstraintConversion::new(
320                self.infcx,
321                &self.universal_regions,
322                &self.region_bound_pairs,
323                self.implicit_region_bound,
324                param_env,
325                &known_type_outlives_obligations,
326                Locations::All(span),
327                span,
328                ConstraintCategory::Internal,
329                self.constraints,
330            )
331            .convert_all(c);
332        }
333
334        CreateResult {
335            universal_region_relations: Frozen::freeze(UniversalRegionRelations {
336                universal_regions: self.universal_regions,
337                outlives: self.outlives.freeze(),
338                inverse_outlives: self.inverse_outlives.freeze(),
339            }),
340            known_type_outlives_obligations,
341            region_bound_pairs: self.region_bound_pairs,
342            normalized_inputs_and_output,
343        }
344    }
345
346    fn normalize_and_push_type_outlives_obligation(
347        &self,
348        mut outlives: ty::PolyTypeOutlivesPredicate<'tcx>,
349        span: Span,
350        known_type_outlives_obligations: &mut Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
351        constraints: &mut Vec<&QueryRegionConstraints<'tcx>>,
352    ) {
353        // In the new solver, normalize the type-outlives obligation assumptions.
354        if self.infcx.next_trait_solver() {
355            let Ok(TypeOpOutput {
356                output: normalized_outlives,
357                constraints: constraints_normalize,
358                error_info: _,
359            }) = self
360                .param_env
361                .and(DeeplyNormalize { value: outlives })
362                .fully_perform(self.infcx, span)
363            else {
364                self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}"));
365                return;
366            };
367            outlives = normalized_outlives;
368            if let Some(c) = constraints_normalize {
369                constraints.push(c);
370            }
371        }
372
373        known_type_outlives_obligations.push(outlives);
374    }
375
376    /// Update the type of a single local, which should represent
377    /// either the return type of the MIR or one of its arguments. At
378    /// the same time, compute and add any implied bounds that come
379    /// from this local.
380    #[instrument(level = "debug", skip(self))]
381    fn add_implied_bounds(
382        &mut self,
383        ty: Ty<'tcx>,
384        span: Span,
385    ) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
386        let TypeOpOutput { output: bounds, constraints, .. } = self
387            .param_env
388            .and(type_op::ImpliedOutlivesBounds { ty })
389            .fully_perform(self.infcx, span)
390            .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
391            .ok()?;
392        debug!(?bounds, ?constraints);
393        // Because of #109628, we may have unexpected placeholders. Ignore them!
394        // FIXME(#109628): panic in this case once the issue is fixed.
395        let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders());
396        self.add_outlives_bounds(bounds);
397        constraints
398    }
399
400    /// Registers the `OutlivesBound` items from `outlives_bounds` in
401    /// the outlives relation as well as the region-bound pairs
402    /// listing.
403    fn add_outlives_bounds<I>(&mut self, outlives_bounds: I)
404    where
405        I: IntoIterator<Item = OutlivesBound<'tcx>>,
406    {
407        for outlives_bound in outlives_bounds {
408            debug!("add_outlives_bounds(bound={:?})", outlives_bound);
409
410            match outlives_bound {
411                OutlivesBound::RegionSubRegion(r1, r2) => {
412                    // The bound says that `r1 <= r2`; we store `r2: r1`.
413                    let r1 = self.universal_regions.to_region_vid(r1);
414                    let r2 = self.universal_regions.to_region_vid(r2);
415                    self.relate_universal_regions(r2, r1);
416                }
417
418                OutlivesBound::RegionSubParam(r_a, param_b) => {
419                    self.region_bound_pairs
420                        .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
421                }
422
423                OutlivesBound::RegionSubAlias(r_a, alias_b) => {
424                    self.region_bound_pairs
425                        .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
426                }
427            }
428        }
429    }
430}