Skip to main content

rustc_next_trait_solver/
normalize.rs

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