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