hir_ty/next_solver/
normalize.rs

1use rustc_next_trait_solver::placeholder::BoundVarReplacer;
2use rustc_type_ir::{
3    AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable,
4    TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
5    inherent::{IntoKind, Term as _},
6};
7
8use crate::next_solver::{
9    Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Term, Ty,
10    TyKind,
11    fulfill::{FulfillmentCtxt, NextSolverError},
12    infer::{
13        InferCtxt,
14        at::At,
15        traits::{Obligation, ObligationCause},
16    },
17    util::PlaceholderReplacer,
18};
19
20/// Deeply normalize all aliases in `value`. This does not handle inference and expects
21/// its input to be already fully resolved.
22pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result<T, Vec<NextSolverError<'db>>>
23where
24    T: TypeFoldable<DbInterner<'db>>,
25{
26    assert!(!value.has_escaping_bound_vars());
27    deeply_normalize_with_skipped_universes(at, value, vec![])
28}
29
30/// Deeply normalize all aliases in `value`. This does not handle inference and expects
31/// its input to be already fully resolved.
32///
33/// Additionally takes a list of universes which represents the binders which have been
34/// entered before passing `value` to the function. This is currently needed for
35/// `normalize_erasing_regions`, which skips binders as it walks through a type.
36pub fn deeply_normalize_with_skipped_universes<'db, T>(
37    at: At<'_, 'db>,
38    value: T,
39    universes: Vec<Option<UniverseIndex>>,
40) -> Result<T, Vec<NextSolverError<'db>>>
41where
42    T: TypeFoldable<DbInterner<'db>>,
43{
44    let (value, coroutine_goals) =
45        deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
46            at, value, universes,
47        )?;
48    assert_eq!(coroutine_goals, vec![]);
49
50    Ok(value)
51}
52
53/// Deeply normalize all aliases in `value`. This does not handle inference and expects
54/// its input to be already fully resolved.
55///
56/// Additionally takes a list of universes which represents the binders which have been
57/// entered before passing `value` to the function. This is currently needed for
58/// `normalize_erasing_regions`, which skips binders as it walks through a type.
59///
60/// This returns a set of stalled obligations involving coroutines if the typing mode of
61/// the underlying infcx has any stalled coroutine def ids.
62pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'db, T>(
63    at: At<'_, 'db>,
64    value: T,
65    universes: Vec<Option<UniverseIndex>>,
66) -> Result<(T, Vec<Goal<'db, Predicate<'db>>>), Vec<NextSolverError<'db>>>
67where
68    T: TypeFoldable<DbInterner<'db>>,
69{
70    let fulfill_cx = FulfillmentCtxt::new(at.infcx);
71    let mut folder = NormalizationFolder {
72        at,
73        fulfill_cx,
74        depth: 0,
75        universes,
76        stalled_coroutine_goals: vec![],
77    };
78    let value = value.try_fold_with(&mut folder)?;
79    let errors = folder.fulfill_cx.evaluate_obligations_error_on_ambiguity(at.infcx);
80    if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) }
81}
82
83struct NormalizationFolder<'me, 'db> {
84    at: At<'me, 'db>,
85    fulfill_cx: FulfillmentCtxt<'db>,
86    depth: usize,
87    universes: Vec<Option<UniverseIndex>>,
88    stalled_coroutine_goals: Vec<Goal<'db, Predicate<'db>>>,
89}
90
91impl<'db> NormalizationFolder<'_, 'db> {
92    fn normalize_alias_term(
93        &mut self,
94        alias_term: Term<'db>,
95    ) -> Result<Term<'db>, Vec<NextSolverError<'db>>> {
96        let infcx = self.at.infcx;
97        let interner = infcx.interner;
98        let recursion_limit = interner.recursion_limit();
99
100        self.depth += 1;
101
102        let infer_term = infcx.next_term_var_of_kind(alias_term);
103        let obligation = Obligation::new(
104            interner,
105            self.at.cause.clone(),
106            self.at.param_env,
107            PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate),
108        );
109
110        if self.depth > recursion_limit {
111            //     let term = alias_term.to_alias_term().unwrap();
112            //     self.at.infcx.err_ctxt().report_overflow_error(
113            //         OverflowCause::DeeplyNormalize(term),
114            //         self.at.cause.span,
115            //         true,
116            //         |_| {},
117            //     );
118            return Err(vec![NextSolverError::Overflow(obligation)]);
119        }
120
121        self.fulfill_cx.register_predicate_obligation(infcx, obligation);
122        self.select_all_and_stall_coroutine_predicates()?;
123
124        // Alias is guaranteed to be fully structurally resolved,
125        // so we can super fold here.
126        let term = infcx.resolve_vars_if_possible(infer_term);
127        // super-folding the `term` will directly fold the `Ty` or `Const` so
128        // we have to match on the term and super-fold them manually.
129        let result = match term.kind() {
130            TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(),
131            TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(),
132        };
133        self.depth -= 1;
134        Ok(result)
135    }
136
137    fn select_all_and_stall_coroutine_predicates(
138        &mut self,
139    ) -> Result<(), Vec<NextSolverError<'db>>> {
140        let errors = self.fulfill_cx.try_evaluate_obligations(self.at.infcx);
141        if !errors.is_empty() {
142            return Err(errors);
143        }
144
145        self.stalled_coroutine_goals.extend(
146            self.fulfill_cx
147                .drain_stalled_obligations_for_coroutines(self.at.infcx)
148                .into_iter()
149                .map(|obl| obl.as_goal()),
150        );
151
152        let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx);
153        if !errors.is_empty() {
154            return Err(errors);
155        }
156
157        Ok(())
158    }
159}
160
161impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> {
162    type Error = Vec<NextSolverError<'db>>;
163
164    fn cx(&self) -> DbInterner<'db> {
165        self.at.infcx.interner
166    }
167
168    fn try_fold_binder<T: TypeFoldable<DbInterner<'db>>>(
169        &mut self,
170        t: Binder<'db, T>,
171    ) -> Result<Binder<'db, T>, Self::Error> {
172        self.universes.push(None);
173        let t = t.try_super_fold_with(self)?;
174        self.universes.pop();
175        Ok(t)
176    }
177
178    fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result<Ty<'db>, Self::Error> {
179        let infcx = self.at.infcx;
180        debug_assert_eq!(ty, infcx.shallow_resolve(ty));
181        if !ty.has_aliases() {
182            return Ok(ty);
183        }
184
185        let TyKind::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) };
186
187        if ty.has_escaping_bound_vars() {
188            let (ty, mapped_regions, mapped_types, mapped_consts) =
189                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty);
190            let result = self.normalize_alias_term(ty.into())?.expect_type();
191            Ok(PlaceholderReplacer::replace_placeholders(
192                infcx,
193                mapped_regions,
194                mapped_types,
195                mapped_consts,
196                &self.universes,
197                result,
198            ))
199        } else {
200            Ok(self.normalize_alias_term(ty.into())?.expect_type())
201        }
202    }
203
204    fn try_fold_const(&mut self, ct: Const<'db>) -> Result<Const<'db>, Self::Error> {
205        let infcx = self.at.infcx;
206        debug_assert_eq!(ct, infcx.shallow_resolve_const(ct));
207        if !ct.has_aliases() {
208            return Ok(ct);
209        }
210
211        let ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) };
212
213        if ct.has_escaping_bound_vars() {
214            let (ct, mapped_regions, mapped_types, mapped_consts) =
215                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct);
216            let result = self.normalize_alias_term(ct.into())?.expect_const();
217            Ok(PlaceholderReplacer::replace_placeholders(
218                infcx,
219                mapped_regions,
220                mapped_types,
221                mapped_consts,
222                &self.universes,
223                result,
224            ))
225        } else {
226            Ok(self.normalize_alias_term(ct.into())?.expect_const())
227        }
228    }
229}
230
231// Deeply normalize a value and return it
232pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>(
233    infcx: &InferCtxt<'db>,
234    param_env: ParamEnv<'db>,
235    t: T,
236) -> T {
237    t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
238        at: infcx.at(&ObligationCause::dummy(), param_env),
239    })
240}
241
242struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> {
243    at: At<'a, 'tcx>,
244}
245
246impl<'db> TypeFolder<DbInterner<'db>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> {
247    fn cx(&self) -> DbInterner<'db> {
248        self.at.infcx.interner
249    }
250
251    fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
252        let infcx = self.at.infcx;
253        let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| {
254            deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
255                self.at,
256                ty,
257                vec![None; ty.outer_exclusive_binder().as_usize()],
258            )
259        });
260        match result {
261            Ok((ty, _)) => ty,
262            Err(_) => ty.super_fold_with(self),
263        }
264    }
265
266    fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
267        let infcx = self.at.infcx;
268        let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| {
269            deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
270                self.at,
271                ct,
272                vec![None; ct.outer_exclusive_binder().as_usize()],
273            )
274        });
275        match result {
276            Ok((ct, _)) => ct,
277            Err(_) => ct.super_fold_with(self),
278        }
279    }
280}