Skip to main content

rustc_next_trait_solver/
normalize.rs

1use std::fmt::Debug;
2
3use rustc_type_ir::data_structures::ensure_sufficient_stack;
4use rustc_type_ir::inherent::*;
5use rustc_type_ir::{
6    self as ty, AliasTerm, Binder, FallibleTypeFolder, InferCtxtLike, Interner, TypeFoldable,
7    TypeSuperFoldable, TypeVisitableExt, UniverseIndex, eager_resolve_vars,
8};
9use tracing::instrument;
10
11use crate::placeholder::{BoundVarReplacer, PlaceholderReplacer};
12
13/// This folder normalizes value and collects ambiguous goals.
14///
15/// Note that for ambiguous alias which contains escaping bound vars,
16/// we just return the original alias and don't collect the ambiguous goal.
17pub struct NormalizationFolder<'a, Infcx, I, F>
18where
19    Infcx: InferCtxtLike<Interner = I>,
20    I: Interner,
21{
22    infcx: &'a Infcx,
23    universes: Vec<Option<UniverseIndex>>,
24    normalize: F,
25}
26
27#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for HasEscapingBoundVars {
    #[inline]
    fn eq(&self, other: &HasEscapingBoundVars) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for HasEscapingBoundVars {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
28enum HasEscapingBoundVars {
29    Yes,
30    No,
31}
32
33#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for NormalizationWasAmbiguous {
    #[inline]
    fn eq(&self, other: &NormalizationWasAmbiguous) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NormalizationWasAmbiguous {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
34pub enum NormalizationWasAmbiguous {
35    Yes,
36    No,
37}
38
39impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F>
40where
41    Infcx: InferCtxtLike<Interner = I>,
42    I: Interner,
43    F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
44{
45    pub fn new(infcx: &'a Infcx, universes: Vec<Option<UniverseIndex>>, normalize: F) -> Self {
46        Self { infcx, universes, normalize }
47    }
48
49    fn normalize_alias_term(
50        &mut self,
51        alias_term: AliasTerm<I>,
52        has_escaping: HasEscapingBoundVars,
53    ) -> Result<Option<I::Term>, E> {
54        let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?;
55
56        // Return ambiguous higher ranked alias as is, if
57        //   - it contains escaping vars, and
58        //   - the normalized term contains infer vars which may mention
59        //     temporary placeholders after we've already mapped them back
60        //     to bound vars.
61        //
62        // We can normalize the ambiguous alias again after the binder is instantiated.
63        if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes
64            && has_escaping == HasEscapingBoundVars::Yes
65        {
66            let max_universe = ty::max_universe_of_infer_vars(self.infcx, normalized);
67            if max_universe.can_name(self.universes.first().unwrap().unwrap()) {
68                return Ok(None);
69            }
70        }
71
72        Ok(Some(normalized))
73    }
74}
75
76impl<'a, Infcx, I, F, E> FallibleTypeFolder<I> for NormalizationFolder<'a, Infcx, I, F>
77where
78    Infcx: InferCtxtLike<Interner = I>,
79    I: Interner,
80    F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
81    E: Debug,
82{
83    type Error = E;
84
85    fn cx(&self) -> I {
86        self.infcx.cx()
87    }
88
89    fn try_fold_binder<T: TypeFoldable<I>>(
90        &mut self,
91        t: Binder<I, T>,
92    ) -> Result<Binder<I, T>, Self::Error> {
93        self.universes.push(None);
94        let t = t.try_super_fold_with(self)?;
95        self.universes.pop();
96        Ok(t)
97    }
98
99    x;#[instrument(level = "trace", skip(self), ret)]
100    fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Self::Error> {
101        let infcx = self.infcx;
102        let original = ty;
103
104        if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() {
105            return Ok(ty);
106        }
107
108        // With eager normalization, we should normalize the args of alias before
109        // normalizing the alias itself.
110        let ty = ty.try_super_fold_with(self)?;
111        let ty::Alias(orig_is_rigid, alias_ty) = ty.kind() else { return Ok(ty) };
112        // We support ambiguous aliases inside rigid alias. So we still recognize
113        // the rigidness of the outer alias.
114        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
115            return Ok(ty);
116        }
117
118        let normalized = if ty.has_escaping_bound_vars() {
119            let (alias_ty, mapped_regions, mapped_types, mapped_consts) =
120                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty);
121            let Some(result) = ensure_sufficient_stack(|| {
122                self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes)
123            })?
124            else {
125                return Ok(ty);
126            };
127
128            PlaceholderReplacer::replace_placeholders(
129                infcx,
130                mapped_regions,
131                mapped_types,
132                mapped_consts,
133                &self.universes,
134                result.expect_ty(),
135            )
136        } else {
137            ensure_sufficient_stack(|| {
138                self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No)
139            })?
140            .map(|term| term.expect_ty())
141            .unwrap_or(ty)
142        };
143
144        if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
145            // find out missing typing env change.
146            let original = eager_resolve_vars(infcx, original);
147            let normalized = eager_resolve_vars(infcx, normalized);
148            assert_eq!(original, normalized, "rigid alias is further normalized");
149        }
150        Ok(normalized)
151    }
152
153    x;#[instrument(level = "trace", skip(self), ret)]
154    fn try_fold_const(&mut self, ct: I::Const) -> Result<I::Const, Self::Error> {
155        let infcx = self.infcx;
156        let original = ct;
157
158        if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() {
159            return Ok(ct);
160        }
161
162        // With eager normalization, we should normalize the args of alias before
163        // normalizing the alias itself.
164        let ct = ct.try_super_fold_with(self)?;
165        let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) };
166        // We support ambiguous aliases inside rigid alias. So we still recognize
167        // the rigidness of the outer alias.
168        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
169            return Ok(ct);
170        }
171
172        let normalized = if ct.has_escaping_bound_vars() {
173            let (alias_const, mapped_regions, mapped_types, mapped_consts) =
174                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const);
175            let Some(result) = ensure_sufficient_stack(|| {
176                self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes)
177            })?
178            else {
179                return Ok(ct);
180            };
181            PlaceholderReplacer::replace_placeholders(
182                infcx,
183                mapped_regions,
184                mapped_types,
185                mapped_consts,
186                &self.universes,
187                result.expect_const(),
188            )
189        } else {
190            ensure_sufficient_stack(|| {
191                self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No)
192            })?
193            .map(|term| term.expect_const())
194            .unwrap_or(ct)
195        };
196
197        if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
198            // find out missing typing env change.
199            let original = eager_resolve_vars(infcx, original);
200            let normalized = eager_resolve_vars(infcx, normalized);
201            assert_eq!(original, normalized, "rigid alias is further normalized");
202        }
203
204        Ok(normalized)
205    }
206
207    fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
208        if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) }
209    }
210}