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