Skip to main content

rustc_type_ir/
region_constraint.rs

1//! The bulk of the logic for implementing `-Zassumptions-on-binders`
2
3use derive_where::derive_where;
4use indexmap::IndexSet;
5#[cfg(feature = "nightly")]
6use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher};
7#[cfg(feature = "nightly")]
8use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
9use tracing::{debug, instrument};
10
11// Workaround for TransitiveRelation being in rustc_data_structures which isn't accessible on stable
12#[cfg(not(feature = "nightly"))]
13#[derive(Default, Clone, Debug)]
14pub struct TransitiveRelation<T>(T);
15#[cfg(not(feature = "nightly"))]
16impl<T> TransitiveRelation<T> {
17    pub fn reachable_from(&self, _data: T) -> Vec<T> {
18        unreachable!("-Zassumptions-on-binders is not supported for r-a")
19    }
20
21    pub fn base_edges(&self) -> impl Iterator<Item = (T, T)> {
22        unreachable!("-Zassumptions-on-binders is not supported for r-a");
23
24        #[allow(unreachable_code)]
25        [].into_iter()
26    }
27}
28#[derive(Clone, Debug)]
29#[cfg(not(feature = "nightly"))]
30pub struct TransitiveRelationBuilder<T>(T);
31#[cfg(not(feature = "nightly"))]
32impl<T> TransitiveRelationBuilder<T> {
33    pub fn freeze(self) -> TransitiveRelation<T> {
34        unreachable!("-Zassumptions-on-binders is not supported for r-a")
35    }
36
37    pub fn add(&mut self, _: T, _: T) {
38        unreachable!("-Zassumptions-on-binders is not supported for r-a")
39    }
40}
41#[cfg(not(feature = "nightly"))]
42impl<T> Default for TransitiveRelationBuilder<T> {
43    fn default() -> Self {
44        unreachable!("-Zassumptions-on-binders is not supported for r-a")
45    }
46}
47
48use crate::data_structures::IndexMap;
49use crate::fold::TypeSuperFoldable;
50use crate::inherent::*;
51use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo};
52use crate::{
53    AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, FallibleTypeFolder,
54    GenericTypeVisitable, InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind,
55    TyKind, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex,
56    Variance, VisitorResult, max_universe, set_aliases_to_non_rigid, try_visit,
57    walk_visitable_list,
58};
59
60#[automatically_derived]
impl<I: Interner> ::core::fmt::Debug for Assumptions<I> where I: Interner {
    fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
        -> ::core::fmt::Result {
        match self {
            Assumptions {
                type_outlives: ref __field_type_outlives,
                region_outlives: ref __field_region_outlives,
                inverse_region_outlives: ref __field_inverse_region_outlives }
                => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_struct(__f, "Assumptions");
                ::core::fmt::DebugStruct::field(&mut __builder,
                    "type_outlives", __field_type_outlives);
                ::core::fmt::DebugStruct::field(&mut __builder,
                    "region_outlives", __field_region_outlives);
                ::core::fmt::DebugStruct::field(&mut __builder,
                    "inverse_region_outlives", __field_inverse_region_outlives);
                ::core::fmt::DebugStruct::finish(&mut __builder)
            }
        }
    }
}#[derive_where(Clone, Debug; I: Interner)]
61pub struct Assumptions<I: Interner> {
62    pub type_outlives: Vec<Binder<I, OutlivesClause<I, I::Ty>>>,
63    pub region_outlives: TransitiveRelation<Region<I>>,
64    pub inverse_region_outlives: TransitiveRelation<Region<I>>,
65}
66
67impl<I: Interner> Assumptions<I> {
68    pub fn empty() -> Self {
69        Self {
70            type_outlives: Vec::new(),
71            region_outlives: TransitiveRelationBuilder::default().freeze(),
72            inverse_region_outlives: TransitiveRelationBuilder::default().freeze(),
73        }
74    }
75
76    pub fn new(
77        type_outlives: Vec<Binder<I, OutlivesClause<I, I::Ty>>>,
78        region_outlives: TransitiveRelation<Region<I>>,
79    ) -> Self {
80        Self {
81            inverse_region_outlives: {
82                let mut builder = TransitiveRelationBuilder::default();
83                for (r1, r2) in region_outlives.base_edges() {
84                    builder.add(r2, r1);
85                }
86                builder.freeze()
87            },
88            type_outlives,
89            region_outlives,
90        }
91    }
92}
93
94#[automatically_derived]
impl<I: Interner, S> ::core::fmt::Debug for RegionConstraint<I, S> where
    I: Interner, S: ::core::fmt::Debug {
    fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
        -> ::core::fmt::Result {
        match self {
            RegionConstraint::Ambiguity(ref __field_0) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f, "Ambiguity");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
            RegionConstraint::RegionOutlives(ref __field_0, ref __field_1,
                ref __field_2) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f, "RegionOutlives");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::field(&mut __builder, __field_1);
                ::core::fmt::DebugTuple::field(&mut __builder, __field_2);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
            RegionConstraint::AliasTyOutlivesViaEnv(ref __field_0,
                ref __field_1) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f,
                        "AliasTyOutlivesViaEnv");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::field(&mut __builder, __field_1);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
            RegionConstraint::PlaceholderTyOutlives(ref __field_0,
                ref __field_1, ref __field_2) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f,
                        "PlaceholderTyOutlives");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::field(&mut __builder, __field_1);
                ::core::fmt::DebugTuple::field(&mut __builder, __field_2);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
            RegionConstraint::And(ref __field_0) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f, "And");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
            RegionConstraint::Or(ref __field_0) => {
                let mut __builder =
                    ::core::fmt::Formatter::debug_tuple(__f, "Or");
                ::core::fmt::DebugTuple::field(&mut __builder, __field_0);
                ::core::fmt::DebugTuple::finish(&mut __builder)
            }
        }
    }
}#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, S)]
95#[derive(GenericTypeVisitable)]
96pub enum RegionConstraint<I: Interner, S = ()> {
97    Ambiguity(S),
98    RegionOutlives(Region<I>, Region<I>, S),
99    /// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked)
100    /// region due to an assumption in the environment. This cannot be satisfied via component outlives
101    /// or item bounds.
102    ///
103    /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
104    /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
105    ///
106    /// We eagerly destructure alias outlives requirements into region outlives requirements corresponding to
107    /// component outlives & item bound outlives rules, leaving only param env candidates.
108    AliasTyOutlivesViaEnv(Binder<I, (AliasTy<I>, Region<I>)>, S),
109    /// This is an `I::Ty` for two reasons:
110    /// 1. We need the type visitable impl to be able to `visit_ty` on this so canonicalization
111    ///    knows about the placeholder
112    /// 2. When exiting the trait solver there may be placeholder outlives corresponding to params
113    ///    from the root universe. These need to be changed from a `Placeholder` to the original
114    ///    `Param`.
115    ///
116    /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
117    /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
118    PlaceholderTyOutlives(I::Ty, Region<I>, S),
119
120    And(Box<[RegionConstraint<I, S>]>),
121    Or(Box<[RegionConstraint<I, S>]>),
122}
123
124/// A solver region constraint together with the span that caused each leaf constraint.
125///
126/// Solver query responses use [`RegionConstraint`] so source locations do not participate in
127/// candidate equality or caching. Spans are attached when responses are applied to an inference
128/// context.
129pub type SpannedRegionConstraint<I> = RegionConstraint<I, <I as Interner>::Span>;
130
131// This is not a derived impl because a perfect derive leads to inductive
132// cycle causing the trait to never actually be implemented.
133#[cfg(feature = "nightly")]
134impl<I: Interner> StableHash for RegionConstraint<I>
135where
136    Region<I>: StableHash,
137    I::Ty: StableHash,
138    I::GenericArgs: StableHash,
139    I::TraitAssocTyId: StableHash,
140    I::InherentAssocTyId: StableHash,
141    I::OpaqueTyId: StableHash,
142    I::FreeTyAliasId: StableHash,
143    I::BoundVarKinds: StableHash,
144{
145    #[inline]
146    fn stable_hash<CTX: StableHashCtxt>(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
147        use RegionConstraint::*;
148
149        std::mem::discriminant(self).stable_hash(hcx, hasher);
150        match self {
151            Ambiguity(_) => (),
152            RegionOutlives(a, b, _) => {
153                a.stable_hash(hcx, hasher);
154                b.stable_hash(hcx, hasher);
155            }
156            AliasTyOutlivesViaEnv(outlives, _) => {
157                outlives.stable_hash(hcx, hasher);
158            }
159            PlaceholderTyOutlives(a, b, _) => {
160                a.stable_hash(hcx, hasher);
161                b.stable_hash(hcx, hasher);
162            }
163            And(and) => {
164                for a in and.iter() {
165                    a.stable_hash(hcx, hasher);
166                }
167            }
168            Or(or) => {
169                for a in or.iter() {
170                    a.stable_hash(hcx, hasher);
171                }
172            }
173        }
174    }
175}
176
177impl<I: Interner, S: Clone + std::fmt::Debug> TypeFoldable<I> for RegionConstraint<I, S> {
178    fn try_fold_with<F: FallibleTypeFolder<I>>(self, f: &mut F) -> Result<Self, F::Error> {
179        use RegionConstraint::*;
180        Ok(match self {
181            Ambiguity(_) => self,
182            RegionOutlives(a, b, span) => {
183                RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span)
184            }
185            AliasTyOutlivesViaEnv(outlives, span) => {
186                AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?, span)
187            }
188            PlaceholderTyOutlives(a, b, span) => {
189                PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span)
190            }
191            And(and) => {
192                let mut new_and = Vec::new();
193                for a in and {
194                    new_and.push(a.try_fold_with(f)?);
195                }
196                And(new_and.into_boxed_slice())
197            }
198            Or(or) => {
199                let mut new_or = Vec::new();
200                for a in or {
201                    new_or.push(a.try_fold_with(f)?);
202                }
203                Or(new_or.into_boxed_slice())
204            }
205        })
206    }
207
208    fn fold_with<F: TypeFolder<I>>(self, f: &mut F) -> Self {
209        use RegionConstraint::*;
210        match self {
211            Ambiguity(_) => self,
212            RegionOutlives(a, b, span) => RegionOutlives(a.fold_with(f), b.fold_with(f), span),
213            AliasTyOutlivesViaEnv(outlives, span) => {
214                AliasTyOutlivesViaEnv(outlives.fold_with(f), span)
215            }
216            PlaceholderTyOutlives(a, b, span) => {
217                PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f), span)
218            }
219            And(and) => {
220                let mut new_and = Vec::new();
221                for a in and {
222                    new_and.push(a.fold_with(f));
223                }
224                And(new_and.into_boxed_slice())
225            }
226            Or(or) => {
227                let mut new_or = Vec::new();
228                for a in or {
229                    new_or.push(a.fold_with(f));
230                }
231                Or(new_or.into_boxed_slice())
232            }
233        }
234    }
235}
236
237impl<I: Interner, S: std::fmt::Debug> TypeVisitable<I> for RegionConstraint<I, S> {
238    fn visit_with<F: TypeVisitor<I>>(&self, f: &mut F) -> F::Result {
239        use RegionConstraint::*;
240
241        match self {
242            Ambiguity(_) => (),
243            RegionOutlives(a, b, _) => {
244                match ::rustc_ast_ir::visit::VisitorResult::branch(a.visit_with(f)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(a.visit_with(f));
245                match ::rustc_ast_ir::visit::VisitorResult::branch(b.visit_with(f)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(b.visit_with(f));
246            }
247            AliasTyOutlivesViaEnv(outlives, _) => {
248                match ::rustc_ast_ir::visit::VisitorResult::branch(outlives.visit_with(f)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(outlives.visit_with(f));
249            }
250            PlaceholderTyOutlives(a, b, _) => {
251                match ::rustc_ast_ir::visit::VisitorResult::branch(a.visit_with(f)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(a.visit_with(f));
252                match ::rustc_ast_ir::visit::VisitorResult::branch(b.visit_with(f)) {
    core::ops::ControlFlow::Continue(()) =>
        (),
        #[allow(unreachable_code)]
        core::ops::ControlFlow::Break(r) => {
        return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
    }
};try_visit!(b.visit_with(f));
253            }
254            And(and) => {
255                for elem in and {
    match ::rustc_ast_ir::visit::VisitorResult::branch(::rustc_type_ir::TypeVisitable::visit_with(elem,
                f)) {
        core::ops::ControlFlow::Continue(()) =>
            (),
            #[allow(unreachable_code)]
            core::ops::ControlFlow::Break(r) => {
            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
        }
    };
};walk_visitable_list!(f, and);
256            }
257            Or(or) => {
258                for elem in or {
    match ::rustc_ast_ir::visit::VisitorResult::branch(::rustc_type_ir::TypeVisitable::visit_with(elem,
                f)) {
        core::ops::ControlFlow::Continue(()) =>
            (),
            #[allow(unreachable_code)]
            core::ops::ControlFlow::Break(r) => {
            return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
        }
    };
};walk_visitable_list!(f, or);
259            }
260        };
261
262        F::Result::output()
263    }
264}
265
266impl<I: Interner, S> RegionConstraint<I, S> {
267    fn map_spans<T>(self, f: &mut impl FnMut(S) -> T) -> RegionConstraint<I, T> {
268        use RegionConstraint::*;
269
270        match self {
271            Ambiguity(span) => Ambiguity(f(span)),
272            RegionOutlives(a, b, span) => RegionOutlives(a, b, f(span)),
273            AliasTyOutlivesViaEnv(outlives, span) => AliasTyOutlivesViaEnv(outlives, f(span)),
274            PlaceholderTyOutlives(ty, region, span) => PlaceholderTyOutlives(ty, region, f(span)),
275            And(constraints) => And(constraints.into_iter().map(|c| c.map_spans(f)).collect()),
276            Or(constraints) => Or(constraints.into_iter().map(|c| c.map_spans(f)).collect()),
277        }
278    }
279
280    pub fn without_spans(self) -> RegionConstraint<I> {
281        self.map_spans(&mut |_| ())
282    }
283}
284
285impl<I: Interner> RegionConstraint<I> {
286    pub fn with_span<S: Clone>(self, span: S) -> RegionConstraint<I, S> {
287        self.map_spans(&mut |_| span.clone())
288    }
289}
290
291impl<I: Interner, S: Clone + std::fmt::Debug> Default for RegionConstraint<I, S> {
292    fn default() -> Self {
293        Self::new_true()
294    }
295}
296
297impl<I: Interner, S: Clone + std::fmt::Debug> RegionConstraint<I, S> {
298    pub fn new_true() -> Self {
299        RegionConstraint::And(Box::new([]))
300    }
301
302    pub fn is_true(&self) -> bool {
303        match self {
304            Self::And(and) => and.is_empty(),
305            _ => false,
306        }
307    }
308
309    pub fn new_false() -> Self {
310        RegionConstraint::Or(Box::new([]))
311    }
312
313    pub fn is_false(&self) -> bool {
314        match self {
315            Self::Or(or) => or.is_empty(),
316            _ => false,
317        }
318    }
319
320    pub fn is_or(&self) -> bool {
321        #[allow(non_exhaustive_omitted_patterns)] match self {
    Self::Or(_) => true,
    _ => false,
}matches!(self, Self::Or(_))
322    }
323
324    pub fn unwrap_or(self) -> Box<[RegionConstraint<I, S>]> {
325        match self {
326            Self::Or(ors) => ors,
327            _ => {
    ::core::panicking::panic_fmt(format_args!("`unwrap_or` on non-Or: {0:?}",
            self));
}panic!("`unwrap_or` on non-Or: {self:?}"),
328        }
329    }
330
331    pub fn unwrap_and(self) -> Box<[RegionConstraint<I, S>]> {
332        match self {
333            Self::And(ands) => ands,
334            _ => {
    ::core::panicking::panic_fmt(format_args!("`unwrap_and` on non-And: {0:?}",
            self));
}panic!("`unwrap_and` on non-And: {self:?}"),
335        }
336    }
337
338    pub fn is_and(&self) -> bool {
339        #[allow(non_exhaustive_omitted_patterns)] match self {
    Self::And(_) => true,
    _ => false,
}matches!(self, Self::And(_))
340    }
341
342    pub fn is_ambig(&self) -> bool {
343        #[allow(non_exhaustive_omitted_patterns)] match self {
    Self::Ambiguity(_) => true,
    _ => false,
}matches!(self, Self::Ambiguity(_))
344    }
345
346    pub fn and(self, other: RegionConstraint<I, S>) -> RegionConstraint<I, S> {
347        use RegionConstraint::*;
348
349        match (self, other) {
350            (And(a_ands), And(b_ands)) => And(a_ands
351                .into_iter()
352                .chain(b_ands.into_iter())
353                .collect::<Vec<_>>()
354                .into_boxed_slice()),
355            (And(ands), other) | (other, And(ands)) => {
356                And(ands.into_iter().chain([other]).collect::<Vec<_>>().into_boxed_slice())
357            }
358            (this, other) => And(Box::new([this, other])),
359        }
360    }
361
362    /// Converts the region constraint into an ORs of ANDs of "leaf" constraints. Where
363    /// a leaf constraint is a non-or/and constraint.
364    x;#[instrument(level = "debug", ret)]
365    pub fn canonical_form(self) -> Self {
366        use RegionConstraint::*;
367
368        fn permutations<I: Interner, S: Clone>(
369            ors: &[Vec<RegionConstraint<I, S>>],
370        ) -> Vec<Vec<RegionConstraint<I, S>>> {
371            match ors {
372                [] => vec![vec![]],
373                [or1] => {
374                    let mut choices = vec![];
375                    for choice in or1 {
376                        choices.push(vec![choice.clone()]);
377                    }
378                    choices
379                }
380                [or1, rest_ors @ ..] => {
381                    let mut choices = vec![];
382                    for choice in or1 {
383                        choices.extend(
384                            permutations(rest_ors)
385                                .into_iter()
386                                .map(|and| std::iter::once(choice.clone()).chain(and).collect()),
387                        );
388                    }
389                    choices
390                }
391            }
392        }
393
394        let canonical = match self {
395            And(ands) => {
396                // AND of OR of AND of LEAFs
397                //
398                // We can turn `AND of OR of X` into `OR of AND of X` by enumerating every set of choices
399                // for the list of ORs. For example if we have `AND ( OR(A, B), OR(C, D) )` we can convert this into
400                // `OR ( AND (A, C), AND (A, D), AND (B, C), AND (B, D ))`
401                //
402                // if A/B/C/D are all in canonical forms then we wind up with an `OR of AND of AND of LEAFs` which
403                // is trivially canonicalizeable by flattening the multiple layers of AND into one.
404                let ors = ands
405                    .into_iter()
406                    .map(|c| c.canonical_form().unwrap_or().to_vec())
407                    .collect::<Vec<_>>();
408                debug!(?ors);
409                let or_permutations = permutations(&ors);
410                debug!(?or_permutations);
411
412                Or(or_permutations
413                    .into_iter()
414                    .map(|c| {
415                        And(c
416                            .into_iter()
417                            .flat_map(|c2| c2.unwrap_and().into_iter())
418                            .collect::<Vec<_>>()
419                            .into_boxed_slice())
420                    })
421                    .collect::<Vec<_>>()
422                    .into_boxed_slice())
423            }
424            Or(ors) => {
425                // OR of OR of AND of LEAFs
426                //
427                // trivially canonicalizeable by concatenating all of the ORs into one big OR
428                Or(ors
429                    .into_iter()
430                    .flat_map(|c| c.canonical_form().unwrap_or().into_iter())
431                    .collect::<Vec<_>>()
432                    .into_boxed_slice())
433            }
434            _ => Or(Box::new([And(Box::new([self]))])),
435        };
436
437        assert!(
438            canonical.is_canonical_form(),
439            "non canonical form region constraint: {:?}",
440            canonical
441        );
442        canonical
443    }
444
445    fn is_leaf_constraint(&self) -> bool {
446        use RegionConstraint::*;
447        match self {
448            Ambiguity(_)
449            | RegionOutlives(..)
450            | AliasTyOutlivesViaEnv(..)
451            | PlaceholderTyOutlives(..) => true,
452            And(..) | Or(..) => false,
453        }
454    }
455
456    fn is_canonical_and(&self) -> bool {
457        if let Self::And(ands) = self { ands.iter().all(|c| c.is_leaf_constraint()) } else { false }
458    }
459
460    pub fn is_canonical_form(&self) -> bool {
461        if let Self::Or(ors) = self { ors.iter().all(|c| c.is_canonical_and()) } else { false }
462    }
463}
464
465/// Takes any constraints involving placeholders from the current universe and eagerly checks them.
466/// This can be done a few ways:
467/// - There's an assumption on the binder introducing the placeholder which means the constraint is satisfied (true)
468/// - There's assumptions on the binder introducing the placeholder which allow us to rewrite the constraint in
469///    terms of lower universe variables. For example given `for<'a> where('b: 'a) { prove(T: '!a_u1) }` we can
470///    convert this constraint to `T: 'b` which no longer references anything from `u1`.
471/// - There are no relevant assumptions so we can neither rewrite the constraint nor consider it satisfied (false)
472/// - We failed to compute the full set of assumptions when entering the binder corresponding to `u`. (ambiguity)
473///
474/// After handling all of the region constraints in `u` we then evaluate the entire constraint as much as possible,
475/// propagating true/false/ambiguity as close to the root of the constraint as we can. The returned constraint should
476/// be checked for whether it is true/false/ambiguous as that should affect the result of whatever operation required
477/// entering the binder corresponding to `u`.
478x;#[instrument(level = "debug", skip(infcx), ret)]
479pub fn eagerly_handle_placeholders_in_universe<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
480    infcx: &Infcx,
481    constraint: RegionConstraint<I>,
482    u: UniverseIndex,
483) -> RegionConstraint<I> {
484    use RegionConstraint::*;
485
486    let assumptions = infcx.get_placeholder_assumptions(u);
487
488    // 1. rewrite type outlives constraints involving things from `u` into either region constraints
489    //     involving things from `u` or type outlives constraints not involving things from `u`
490    //
491    //    IOW, we only want to encounter things from `u` as part of region out lives constraints.
492    let constraint = rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
493        infcx,
494        constraint,
495        u,
496        &assumptions,
497    );
498
499    // 2. rewrite the constraint into a canonical ORs of ANDs form
500    let constraint = constraint.canonical_form();
501
502    // 3. compute transitive region outlives and get a new set of region outlives constraints by
503    //     looking for every region which either a placeholder_u flows into it, or it flows into
504    //     the placeholder.
505    //
506    //    do this for each element in the top level OR
507    let constraint = Or(constraint
508        .unwrap_or()
509        .into_iter()
510        .map(|c| {
511            let and =
512                And(compute_new_region_constraints(infcx, &c.unwrap_and(), u).into_boxed_slice());
513
514            // 4. rewrite region outlives constraints (potentially to false/true)
515            pull_region_outlives_constraints_out_of_universe(infcx, and, u, &assumptions)
516        })
517        .collect::<Vec<_>>()
518        .into_boxed_slice());
519
520    // 5. actually evaluate the constraint to eagerly error on false
521    evaluate_solver_constraint(&constraint)
522}
523
524/// Filter our region constraints to not include constraints between region variables from `u` and
525/// other regions as those are always satisfied. This requires some care to handle correctly for example:
526/// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two
527/// constraints entirely.
528///
529/// The only constraints involving things from `u` should be region outlives constraints at this point. Type
530/// outlives constraints should have been handled already either by destructuring into region outlives or by
531/// being rewritten in terms of smaller universe variables.
532x;#[instrument(level = "debug", skip(infcx), ret)]
533fn compute_new_region_constraints<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
534    infcx: &Infcx,
535    constraints: &[RegionConstraint<I>],
536    u: UniverseIndex,
537) -> Vec<RegionConstraint<I>> {
538    use RegionConstraint::*;
539
540    let mut new_constraints = vec![];
541
542    let mut region_flows_builder = TransitiveRelationBuilder::default();
543    let mut regions = IndexSet::new();
544    for c in constraints {
545        match c {
546            And(..) | Or(..) => unreachable!(),
547            Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
548                new_constraints.push(c.clone())
549            }
550            RegionOutlives(r1, r2, _) => {
551                regions.insert(r1);
552                regions.insert(r2);
553                region_flows_builder.add(r2, r1);
554            }
555        }
556    }
557
558    let region_flow = region_flows_builder.freeze();
559    for r in regions.into_iter() {
560        for ub in region_flow.reachable_from(r) {
561            // we want to retain any region constraints between two "placeholder-likes" where for our
562            // purposes a placeholder-like is either a placeholder or variable in a lower universe
563            let is_placeholder_like = |r: Region<I>| match r.kind() {
564                RegionKind::ReLateParam(..)
565                | RegionKind::ReEarlyParam(..)
566                | RegionKind::RePlaceholder(..)
567                | RegionKind::ReStatic => true,
568                RegionKind::ReVar(..) => max_universe(infcx, r) < u,
569                RegionKind::ReError(..) => false,
570                RegionKind::ReErased | RegionKind::ReBound(..) => unreachable!(),
571            };
572
573            if is_placeholder_like(*r) && is_placeholder_like(*ub) {
574                new_constraints.push(RegionOutlives(*ub, *r, ()));
575            }
576        }
577    }
578
579    new_constraints
580}
581
582/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous
583x;#[instrument(level = "debug", ret)]
584pub fn evaluate_solver_constraint<I: Interner, S: Clone + std::fmt::Debug>(
585    constraint: &RegionConstraint<I, S>,
586) -> RegionConstraint<I, S> {
587    use RegionConstraint::*;
588    match constraint {
589        Ambiguity(_)
590        | RegionOutlives(..)
591        | AliasTyOutlivesViaEnv(..)
592        | PlaceholderTyOutlives(..) => constraint.clone(),
593        And(and) => {
594            let mut and_constraints = Vec::new();
595            let mut ambiguity = None;
596            for c in and.iter() {
597                let evaluated_constraint = evaluate_solver_constraint(c);
598                if evaluated_constraint.is_true() {
599                    // - do nothing
600                } else if evaluated_constraint.is_false() {
601                    return RegionConstraint::new_false();
602                } else if let Ambiguity(span) = evaluated_constraint {
603                    ambiguity.get_or_insert(span);
604                } else {
605                    and_constraints.push(evaluated_constraint);
606                }
607            }
608
609            ambiguity.map_or_else(
610                || RegionConstraint::And(and_constraints.into_boxed_slice()),
611                RegionConstraint::Ambiguity,
612            )
613        }
614        Or(or) => {
615            let mut or_constraints = Vec::new();
616            let mut ambiguity = None;
617            for c in or.iter() {
618                let evaluated_constraint = evaluate_solver_constraint(c);
619                if evaluated_constraint.is_false() {
620                    // do nothing
621                } else if evaluated_constraint.is_true() {
622                    return RegionConstraint::new_true();
623                } else if let Ambiguity(span) = evaluated_constraint {
624                    ambiguity.get_or_insert(span);
625                } else {
626                    or_constraints.push(evaluated_constraint);
627                }
628            }
629
630            ambiguity.map_or_else(
631                || RegionConstraint::Or(or_constraints.into_boxed_slice()),
632                RegionConstraint::Ambiguity,
633            )
634        }
635    }
636}
637
638/// Handles converting region outlives constraints involving placeholders from `u` into OR constraints
639/// involving regions from smaller universes with known relationships to the placeholder. For example:
640/// ```ignore (not rust)
641/// for<'a, 'b> where(
642///     'c: 'b, 'd: 'b,
643///     'a: 'e, 'a: 'f,
644/// ) {
645///     'a_u1: 'b_u1
646/// }
647/// ```
648/// will get converted to:
649/// ```ignore (not rust)
650/// OR(
651///     'e: 'c,
652///     'e: 'd,
653///     'f: 'c,
654///     'f: 'd,
655/// )
656/// ```
657/// if we are handling constraints in `u1`.
658x;#[instrument(level = "debug", skip(infcx), ret)]
659fn pull_region_outlives_constraints_out_of_universe<
660    Infcx: InferCtxtLike<Interner = I>,
661    I: Interner,
662>(
663    infcx: &Infcx,
664    constraint: RegionConstraint<I>,
665    u: UniverseIndex,
666    assumptions: &Option<Assumptions<I>>,
667) -> RegionConstraint<I> {
668    assert!(max_universe(infcx, constraint.clone()) <= u);
669
670    // FIXME(-Zassumptions-on-binders): we don't lower universes of region variables when exiting `u`
671    // this seems dubious/potentially wrong? we can't just blindly do this though as if we had something
672    // like `!T_u -> ?x_u -> !U_u` then lowering `?x` to `u-1` when exiting `u` would be wrong.
673    //
674    // I'm not even sure this would be necessary given we filter out region constraints involving regions#
675    // from the current universe and only retain those between placeholders.
676
677    use RegionConstraint::*;
678    match constraint {
679        Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
680            assert!(max_universe(infcx, constraint.clone()) < u);
681            constraint
682        }
683        RegionOutlives(region_1, region_2, ()) => {
684            let region_1_u = max_universe(infcx, region_1);
685            let region_2_u = max_universe(infcx, region_2);
686
687            if region_1_u != u && region_2_u != u {
688                return constraint;
689            }
690
691            let assumptions = match assumptions {
692                Some(assumptions) => assumptions,
693                None => return RegionConstraint::Ambiguity(()),
694            };
695
696            let mut candidates = vec![];
697            for ub in
698                regions_outlived_by(region_1, assumptions).filter(|r| max_universe(infcx, *r) < u)
699            {
700                // FIXME(-Zassumptions-on-binders): if `region_2` is in a smaller universe there'll be both
701                // `'region_2` and `'static` as lower bounds which seems... unfortunate and may cause us to
702                // add a bunch of duplicate `'ub: 'static` candidates the more binders we leave.
703                for lb in regions_outliving(region_2, assumptions, infcx.cx())
704                    .filter(|r| max_universe(infcx, *r) < u)
705                {
706                    // As long as any region outlived by `region_1` outlives any region region which
707                    // `region_2` outlives, we know that `region_1: region_2` holds. In other words,
708                    // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2`
709                    candidates.push(RegionOutlives(ub, lb, ()));
710                }
711            }
712
713            RegionConstraint::Or(candidates.into_boxed_slice())
714        }
715        And(constraints) => And(constraints
716            .into_iter()
717            .map(|constraint| {
718                pull_region_outlives_constraints_out_of_universe(infcx, constraint, u, assumptions)
719            })
720            .collect()),
721        Or(_) => unreachable!(),
722    }
723}
724
725/// Converts type outlives constraints into region outlives constraints. This assumes the *complete* set of
726/// assumptions are known. This should not be called until the end of type checking.
727///
728/// The returned region constraint will not have *any* PlaceholderTyOutlives or AliasTyOutlivesViaEnv constraints.
729pub fn destructure_type_outlives_constraints_in_root<
730    Infcx: InferCtxtLike<Interner = I>,
731    I: Interner,
732    S: Clone + std::fmt::Debug,
733>(
734    infcx: &Infcx,
735    constraint: RegionConstraint<I, S>,
736    assumptions: &Assumptions<I>,
737) -> RegionConstraint<I, S> {
738    use RegionConstraint::*;
739
740    match constraint {
741        Ambiguity(_) | RegionOutlives(..) => constraint,
742        PlaceholderTyOutlives(ty, r, span) => {
743            Or(regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
744                .map(move |assumption_r| RegionOutlives(assumption_r, r, span.clone()))
745                .collect::<Vec<_>>()
746                .into_boxed_slice())
747        }
748        AliasTyOutlivesViaEnv(bound_outlives, span) => {
749            alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions)
750                .with_span(span)
751        }
752        And(constraints) => And(constraints
753            .into_iter()
754            .map(|constraint| {
755                destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
756            })
757            .collect()),
758        Or(constraints) => Or(constraints
759            .into_iter()
760            .map(|constraint| {
761                destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
762            })
763            .collect()),
764    }
765}
766
767/// Converts type outlives constraints into either region outlives constraints, or type outlives
768/// constraints which do not contain anything from `u`.
769///
770/// This only works off assumptions associated with the binder corresponding to `u` both for
771/// perf reasons and because the full set of region assumptions is not known during type checking
772/// due to closure signature inference.
773///
774/// This only really causes problems for higher-ranked outlives assumptions, for example if we have
775/// `where for<'a> <T as Trait<'a>>::Assoc: 'b` then we can't use that to prove `<T as Trait<'!c>>::Assoc: 'b`
776/// until we are in the root context. See comments inside this function for more detail.
777x;#[instrument(level = "debug", skip(infcx), ret)]
778fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling<
779    Infcx: InferCtxtLike<Interner = I>,
780    I: Interner,
781>(
782    infcx: &Infcx,
783    constraint: RegionConstraint<I>,
784    u: UniverseIndex,
785    assumptions: &Option<Assumptions<I>>,
786) -> RegionConstraint<I> {
787    assert!(
788        max_universe(infcx, constraint.clone()) <= u,
789        "constraint {:?} contains terms from a larger universe than {:?}",
790        constraint.clone(),
791        u
792    );
793
794    use RegionConstraint::*;
795    match constraint {
796        Ambiguity(_) | RegionOutlives(..) => constraint,
797        PlaceholderTyOutlives(ty, region, ()) => {
798            let ty_u = max_universe(infcx, ty);
799            let region_u = max_universe(infcx, region);
800
801            if region_u != u && ty_u != u {
802                return constraint;
803            }
804
805            let assumptions = match assumptions {
806                Some(assumptions) => assumptions,
807                None => return Ambiguity(()),
808            };
809
810            let mut candidates = vec![];
811
812            // There could be `!T: 'region` assumptions in the env even if `!T` is in a
813            // smaller universe
814            candidates.extend(
815                regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
816                    .map(move |assumption_r| RegionOutlives(assumption_r, region, ())),
817            );
818
819            // We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary
820            // if the placeholder type is in a smaller universe as otherwise we know all regions which
821            // the placeholder outlives and can just destructure into an OR of RegionOutlives.
822            if region_u == u && ty_u < u {
823                candidates.extend(
824                    regions_outliving::<I>(region, assumptions, infcx.cx())
825                        .filter(|r| max_universe(infcx, *r) < u)
826                        .map(|r| PlaceholderTyOutlives(ty, r, ())),
827                );
828            }
829
830            Or(candidates.into_boxed_slice())
831        }
832        AliasTyOutlivesViaEnv(bound_outlives, ()) => {
833            let mut candidates = Vec::new();
834
835            // given there can be higher ranked assumptions, e.g. `for<'a> <T as Trait<'a>>::Assoc: 'c`, that
836            // means that it's actually *always* possible for an alias outlive to be satisfied in the root universe
837            // which means there should *always* be atleast two candidates when destructuring alias outlives. The
838            // two candidates being component outlives and then a higher ranked alias outlives.
839            //
840            // we dont care about this for region outlives as `for<'a> 'a: 'b` can't exist as we don't elaborate
841            // higher ranked type outlives assumptions into higher ranked region outlives assumptions. similarly,
842            // we don't care about `for<'a> Foo<'a>: 'b` as we always destructure adts into their components and if
843            // we dont equivalently elaborate the assumption into assumptions on the adt's components we just drop the
844            // assumptions
845            //
846            // so actually only `for<'a, 'b> Alias<'a>: 'b` and `for<'a> T: 'a` are assumptions we actually need to
847            // handle.
848            //
849            // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
850            if max_universe(infcx, bound_outlives) == u {
851                let mut replacer = PlaceholderReplacer {
852                    cx: infcx.cx(),
853                    existing_var_count: bound_outlives.bound_vars().len(),
854                    bound_vars: IndexMap::default(),
855                    universe: u,
856                    current_index: DebruijnIndex::ZERO,
857                };
858                let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer);
859                let bound_vars = bound_outlives.bound_vars().iter().chain(
860                    core::mem::take(&mut replacer.bound_vars)
861                        .into_iter()
862                        .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
863                );
864                let bound_outlives = Binder::bind_with_vars(
865                    escaping_outlives,
866                    I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
867                );
868                let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives, ());
869                if max_universe(infcx, candidate.clone()) < u {
870                    candidates.push(candidate);
871                } else {
872                    // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
873                    // a placeholder type in `u`, so this type-outlives constraint cannot be
874                    // handled by the region-outlives-only eager placeholder machinery.
875                    candidates.push(Ambiguity(()));
876                }
877            }
878
879            let assumptions = match assumptions {
880                Some(assumptions) => assumptions,
881                None => {
882                    candidates.push(Ambiguity(()));
883                    return Or(candidates.into_boxed_slice());
884                }
885            };
886
887            // Actually look at the assumptions and matching our higher ranked alias outlives goal
888            // against potentially higher ranked type outlives assumptions.
889            candidates.push(alias_outlives_candidates_from_assumptions(
890                infcx,
891                bound_outlives,
892                assumptions,
893            ));
894
895            // we can rewrite `Alias_u1: 'u2` into `Or(Alias_u1: 'u1)`
896            // given a list of regions which outlive `'u2`
897            //
898            // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
899            let (escaping_alias, escaping_r) = bound_outlives.skip_binder();
900            if max_universe(infcx, escaping_r) == u {
901                let mut replacer = PlaceholderReplacer {
902                    cx: infcx.cx(),
903                    existing_var_count: bound_outlives.bound_vars().len(),
904                    bound_vars: IndexMap::default(),
905                    universe: u,
906                    current_index: DebruijnIndex::ZERO,
907                };
908                let escaping_alias = escaping_alias.fold_with(&mut replacer);
909                let bound_vars = bound_outlives.bound_vars().iter().chain(
910                    core::mem::take(&mut replacer.bound_vars)
911                        .into_iter()
912                        .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
913                );
914                let bound_alias = Binder::bind_with_vars(
915                    escaping_alias,
916                    I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
917                );
918
919                // while we did skip the binder, bound vars aren't in any universe so
920                // this can't be an escaping bound var
921                for r2 in regions_outliving(escaping_r, assumptions, infcx.cx())
922                    .filter(|r2| max_universe(infcx, *r2) < u)
923                {
924                    let candidate =
925                        AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)), ());
926                    if max_universe(infcx, candidate.clone()) < u {
927                        candidates.push(candidate);
928                    } else {
929                        candidates.push(Ambiguity(()));
930                    }
931                }
932            }
933
934            // I'm not convinced our handling here is *complete* so for now
935            // let's be conservative and not let alias outlives' cause NoSolution
936            // in coherence
937            match infcx.typing_mode_raw() {
938                TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity(())),
939                TypingMode::Typeck { .. }
940                | TypingMode::ErasedNotCoherence { .. }
941                | TypingMode::PostTypeckUntilBorrowck { .. }
942                | TypingMode::PostBorrowck { .. }
943                | TypingMode::Reflection
944                | TypingMode::PostAnalysis
945                | TypingMode::Codegen => (),
946            };
947
948            RegionConstraint::Or(candidates.into_boxed_slice())
949        }
950        And(constraints) => And(constraints
951            .into_iter()
952            .map(|constraint| {
953                rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
954                    infcx,
955                    constraint,
956                    u,
957                    assumptions,
958                )
959            })
960            .collect()),
961        Or(constraints) => Or(constraints
962            .into_iter()
963            .map(|constraint| {
964                rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
965                    infcx,
966                    constraint,
967                    u,
968                    assumptions,
969                )
970            })
971            .collect()),
972    }
973}
974
975/// Returns all regions `r2` for which `r: r2` is known to hold in
976/// the universe associated with `assumptions`
977pub fn regions_outlived_by<I: Interner>(
978    r: Region<I>,
979    assumptions: &Assumptions<I>,
980) -> impl Iterator<Item = Region<I>> {
981    // FIXME(-Zassumptions-on-binders): do we need to be adding the reflexive edge here?
982    assumptions.region_outlives.reachable_from(r).into_iter().chain([r])
983}
984
985/// Returns all regions `r2` for which `r2: r` is known to hold in
986/// the universe associated with `assumptions`
987pub fn regions_outliving<I: Interner>(
988    r: Region<I>,
989    assumptions: &Assumptions<I>,
990    cx: I,
991) -> impl Iterator<Item = Region<I>> {
992    assumptions
993        .inverse_region_outlives
994        .reachable_from(r)
995        .into_iter()
996        // FIXME(-Zassumptions-on-binders): 'static may have been an input region canonicalized to something else is that important?
997        // FIXME(-Zassumptions-on-binders): do we need to adding the reflexive edge here?
998        .chain([r, Region::new_static(cx)])
999}
1000
1001/// Returns all regions `r` for which `!t: r` is known to hold in
1002/// the universe associated with `assumptions`
1003pub fn regions_outlived_by_placeholder<I: Interner>(
1004    t: I::Ty,
1005    assumptions: &Assumptions<I>,
1006    cx: I,
1007) -> impl Iterator<Item = Region<I>> {
1008    match t.kind() {
1009        TyKind::Placeholder(..) | TyKind::Param(..) => (),
1010        _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("non-placeholder in `regions_outlived_by_placeholder`: {0:?}",
                t)));
}unreachable!("non-placeholder in `regions_outlived_by_placeholder`: {t:?}"),
1011    }
1012
1013    assumptions.type_outlives.iter().flat_map(move |binder| match binder.no_bound_vars() {
1014        Some(OutlivesClause(ty, r)) => (ty == t).then_some(r),
1015        None => Some(Region::new_static(cx)),
1016    })
1017}
1018
1019pub struct PlaceholderReplacer<I: Interner> {
1020    cx: I,
1021    existing_var_count: usize,
1022    bound_vars: IndexMap<BoundVar, BoundRegion<I>>,
1023    universe: UniverseIndex,
1024    current_index: DebruijnIndex,
1025}
1026
1027impl<I: Interner> TypeFolder<I> for PlaceholderReplacer<I> {
1028    fn cx(&self) -> I {
1029        self.cx
1030    }
1031
1032    fn fold_region(&mut self, r: Region<I>) -> Region<I> {
1033        match r.kind() {
1034            RegionKind::RePlaceholder(p) if p.universe == self.universe => {
1035                let bound_vars_len = self.bound_vars.len();
1036                let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion {
1037                    var: BoundVar::from_usize(self.existing_var_count + bound_vars_len),
1038                    kind: p.bound.kind,
1039                });
1040                Region::new_bound(self.cx, self.current_index, *mapped_var)
1041            }
1042            // FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow
1043            _ => r,
1044        }
1045    }
1046
1047    fn fold_binder<T: TypeFoldable<I>>(&mut self, b: Binder<I, T>) -> Binder<I, T> {
1048        self.current_index.shift_in(1);
1049        let b = b.super_fold_with(self);
1050        self.current_index.shift_out(1);
1051        b
1052    }
1053}
1054
1055/// Converts an `AliasTyOutlivesViaEnv` constraint into an OR of region outlives constraints by
1056/// matching the alias against any `Alias: 'a` assumptions. This is somewhat tricky as we have a
1057/// potentially higher ranked alias being equated with a potentially higher ranked assumption and
1058/// we don't handle it correctly right now (though it is a somewhat reasonable halfway step).
1059x;#[instrument(level = "debug", skip(infcx), ret)]
1060fn alias_outlives_candidates_from_assumptions<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
1061    infcx: &Infcx,
1062    bound_outlives: Binder<I, (AliasTy<I>, Region<I>)>,
1063    assumptions: &Assumptions<I>,
1064) -> RegionConstraint<I> {
1065    let mut candidates = Vec::new();
1066
1067    let prev_universe = infcx.universe();
1068
1069    infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| {
1070        for bound_type_outlives in assumptions.type_outlives.iter() {
1071            let OutlivesClause(alias2, r2) =
1072                infcx.instantiate_binder_with_infer(*bound_type_outlives);
1073
1074            let mut relation = HigherRankedAliasMatcher {
1075                infcx,
1076                region_constraints: vec![RegionConstraint::RegionOutlives(r2, r, ())],
1077            };
1078
1079            // FIXME(#155345): Both sides should be rigid in the future.
1080            // Currently we can't guarantee that.
1081            if let Ok(_) = relation.relate(
1082                alias.to_ty(infcx.cx(), IsRigid::No),
1083                set_aliases_to_non_rigid(infcx.cx(), alias2).skip_norm_wip(),
1084            ) {
1085                candidates
1086                    .push(RegionConstraint::And(relation.region_constraints.into_boxed_slice()));
1087            }
1088        }
1089    });
1090
1091    let constraint = RegionConstraint::Or(candidates.into_boxed_slice());
1092
1093    let largest_universe = infcx.universe();
1094    debug!(?prev_universe, ?largest_universe);
1095
1096    ((prev_universe.index() + 1)..=largest_universe.index())
1097        .map(|u| UniverseIndex::from_usize(u))
1098        .rev()
1099        .fold(constraint, |constraint, u| {
1100            eagerly_handle_placeholders_in_universe(infcx, constraint, u)
1101        })
1102}
1103
1104struct HigherRankedAliasMatcher<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
1105    infcx: &'a Infcx,
1106    region_constraints: Vec<RegionConstraint<I>>,
1107}
1108
1109impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeRelation<I>
1110    for HigherRankedAliasMatcher<'a, Infcx, I>
1111{
1112    fn cx(&self) -> I {
1113        self.infcx.cx()
1114    }
1115
1116    fn relate_ty_args(
1117        &mut self,
1118        a_ty: I::Ty,
1119        _b_ty: I::Ty,
1120        _ty_def_id: I::DefId,
1121        a_args: I::GenericArgs,
1122        b_args: I::GenericArgs,
1123        _mk: impl FnOnce(I::GenericArgs) -> I::Ty,
1124    ) -> RelateResult<I, I::Ty> {
1125        rustc_type_ir::relate::relate_args_invariantly(self, a_args, b_args)?;
1126        Ok(a_ty)
1127    }
1128
1129    fn relate_with_variance<T: Relate<I>>(
1130        &mut self,
1131        _variance: Variance,
1132        _info: VarianceDiagInfo<I>,
1133        a: T,
1134        b: T,
1135    ) -> RelateResult<I, T> {
1136        // FIXME(-Zassumptions-on-binders): bivariance is important for opaque type args so
1137        // we should actually handle variance in some way here.
1138        self.relate(a, b)
1139    }
1140
1141    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
1142        rustc_type_ir::relate::structurally_relate_tys(self, a, b)
1143    }
1144
1145    fn regions(&mut self, a: Region<I>, b: Region<I>) -> RelateResult<I, Region<I>> {
1146        if a != b {
1147            self.region_constraints.push(RegionConstraint::RegionOutlives(a, b, ()));
1148            self.region_constraints.push(RegionConstraint::RegionOutlives(b, a, ()));
1149        }
1150        Ok(a)
1151    }
1152
1153    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
1154        rustc_type_ir::relate::structurally_relate_consts(self, a, b)
1155    }
1156
1157    fn binders<T>(&mut self, a: Binder<I, T>, b: Binder<I, T>) -> RelateResult<I, Binder<I, T>>
1158    where
1159        T: Relate<I>,
1160    {
1161        self.infcx.enter_forall_with_empty_assumptions(a, |a| {
1162            let u = self.infcx.universe();
1163            self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1164            let b = self.infcx.instantiate_binder_with_infer(b);
1165            self.relate(a, b)
1166        })?;
1167
1168        self.infcx.enter_forall_with_empty_assumptions(b, |b| {
1169            let u = self.infcx.universe();
1170            self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1171            let a = self.infcx.instantiate_binder_with_infer(a);
1172            self.relate(a, b)
1173        })?;
1174
1175        Ok(a)
1176    }
1177}