| use rustc_infer::infer::InferCtxt; |
| use rustc_infer::infer::at::At; |
| use rustc_infer::traits::solve::Goal; |
| use rustc_infer::traits::{ |
| FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, |
| }; |
| use rustc_middle::traits::ObligationCause; |
| use rustc_middle::ty::{ |
| self, Binder, Flags, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, |
| UniverseIndex, Unnormalized, |
| }; |
| use rustc_next_trait_solver::normalize::NormalizationFolder; |
| use rustc_next_trait_solver::solve::SolverDelegateEvalExt; |
| |
| use super::{FulfillmentCtxt, NextSolverError}; |
| use crate::solve::{Certainty, SolverDelegate}; |
| use crate::traits::{BoundVarReplacer, ScrubbedTraitError}; |
| |
| /// see `normalize_with_universes`. |
| pub fn normalize<'tcx, T>(at: At<'_, 'tcx>, value: Unnormalized<'tcx, T>) -> Normalized<'tcx, T> |
| where |
| T: TypeFoldable<TyCtxt<'tcx>>, |
| { |
| normalize_with_universes(at, value, vec![]) |
| } |
| |
| /// Like `deeply_normalize`, but we handle ambiguity and inference variables in this routine. |
| /// The behavior should be same as the old solver. |
| /// For error, we return an infer var plus the failed obligation. |
| /// For ambiguity, we have two cases: |
| /// - has_escaping_bound_vars: return the original alias. |
| /// - otherwise: return the normalized result. It can be (partially) inferred |
| /// even if the evaluation result is ambiguous. |
| fn normalize_with_universes<'tcx, T>( |
| at: At<'_, 'tcx>, |
| value: Unnormalized<'tcx, T>, |
| universes: Vec<Option<UniverseIndex>>, |
| ) -> Normalized<'tcx, T> |
| where |
| T: TypeFoldable<TyCtxt<'tcx>>, |
| { |
| let infcx = at.infcx; |
| let value = value.skip_normalization(); |
| let value = infcx.resolve_vars_if_possible(value); |
| let original_value = value.clone(); |
| let mut folder = |
| NormalizationFolder::new(infcx, universes.clone(), Default::default(), |alias_term| { |
| let delegate = <&SolverDelegate<'tcx>>::from(infcx); |
| let infer_term = delegate.next_term_var_of_kind(alias_term, at.cause.span); |
| let predicate = ty::PredicateKind::AliasRelate( |
| alias_term.into(), |
| infer_term.into(), |
| ty::AliasRelationDirection::Equate, |
| ); |
| let goal = Goal::new(infcx.tcx, at.param_env, predicate); |
| let result = delegate.evaluate_root_goal(goal, at.cause.span, None)?; |
| let normalized = infcx.resolve_vars_if_possible(infer_term); |
| let stalled_goal = match result.certainty { |
| Certainty::Yes => None, |
| Certainty::Maybe { .. } => Some(infcx.resolve_vars_if_possible(result.goal)), |
| }; |
| Ok((normalized, stalled_goal)) |
| }); |
| if let Ok(value) = value.try_fold_with(&mut folder) { |
| let obligations = folder |
| .stalled_goals() |
| .into_iter() |
| .map(|goal| { |
| Obligation::new(infcx.tcx, at.cause.clone(), goal.param_env, goal.predicate) |
| }) |
| .collect(); |
| Normalized { value, obligations } |
| } else { |
| let mut replacer = ReplaceAliasWithInfer { at, obligations: Default::default(), universes }; |
| let value = original_value.fold_with(&mut replacer); |
| Normalized { value, obligations: replacer.obligations } |
| } |
| } |
| |
| struct ReplaceAliasWithInfer<'me, 'tcx> { |
| at: At<'me, 'tcx>, |
| obligations: PredicateObligations<'tcx>, |
| universes: Vec<Option<UniverseIndex>>, |
| } |
| |
| impl<'me, 'tcx> ReplaceAliasWithInfer<'me, 'tcx> { |
| fn term_to_infer(&mut self, alias_term: ty::Term<'tcx>) -> ty::Term<'tcx> { |
| let infcx = self.at.infcx; |
| let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span); |
| let obligation = Obligation::new( |
| infcx.tcx, |
| self.at.cause.clone(), |
| self.at.param_env, |
| ty::PredicateKind::AliasRelate( |
| alias_term.into(), |
| infer_term.into(), |
| ty::AliasRelationDirection::Equate, |
| ), |
| ); |
| self.obligations.push(obligation); |
| infer_term |
| } |
| } |
| |
| impl<'me, 'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'me, 'tcx> { |
| fn cx(&self) -> TyCtxt<'tcx> { |
| self.at.infcx.tcx |
| } |
| |
| fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( |
| &mut self, |
| t: Binder<'tcx, T>, |
| ) -> Binder<'tcx, T> { |
| self.universes.push(None); |
| let t = t.super_fold_with(self); |
| self.universes.pop(); |
| t |
| } |
| |
| fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { |
| if !ty.has_aliases() { |
| return ty; |
| } |
| |
| let ty = ty.super_fold_with(self); |
| let ty::Alias(..) = *ty.kind() else { return ty }; |
| |
| if ty.has_escaping_bound_vars() { |
| let (replaced, ..) = |
| BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, ty); |
| let _ = self.term_to_infer(replaced.into()); |
| ty |
| } else { |
| self.term_to_infer(ty.into()).expect_type() |
| } |
| } |
| |
| fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { |
| if !ct.has_aliases() { |
| return ct; |
| } |
| |
| let ct = ct.super_fold_with(self); |
| let ty::ConstKind::Unevaluated(..) = ct.kind() else { return ct }; |
| |
| if ct.has_escaping_bound_vars() { |
| let (replaced, ..) = |
| BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, ct); |
| let _ = self.term_to_infer(replaced.into()); |
| ct |
| } else { |
| self.term_to_infer(ct.into()).expect_const() |
| } |
| } |
| } |
| |
| /// 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: Unnormalized<'tcx, T>, |
| ) -> Result<T, Vec<E>> |
| where |
| T: TypeFoldable<TyCtxt<'tcx>>, |
| E: FromSolverError<'tcx, NextSolverError<'tcx>>, |
| { |
| assert!(!value.as_ref().skip_normalization().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: Unnormalized<'tcx, T>, |
| universes: Vec<Option<UniverseIndex>>, |
| ) -> Result<T, Vec<E>> |
| where |
| T: TypeFoldable<TyCtxt<'tcx>>, |
| 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: Unnormalized<'tcx, T>, |
| universes: Vec<Option<UniverseIndex>>, |
| ) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>> |
| where |
| T: TypeFoldable<TyCtxt<'tcx>>, |
| E: FromSolverError<'tcx, NextSolverError<'tcx>>, |
| { |
| let Normalized { value, obligations } = normalize_with_universes(at, value, universes); |
| |
| let mut fulfill_cx = FulfillmentCtxt::new(at.infcx); |
| for pred in obligations { |
| fulfill_cx.register_predicate_obligation(at.infcx, pred); |
| } |
| |
| let errors = fulfill_cx.try_evaluate_obligations(at.infcx); |
| if !errors.is_empty() { |
| return Err(errors); |
| } |
| |
| let stalled_coroutine_goals = fulfill_cx |
| .drain_stalled_obligations_for_coroutines(at.infcx) |
| .into_iter() |
| .map(|obl| obl.as_goal()) |
| .collect(); |
| |
| let errors = fulfill_cx.collect_remaining_errors(at.infcx); |
| if !errors.is_empty() { |
| return Err(errors); |
| } |
| |
| Ok((value, stalled_coroutine_goals)) |
| } |
| |
| // Deeply normalize a value and return it |
| pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( |
| 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<TyCtxt<'tcx>> 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<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| { |
| deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( |
| self.at, |
| Unnormalized::new_wip(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<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| { |
| deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( |
| self.at, |
| Unnormalized::new_wip(ct), |
| vec![None; ct.outer_exclusive_binder().as_usize()], |
| ) |
| }); |
| match result { |
| Ok((ct, _)) => ct, |
| Err(_) => ct.super_fold_with(self), |
| } |
| } |
| } |