use std::fmt::Debug; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::InferCtxt; use rustc_infer::infer::at::At; use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, }; use tracing::instrument; use super::{FulfillmentCtxt, NextSolverError}; use crate::error_reporting::InferCtxtErrorExt; use crate::error_reporting::traits::OverflowCause; use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. pub fn deeply_normalize<'tcx, T, E>(at: At<'_, 'tcx>, value: T) -> Result> where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { assert!(!value.has_escaping_bound_vars()); deeply_normalize_with_skipped_universes(at, value, vec![]) } /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. /// /// Additionally takes a list of universes which represents the binders which have been /// entered before passing `value` to the function. This is currently needed for /// `normalize_erasing_regions`, which skips binders as it walks through a type. pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>( at: At<'_, 'tcx>, value: T, universes: Vec>, ) -> Result> where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { let (value, coroutine_goals) = deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, value, universes, )?; assert_eq!(coroutine_goals, vec![]); Ok(value) } /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. /// /// Additionally takes a list of universes which represents the binders which have been /// entered before passing `value` to the function. This is currently needed for /// `normalize_erasing_regions`, which skips binders as it walks through a type. /// /// This returns a set of stalled obligations involving coroutines if the typing mode of /// the underlying infcx has any stalled coroutine def ids. pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'tcx, T, E>( at: At<'_, 'tcx>, value: T, universes: Vec>, ) -> Result<(T, Vec>>), Vec> where T: TypeFoldable>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { let fulfill_cx = FulfillmentCtxt::new(at.infcx); let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_coroutine_goals: vec![], }; let value = value.try_fold_with(&mut folder)?; let errors = folder.fulfill_cx.select_all_or_error(at.infcx); if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'tcx, E> { at: At<'me, 'tcx>, fulfill_cx: FulfillmentCtxt<'tcx, E>, depth: usize, universes: Vec>, stalled_coroutine_goals: Vec>>, } impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> where E: FromSolverError<'tcx, NextSolverError<'tcx>>, { fn normalize_alias_term( &mut self, alias_term: ty::Term<'tcx>, ) -> Result, Vec> { let infcx = self.at.infcx; let tcx = infcx.tcx; let recursion_limit = tcx.recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { let term = alias_term.to_alias_term().unwrap(); self.at.infcx.err_ctxt().report_overflow_error( OverflowCause::DeeplyNormalize(term), self.at.cause.span, true, |_| {}, ); } self.depth += 1; let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span); let obligation = Obligation::new( tcx, self.at.cause.clone(), self.at.param_env, ty::PredicateKind::AliasRelate( alias_term.into(), infer_term.into(), ty::AliasRelationDirection::Equate, ), ); self.fulfill_cx.register_predicate_obligation(infcx, obligation); self.select_all_and_stall_coroutine_predicates()?; // Alias is guaranteed to be fully structurally resolved, // so we can super fold here. let term = infcx.resolve_vars_if_possible(infer_term); // super-folding the `term` will directly fold the `Ty` or `Const` so // we have to match on the term and super-fold them manually. let result = match term.kind() { ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), }; self.depth -= 1; Ok(result) } fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec> { let errors = self.fulfill_cx.select_where_possible(self.at.infcx); if !errors.is_empty() { return Err(errors); } self.stalled_coroutine_goals.extend( self.fulfill_cx .drain_stalled_obligations_for_coroutines(self.at.infcx) .into_iter() .map(|obl| obl.as_goal()), ); let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); if !errors.is_empty() { return Err(errors); } Ok(()) } } impl<'tcx, E> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx, E> where E: FromSolverError<'tcx, NextSolverError<'tcx>> + Debug, { type Error = Vec; fn cx(&self) -> TyCtxt<'tcx> { self.at.infcx.tcx } fn try_fold_binder>>( &mut self, t: ty::Binder<'tcx, T>, ) -> Result, Self::Error> { self.universes.push(None); let t = t.try_super_fold_with(self)?; self.universes.pop(); Ok(t) } #[instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); if !ty.has_aliases() { return Ok(ty); } let ty::Alias(..) = *ty.kind() else { return ty.try_super_fold_with(self) }; if ty.has_escaping_bound_vars() { let (ty, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); let result = ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type(); Ok(PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, result, )) } else { Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type()) } } #[instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); if !ct.has_aliases() { return Ok(ct); } let ty::ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) }; if ct.has_escaping_bound_vars() { let (ct, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct); let result = ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const(); Ok(PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, result, )) } else { Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const()) } } } // Deeply normalize a value and return it pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable>>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, t: T, ) -> T { t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { at: infcx.at(&ObligationCause::dummy(), param_env), }) } struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> { at: At<'a, 'tcx>, } impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.at.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { let infcx = self.at.infcx; let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()], ) }); match result { Ok((ty, _)) => ty, Err(_) => ty.super_fold_with(self), } } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { let infcx = self.at.infcx; let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()], ) }); match result { Ok((ct, _)) => ct, Err(_) => ct.super_fold_with(self), } } }