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
20pub 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
30pub 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
53pub 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 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 let term = infcx.resolve_vars_if_possible(infer_term);
127 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
231pub(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}