rustc_trait_selection/solve/
normalize.rs1use std::assert_matches::assert_matches;
2use std::fmt::Debug;
3
4use rustc_data_structures::stack::ensure_sufficient_stack;
5use rustc_infer::infer::InferCtxt;
6use rustc_infer::infer::at::At;
7use rustc_infer::traits::solve::Goal;
8use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine};
9use rustc_middle::traits::ObligationCause;
10use rustc_middle::ty::{
11 self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
12 TypeVisitableExt, UniverseIndex,
13};
14use tracing::instrument;
15
16use super::{FulfillmentCtxt, NextSolverError};
17use crate::error_reporting::InferCtxtErrorExt;
18use crate::error_reporting::traits::OverflowCause;
19use crate::traits::query::evaluate_obligation::InferCtxtExt;
20use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
21
22pub fn deeply_normalize<'tcx, T, E>(at: At<'_, 'tcx>, value: T) -> Result<T, Vec<E>>
25where
26 T: TypeFoldable<TyCtxt<'tcx>>,
27 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
28{
29 assert!(!value.has_escaping_bound_vars());
30 deeply_normalize_with_skipped_universes(at, value, vec![])
31}
32
33pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>(
40 at: At<'_, 'tcx>,
41 value: T,
42 universes: Vec<Option<UniverseIndex>>,
43) -> Result<T, Vec<E>>
44where
45 T: TypeFoldable<TyCtxt<'tcx>>,
46 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
47{
48 let (value, goals) =
49 deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?;
50 assert_eq!(goals, vec![]);
51
52 Ok(value)
53}
54
55pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>(
65 at: At<'_, 'tcx>,
66 value: T,
67 universes: Vec<Option<UniverseIndex>>,
68) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>>
69where
70 T: TypeFoldable<TyCtxt<'tcx>>,
71 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
72{
73 let fulfill_cx = FulfillmentCtxt::new(at.infcx);
74 let mut folder =
75 NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] };
76 let value = value.try_fold_with(&mut folder)?;
77 let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
78 if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) }
79}
80
81struct NormalizationFolder<'me, 'tcx, E> {
82 at: At<'me, 'tcx>,
83 fulfill_cx: FulfillmentCtxt<'tcx, E>,
84 depth: usize,
85 universes: Vec<Option<UniverseIndex>>,
86 stalled_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
87}
88
89impl<'tcx, E> NormalizationFolder<'_, 'tcx, E>
90where
91 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
92{
93 fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result<Ty<'tcx>, Vec<E>> {
94 assert_matches!(alias_ty.kind(), ty::Alias(..));
95
96 let infcx = self.at.infcx;
97 let tcx = infcx.tcx;
98 let recursion_limit = tcx.recursion_limit();
99 if !recursion_limit.value_within_limit(self.depth) {
100 let ty::Alias(_, data) = *alias_ty.kind() else {
101 unreachable!();
102 };
103
104 self.at.infcx.err_ctxt().report_overflow_error(
105 OverflowCause::DeeplyNormalize(data.into()),
106 self.at.cause.span,
107 true,
108 |_| {},
109 );
110 }
111
112 self.depth += 1;
113
114 let new_infer_ty = infcx.next_ty_var(self.at.cause.span);
115 let obligation = Obligation::new(
116 tcx,
117 self.at.cause.clone(),
118 self.at.param_env,
119 ty::PredicateKind::AliasRelate(
120 alias_ty.into(),
121 new_infer_ty.into(),
122 ty::AliasRelationDirection::Equate,
123 ),
124 );
125
126 self.fulfill_cx.register_predicate_obligation(infcx, obligation);
127 self.select_all_and_stall_coroutine_predicates()?;
128
129 let ty = infcx.resolve_vars_if_possible(new_infer_ty);
132 let result = ty.try_super_fold_with(self)?;
133 self.depth -= 1;
134 Ok(result)
135 }
136
137 fn normalize_unevaluated_const(
138 &mut self,
139 uv: ty::UnevaluatedConst<'tcx>,
140 ) -> Result<ty::Const<'tcx>, Vec<E>> {
141 let infcx = self.at.infcx;
142 let tcx = infcx.tcx;
143 let recursion_limit = tcx.recursion_limit();
144 if !recursion_limit.value_within_limit(self.depth) {
145 self.at.infcx.err_ctxt().report_overflow_error(
146 OverflowCause::DeeplyNormalize(uv.into()),
147 self.at.cause.span,
148 true,
149 |_| {},
150 );
151 }
152
153 self.depth += 1;
154
155 let new_infer_ct = infcx.next_const_var(self.at.cause.span);
156 let obligation = Obligation::new(
157 tcx,
158 self.at.cause.clone(),
159 self.at.param_env,
160 ty::NormalizesTo { alias: uv.into(), term: new_infer_ct.into() },
161 );
162
163 let result = if infcx.predicate_may_hold(&obligation) {
164 self.fulfill_cx.register_predicate_obligation(infcx, obligation);
165 let errors = self.fulfill_cx.select_where_possible(infcx);
166 if !errors.is_empty() {
167 return Err(errors);
168 }
169 let ct = infcx.resolve_vars_if_possible(new_infer_ct);
170 ct.try_fold_with(self)?
171 } else {
172 ty::Const::new_unevaluated(tcx, uv).try_super_fold_with(self)?
173 };
174
175 self.depth -= 1;
176 Ok(result)
177 }
178
179 fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec<E>> {
180 let errors = self.fulfill_cx.select_where_possible(self.at.infcx);
181 if !errors.is_empty() {
182 return Err(errors);
183 }
184
185 self.stalled_goals.extend(
186 self.fulfill_cx
187 .drain_stalled_obligations_for_coroutines(self.at.infcx)
188 .into_iter()
189 .map(|obl| obl.as_goal()),
190 );
191
192 let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx);
193 if !errors.is_empty() {
194 return Err(errors);
195 }
196
197 Ok(())
198 }
199}
200
201impl<'tcx, E> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx, E>
202where
203 E: FromSolverError<'tcx, NextSolverError<'tcx>> + Debug,
204{
205 type Error = Vec<E>;
206
207 fn cx(&self) -> TyCtxt<'tcx> {
208 self.at.infcx.tcx
209 }
210
211 fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
212 &mut self,
213 t: ty::Binder<'tcx, T>,
214 ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
215 self.universes.push(None);
216 let t = t.try_super_fold_with(self)?;
217 self.universes.pop();
218 Ok(t)
219 }
220
221 #[instrument(level = "trace", skip(self), ret)]
222 fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
223 let infcx = self.at.infcx;
224 debug_assert_eq!(ty, infcx.shallow_resolve(ty));
225 if !ty.has_aliases() {
226 return Ok(ty);
227 }
228
229 let ty::Alias(..) = *ty.kind() else { return ty.try_super_fold_with(self) };
230
231 if ty.has_escaping_bound_vars() {
232 let (ty, mapped_regions, mapped_types, mapped_consts) =
233 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty);
234 let result = ensure_sufficient_stack(|| self.normalize_alias_ty(ty))?;
235 Ok(PlaceholderReplacer::replace_placeholders(
236 infcx,
237 mapped_regions,
238 mapped_types,
239 mapped_consts,
240 &self.universes,
241 result,
242 ))
243 } else {
244 ensure_sufficient_stack(|| self.normalize_alias_ty(ty))
245 }
246 }
247
248 #[instrument(level = "trace", skip(self), ret)]
249 fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
250 let infcx = self.at.infcx;
251 debug_assert_eq!(ct, infcx.shallow_resolve_const(ct));
252 if !ct.has_aliases() {
253 return Ok(ct);
254 }
255
256 let uv = match ct.kind() {
257 ty::ConstKind::Unevaluated(ct) => ct,
258 _ => return ct.try_super_fold_with(self),
259 };
260
261 if uv.has_escaping_bound_vars() {
262 let (uv, mapped_regions, mapped_types, mapped_consts) =
263 BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
264 let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv))?;
265 Ok(PlaceholderReplacer::replace_placeholders(
266 infcx,
267 mapped_regions,
268 mapped_types,
269 mapped_consts,
270 &self.universes,
271 result,
272 ))
273 } else {
274 ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv))
275 }
276 }
277}
278
279pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
281 infcx: &InferCtxt<'tcx>,
282 param_env: ty::ParamEnv<'tcx>,
283 t: T,
284) -> T {
285 t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
286 at: infcx.at(&ObligationCause::dummy(), param_env),
287 })
288}
289
290struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> {
291 at: At<'a, 'tcx>,
292}
293
294impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> {
295 fn cx(&self) -> TyCtxt<'tcx> {
296 self.at.infcx.tcx
297 }
298
299 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
300 let infcx = self.at.infcx;
301 let result =
302 infcx.commit_if_ok(|_| {
303 deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
304 _,
305 ScrubbedTraitError<'tcx>,
306 >(self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()])
307 });
308 match result {
309 Ok((ty, _)) => ty,
310 Err(_) => ty.super_fold_with(self),
311 }
312 }
313
314 fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
315 let infcx = self.at.infcx;
316 let result =
317 infcx.commit_if_ok(|_| {
318 deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
319 _,
320 ScrubbedTraitError<'tcx>,
321 >(self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()])
322 });
323 match result {
324 Ok((ct, _)) => ct,
325 Err(_) => ct.super_fold_with(self),
326 }
327 }
328}