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