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, InferConst, InferCtxtLike, InferTy,
7    Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
8    TypeVisitor, UniverseIndex,
9};
10use tracing::instrument;
11
12use crate::placeholder::{BoundVarReplacer, PlaceholderReplacer};
13
14/// This folder normalizes value and collects ambiguous goals.
15///
16/// Note that for ambiguous alias which contains escaping bound vars,
17/// we just return the original alias and don't collect the ambiguous goal.
18pub struct NormalizationFolder<'a, Infcx, I, F>
19where
20    Infcx: InferCtxtLike<Interner = I>,
21    I: Interner,
22{
23    infcx: &'a Infcx,
24    universes: Vec<Option<UniverseIndex>>,
25    normalize: F,
26}
27
28#[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)]
29enum HasEscapingBoundVars {
30    Yes,
31    No,
32}
33
34#[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)]
35pub enum NormalizationWasAmbiguous {
36    Yes,
37    No,
38}
39
40/// Finds the max universe present in infer vars.
41struct MaxUniverse<'a, Infcx, I>
42where
43    Infcx: InferCtxtLike<Interner = I>,
44    I: Interner,
45{
46    infcx: &'a Infcx,
47    max_universe: ty::UniverseIndex,
48}
49
50impl<'a, Infcx, I> MaxUniverse<'a, Infcx, I>
51where
52    Infcx: InferCtxtLike<Interner = I>,
53    I: Interner,
54{
55    fn new(infcx: &'a Infcx) -> Self {
56        MaxUniverse { infcx, max_universe: ty::UniverseIndex::ROOT }
57    }
58
59    fn max_universe(self) -> ty::UniverseIndex {
60        self.max_universe
61    }
62}
63
64impl<'a, Infcx, I> TypeVisitor<I> for MaxUniverse<'a, Infcx, I>
65where
66    Infcx: InferCtxtLike<Interner = I>,
67    I: Interner,
68{
69    type Result = ();
70
71    fn visit_ty(&mut self, t: I::Ty) {
72        if !t.has_infer() {
73            return;
74        }
75
76        if let ty::Infer(InferTy::TyVar(vid)) = t.kind() {
77            // We shallow resolved the infer var before.
78            // So it should be a unresolved infer var with an universe.
79            self.max_universe = self.max_universe.max(self.infcx.universe_of_ty(vid).unwrap());
80        }
81
82        t.super_visit_with(self)
83    }
84
85    fn visit_const(&mut self, c: I::Const) {
86        if !c.has_infer() {
87            return;
88        }
89
90        if let ty::ConstKind::Infer(InferConst::Var(vid)) = c.kind() {
91            // We shallow resolved the infer var before.
92            // So it should be a unresolved infer var with an universe.
93            self.max_universe = self.max_universe.max(self.infcx.universe_of_ct(vid).unwrap());
94        }
95
96        c.super_visit_with(self)
97    }
98
99    fn visit_region(&mut self, r: I::Region) {
100        if let ty::ReVar(vid) = r.kind() {
101            self.max_universe = self.max_universe.max(self.infcx.universe_of_lt(vid).unwrap());
102        }
103    }
104}
105
106impl<'a, Infcx, I, F, E> NormalizationFolder<'a, Infcx, I, F>
107where
108    Infcx: InferCtxtLike<Interner = I>,
109    I: Interner,
110    F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
111{
112    pub fn new(infcx: &'a Infcx, universes: Vec<Option<UniverseIndex>>, normalize: F) -> Self {
113        Self { infcx, universes, normalize }
114    }
115
116    fn normalize_alias_term(
117        &mut self,
118        alias_term: AliasTerm<I>,
119        has_escaping: HasEscapingBoundVars,
120    ) -> Result<Option<I::Term>, E> {
121        let (normalized, normalization_was_ambiguous) = (self.normalize)(alias_term)?;
122
123        // Return ambiguous higher ranked alias as is, if
124        //   - it contains escaping vars, and
125        //   - the normalized term contains infer vars which may mention
126        //     temporary placeholders after we've already mapped them back
127        //     to bound vars.
128        //
129        // We can normalize the ambiguous alias again after the binder is instantiated.
130        if normalization_was_ambiguous == NormalizationWasAmbiguous::Yes
131            && has_escaping == HasEscapingBoundVars::Yes
132        {
133            let mut visitor = MaxUniverse::new(self.infcx);
134            normalized.visit_with(&mut visitor);
135            let max_universe = visitor.max_universe();
136            if max_universe.can_name(self.universes.first().unwrap().unwrap()) {
137                return Ok(None);
138            }
139        }
140
141        Ok(Some(normalized))
142    }
143}
144
145impl<'a, Infcx, I, F, E> FallibleTypeFolder<I> for NormalizationFolder<'a, Infcx, I, F>
146where
147    Infcx: InferCtxtLike<Interner = I>,
148    I: Interner,
149    F: FnMut(AliasTerm<I>) -> Result<(I::Term, NormalizationWasAmbiguous), E>,
150    E: Debug,
151{
152    type Error = E;
153
154    fn cx(&self) -> I {
155        self.infcx.cx()
156    }
157
158    fn try_fold_binder<T: TypeFoldable<I>>(
159        &mut self,
160        t: Binder<I, T>,
161    ) -> Result<Binder<I, T>, Self::Error> {
162        self.universes.push(None);
163        let t = t.try_super_fold_with(self)?;
164        self.universes.pop();
165        Ok(t)
166    }
167
168    x;#[instrument(level = "trace", skip(self), ret)]
169    fn try_fold_ty(&mut self, ty: I::Ty) -> Result<I::Ty, Self::Error> {
170        let infcx = self.infcx;
171        let original = ty;
172
173        if !self.cx().renormalize_rigid_aliases() && !ty.has_non_rigid_aliases() {
174            return Ok(ty);
175        }
176
177        // With eager normalization, we should normalize the args of alias before
178        // normalizing the alias itself.
179        let ty = ty.try_super_fold_with(self)?;
180        let ty::Alias(orig_is_rigid, alias_ty) = ty.kind() else { return Ok(ty) };
181        // We support ambiguous aliases inside rigid alias. So we still recognize
182        // the rigidness of the outer alias.
183        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
184            return Ok(ty);
185        }
186
187        let normalized = if ty.has_escaping_bound_vars() {
188            let (alias_ty, mapped_regions, mapped_types, mapped_consts) =
189                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_ty);
190            let Some(result) = ensure_sufficient_stack(|| {
191                self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::Yes)
192            })?
193            else {
194                return Ok(ty);
195            };
196
197            PlaceholderReplacer::replace_placeholders(
198                infcx,
199                mapped_regions,
200                mapped_types,
201                mapped_consts,
202                &self.universes,
203                result.expect_ty(),
204            )
205        } else {
206            ensure_sufficient_stack(|| {
207                self.normalize_alias_term(alias_ty.into(), HasEscapingBoundVars::No)
208            })?
209            .map(|term| term.expect_ty())
210            .unwrap_or(ty)
211        };
212
213        if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
214            // find out missing typing env change.
215            let original = crate::resolve::eager_resolve_vars(infcx, original);
216            let normalized = crate::resolve::eager_resolve_vars(infcx, normalized);
217            assert_eq!(original, normalized, "rigid alias is further normalized");
218        }
219        Ok(normalized)
220    }
221
222    x;#[instrument(level = "trace", skip(self), ret)]
223    fn try_fold_const(&mut self, ct: I::Const) -> Result<I::Const, Self::Error> {
224        let infcx = self.infcx;
225        let original = ct;
226
227        if !self.cx().renormalize_rigid_aliases() && !ct.has_non_rigid_aliases() {
228            return Ok(ct);
229        }
230
231        // With eager normalization, we should normalize the args of alias before
232        // normalizing the alias itself.
233        let ct = ct.try_super_fold_with(self)?;
234        let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) };
235        // We support ambiguous aliases inside rigid alias. So we still recognize
236        // the rigidness of the outer alias.
237        if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
238            return Ok(ct);
239        }
240
241        let normalized = if ct.has_escaping_bound_vars() {
242            let (alias_const, mapped_regions, mapped_types, mapped_consts) =
243                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const);
244            let Some(result) = ensure_sufficient_stack(|| {
245                self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes)
246            })?
247            else {
248                return Ok(ct);
249            };
250            PlaceholderReplacer::replace_placeholders(
251                infcx,
252                mapped_regions,
253                mapped_types,
254                mapped_consts,
255                &self.universes,
256                result.expect_const(),
257            )
258        } else {
259            ensure_sufficient_stack(|| {
260                self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No)
261            })?
262            .map(|term| term.expect_const())
263            .unwrap_or(ct)
264        };
265
266        if self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes {
267            // find out missing typing env change.
268            let original = crate::resolve::eager_resolve_vars(infcx, original);
269            let normalized = crate::resolve::eager_resolve_vars(infcx, normalized);
270            assert_eq!(original, normalized, "rigid alias is further normalized");
271        }
272
273        Ok(normalized)
274    }
275
276    fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
277        if p.allow_normalization() { p.try_super_fold_with(self) } else { Ok(p) }
278    }
279}