| //! Deeply normalize types using the old trait solver. | |
| use rustc_data_structures::stack::ensure_sufficient_stack; | |
| use rustc_errors::msg; | |
| use rustc_hir::def::DefKind; | |
| use rustc_infer::infer::at::At; | |
| use rustc_infer::infer::{InferCtxt, InferOk}; | |
| use rustc_infer::traits::{ | |
| FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, | |
| }; | |
| use rustc_macros::extension; | |
| use rustc_middle::span_bug; | |
| use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; | |
| use rustc_middle::ty::{ | |
| self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, | |
| TypeVisitableExt, TypingMode, | |
| }; | |
| use tracing::{debug, instrument}; | |
| use super::{BoundVarReplacer, PlaceholderReplacer, SelectionContext, project}; | |
| use crate::error_reporting::InferCtxtErrorExt; | |
| use crate::error_reporting::traits::OverflowCause; | |
| use crate::solve::NextSolverError; | |
| #[extension(pub trait NormalizeExt<'tcx>)] | |
| impl<'tcx> At<'_, 'tcx> { | |
| /// Normalize a value using the `AssocTypeNormalizer`. | |
| /// | |
| /// This normalization should be used when the type contains inference variables or the | |
| /// projection may be fallible. | |
| fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> { | |
| if self.infcx.next_trait_solver() { | |
| InferOk { value, obligations: PredicateObligations::new() } | |
| } else { | |
| let mut selcx = SelectionContext::new(self.infcx); | |
| let Normalized { value, obligations } = | |
| normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); | |
| InferOk { value, obligations } | |
| } | |
| } | |
| /// Deeply normalizes `value`, replacing all aliases which can by normalized in | |
| /// the current environment. In the new solver this errors in case normalization | |
| /// fails or is ambiguous. | |
| /// | |
| /// In the old solver this simply uses `normalizes` and adds the nested obligations | |
| /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the | |
| /// same goals in both a temporary and the shared context which negatively impacts | |
| /// performance as these don't share caching. | |
| /// | |
| /// FIXME(-Znext-solver=no): For performance reasons, we currently reuse an existing | |
| /// fulfillment context in the old solver. Once we have removed the old solver, we | |
| /// can remove the `fulfill_cx` parameter on this function. | |
| fn deeply_normalize<T, E>( | |
| self, | |
| value: T, | |
| fulfill_cx: &mut dyn TraitEngine<'tcx, E>, | |
| ) -> Result<T, Vec<E>> | |
| where | |
| T: TypeFoldable<TyCtxt<'tcx>>, | |
| E: FromSolverError<'tcx, NextSolverError<'tcx>>, | |
| { | |
| if self.infcx.next_trait_solver() { | |
| crate::solve::deeply_normalize(self, value) | |
| } else { | |
| if fulfill_cx.has_pending_obligations() { | |
| let pending_obligations = fulfill_cx.pending_obligations(); | |
| span_bug!( | |
| pending_obligations[0].cause.span, | |
| "deeply_normalize should not be called with pending obligations: \ | |
| {pending_obligations:#?}" | |
| ); | |
| } | |
| let value = self | |
| .normalize(value) | |
| .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); | |
| let errors = fulfill_cx.evaluate_obligations_error_on_ambiguity(self.infcx); | |
| let value = self.infcx.resolve_vars_if_possible(value); | |
| if errors.is_empty() { | |
| Ok(value) | |
| } else { | |
| // Drop pending obligations, since deep normalization may happen | |
| // in a loop and we don't want to trigger the assertion on the next | |
| // iteration due to pending ambiguous obligations we've left over. | |
| let _ = fulfill_cx.collect_remaining_errors(self.infcx); | |
| Err(errors) | |
| } | |
| } | |
| } | |
| } | |
| /// As `normalize`, but with a custom depth. | |
| pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( | |
| selcx: &'a mut SelectionContext<'b, 'tcx>, | |
| param_env: ty::ParamEnv<'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| depth: usize, | |
| value: T, | |
| ) -> Normalized<'tcx, T> | |
| where | |
| T: TypeFoldable<TyCtxt<'tcx>>, | |
| { | |
| let mut obligations = PredicateObligations::new(); | |
| let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); | |
| Normalized { value, obligations } | |
| } | |
| #[instrument(level = "info", skip(selcx, param_env, cause, obligations))] | |
| pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( | |
| selcx: &'a mut SelectionContext<'b, 'tcx>, | |
| param_env: ty::ParamEnv<'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| depth: usize, | |
| value: T, | |
| obligations: &mut PredicateObligations<'tcx>, | |
| ) -> T | |
| where | |
| T: TypeFoldable<TyCtxt<'tcx>>, | |
| { | |
| debug!(obligations.len = obligations.len()); | |
| let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); | |
| let result = ensure_sufficient_stack(|| AssocTypeNormalizer::fold(&mut normalizer, value)); | |
| debug!(?result, obligations.len = normalizer.obligations.len()); | |
| debug!(?normalizer.obligations,); | |
| result | |
| } | |
| pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( | |
| infcx: &InferCtxt<'tcx>, | |
| value: &T, | |
| ) -> bool { | |
| let mut flags = ty::TypeFlags::HAS_ALIAS; | |
| // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, | |
| // so we can ignore those. | |
| match infcx.typing_mode() { | |
| // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis | |
| TypingMode::Coherence | |
| | TypingMode::Analysis { .. } | |
| | TypingMode::Borrowck { .. } | |
| | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE), | |
| TypingMode::PostAnalysis => {} | |
| } | |
| value.has_type_flags(flags) | |
| } | |
| struct AssocTypeNormalizer<'a, 'b, 'tcx> { | |
| selcx: &'a mut SelectionContext<'b, 'tcx>, | |
| param_env: ty::ParamEnv<'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| obligations: &'a mut PredicateObligations<'tcx>, | |
| depth: usize, | |
| universes: Vec<Option<ty::UniverseIndex>>, | |
| } | |
| impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { | |
| fn new( | |
| selcx: &'a mut SelectionContext<'b, 'tcx>, | |
| param_env: ty::ParamEnv<'tcx>, | |
| cause: ObligationCause<'tcx>, | |
| depth: usize, | |
| obligations: &'a mut PredicateObligations<'tcx>, | |
| ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { | |
| debug_assert!(!selcx.infcx.next_trait_solver()); | |
| AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] } | |
| } | |
| fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { | |
| let value = self.selcx.infcx.resolve_vars_if_possible(value); | |
| debug!(?value); | |
| assert!( | |
| !value.has_escaping_bound_vars(), | |
| "Normalizing {value:?} without wrapping in a `Binder`" | |
| ); | |
| if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) } | |
| } | |
| // FIXME(mgca): While this supports constants, it is only used for types by default right now | |
| #[instrument(level = "debug", skip(self), ret)] | |
| fn normalize_trait_projection(&mut self, proj: AliasTerm<'tcx>) -> Term<'tcx> { | |
| if !proj.has_escaping_bound_vars() { | |
| // When we don't have escaping bound vars we can normalize ambig aliases | |
| // to inference variables (done in `normalize_projection_ty`). This would | |
| // be wrong if there were escaping bound vars as even if we instantiated | |
| // the bound vars with placeholders, we wouldn't be able to map them back | |
| // after normalization succeeded. | |
| // | |
| // Also, as an optimization: when we don't have escaping bound vars, we don't | |
| // need to replace them with placeholders (see branch below). | |
| let proj = proj.fold_with(self); | |
| project::normalize_projection_term( | |
| self.selcx, | |
| self.param_env, | |
| proj, | |
| self.cause.clone(), | |
| self.depth, | |
| self.obligations, | |
| ) | |
| } else { | |
| // If there are escaping bound vars, we temporarily replace the | |
| // bound vars with placeholders. Note though, that in the case | |
| // that we still can't project for whatever reason (e.g. self | |
| // type isn't known enough), we *can't* register an obligation | |
| // and return an inference variable (since then that obligation | |
| // would have bound vars and that's a can of worms). Instead, | |
| // we just give up and fall back to pretending like we never tried! | |
| // | |
| // Note: this isn't necessarily the final approach here; we may | |
| // want to figure out how to register obligations with escaping vars | |
| // or handle this some other way. | |
| let infcx = self.selcx.infcx; | |
| let (proj, mapped_regions, mapped_types, mapped_consts) = | |
| BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, proj); | |
| let proj = proj.fold_with(self); | |
| let normalized_term = project::opt_normalize_projection_term( | |
| self.selcx, | |
| self.param_env, | |
| proj, | |
| self.cause.clone(), | |
| self.depth, | |
| self.obligations, | |
| ) | |
| .ok() | |
| .flatten() | |
| .unwrap_or_else(|| proj.to_term(infcx.tcx)); | |
| PlaceholderReplacer::replace_placeholders( | |
| infcx, | |
| mapped_regions, | |
| mapped_types, | |
| mapped_consts, | |
| &self.universes, | |
| normalized_term, | |
| ) | |
| } | |
| } | |
| // FIXME(mgca): While this supports constants, it is only used for types by default right now | |
| #[instrument(level = "debug", skip(self), ret)] | |
| fn normalize_inherent_projection(&mut self, inherent: AliasTerm<'tcx>) -> Term<'tcx> { | |
| if !inherent.has_escaping_bound_vars() { | |
| // When we don't have escaping bound vars we can normalize ambig aliases | |
| // to inference variables (done in `normalize_projection_ty`). This would | |
| // be wrong if there were escaping bound vars as even if we instantiated | |
| // the bound vars with placeholders, we wouldn't be able to map them back | |
| // after normalization succeeded. | |
| // | |
| // Also, as an optimization: when we don't have escaping bound vars, we don't | |
| // need to replace them with placeholders (see branch below). | |
| let inherent = inherent.fold_with(self); | |
| project::normalize_inherent_projection( | |
| self.selcx, | |
| self.param_env, | |
| inherent, | |
| self.cause.clone(), | |
| self.depth, | |
| self.obligations, | |
| ) | |
| } else { | |
| let infcx = self.selcx.infcx; | |
| let (inherent, mapped_regions, mapped_types, mapped_consts) = | |
| BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, inherent); | |
| let inherent = inherent.fold_with(self); | |
| let inherent = project::normalize_inherent_projection( | |
| self.selcx, | |
| self.param_env, | |
| inherent, | |
| self.cause.clone(), | |
| self.depth, | |
| self.obligations, | |
| ); | |
| PlaceholderReplacer::replace_placeholders( | |
| infcx, | |
| mapped_regions, | |
| mapped_types, | |
| mapped_consts, | |
| &self.universes, | |
| inherent, | |
| ) | |
| } | |
| } | |
| // FIXME(mgca): While this supports constants, it is only used for types by default right now | |
| #[instrument(level = "debug", skip(self), ret)] | |
| fn normalize_free_alias(&mut self, free: AliasTerm<'tcx>) -> Term<'tcx> { | |
| let recursion_limit = self.cx().recursion_limit(); | |
| if !recursion_limit.value_within_limit(self.depth) { | |
| self.selcx.infcx.err_ctxt().report_overflow_error( | |
| OverflowCause::DeeplyNormalize(free.into()), | |
| self.cause.span, | |
| false, | |
| |diag| { | |
| diag.note(msg!("in case this is a recursive type alias, consider using a struct, enum, or union instead")); | |
| }, | |
| ); | |
| } | |
| // We don't replace bound vars in the generic arguments of the free alias with | |
| // placeholders. This doesn't cause any issues as instantiating parameters with | |
| // bound variables is special-cased to rewrite the debruijn index to be higher | |
| // whenever we fold through a binder. | |
| // | |
| // However, we do replace any escaping bound vars in the resulting goals with | |
| // placeholders as the trait solver does not expect to encounter escaping bound | |
| // vars in obligations. | |
| // | |
| // FIXME(lazy_type_alias): Check how much this actually matters for perf before | |
| // stabilization. This is a bit weird and generally not how we handle binders in | |
| // the compiler so ideally we'd do the same boundvar->placeholder->boundvar dance | |
| // that other kinds of normalization do. | |
| let infcx = self.selcx.infcx; | |
| self.obligations.extend( | |
| infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map( | |
| |(mut predicate, span)| { | |
| if free.has_escaping_bound_vars() { | |
| (predicate, ..) = BoundVarReplacer::replace_bound_vars( | |
| infcx, | |
| &mut self.universes, | |
| predicate, | |
| ); | |
| } | |
| let mut cause = self.cause.clone(); | |
| cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, free.def_id)); | |
| Obligation::new(infcx.tcx, cause, self.param_env, predicate) | |
| }, | |
| ), | |
| ); | |
| self.depth += 1; | |
| let res = if free.kind(infcx.tcx).is_type() { | |
| infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into() | |
| } else { | |
| infcx | |
| .tcx | |
| .const_of_item(free.def_id) | |
| .instantiate(infcx.tcx, free.args) | |
| .fold_with(self) | |
| .into() | |
| }; | |
| self.depth -= 1; | |
| res | |
| } | |
| } | |
| impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { | |
| fn cx(&self) -> TyCtxt<'tcx> { | |
| self.selcx.tcx() | |
| } | |
| fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( | |
| &mut self, | |
| t: ty::Binder<'tcx, T>, | |
| ) -> ty::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 !needs_normalization(self.selcx.infcx, &ty) { | |
| return ty; | |
| } | |
| let (kind, data) = match *ty.kind() { | |
| ty::Alias(kind, data) => (kind, data), | |
| _ => return ty.super_fold_with(self), | |
| }; | |
| // We try to be a little clever here as a performance optimization in | |
| // cases where there are nested projections under binders. | |
| // For example: | |
| // ``` | |
| // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) | |
| // ``` | |
| // We normalize the args on the projection before the projecting, but | |
| // if we're naive, we'll | |
| // replace bound vars on inner, project inner, replace placeholders on inner, | |
| // replace bound vars on outer, project outer, replace placeholders on outer | |
| // | |
| // However, if we're a bit more clever, we can replace the bound vars | |
| // on the entire type before normalizing nested projections, meaning we | |
| // replace bound vars on outer, project inner, | |
| // project outer, replace placeholders on outer | |
| // | |
| // This is possible because the inner `'a` will already be a placeholder | |
| // when we need to normalize the inner projection | |
| // | |
| // On the other hand, this does add a bit of complexity, since we only | |
| // replace bound vars if the current type is a `Projection` and we need | |
| // to make sure we don't forget to fold the args regardless. | |
| match kind { | |
| ty::Opaque => { | |
| // Only normalize `impl Trait` outside of type inference, usually in codegen. | |
| match self.selcx.infcx.typing_mode() { | |
| // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis | |
| TypingMode::Coherence | |
| | TypingMode::Analysis { .. } | |
| | TypingMode::Borrowck { .. } | |
| | TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self), | |
| TypingMode::PostAnalysis => { | |
| let recursion_limit = self.cx().recursion_limit(); | |
| if !recursion_limit.value_within_limit(self.depth) { | |
| self.selcx.infcx.err_ctxt().report_overflow_error( | |
| OverflowCause::DeeplyNormalize(data.into()), | |
| self.cause.span, | |
| true, | |
| |_| {}, | |
| ); | |
| } | |
| let args = data.args.fold_with(self); | |
| let generic_ty = self.cx().type_of(data.def_id); | |
| let concrete_ty = generic_ty.instantiate(self.cx(), args); | |
| self.depth += 1; | |
| let folded_ty = self.fold_ty(concrete_ty); | |
| self.depth -= 1; | |
| folded_ty | |
| } | |
| } | |
| } | |
| ty::Projection => self.normalize_trait_projection(data.into()).expect_type(), | |
| ty::Inherent => self.normalize_inherent_projection(data.into()).expect_type(), | |
| ty::Free => self.normalize_free_alias(data.into()).expect_type(), | |
| } | |
| } | |
| #[instrument(skip(self), level = "debug")] | |
| fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { | |
| let tcx = self.selcx.tcx(); | |
| if tcx.features().generic_const_exprs() | |
| // Normalize type_const items even with feature `generic_const_exprs`. | |
| && !matches!(ct.kind(), ty::ConstKind::Unevaluated(uv) if tcx.is_type_const(uv.def)) | |
| || !needs_normalization(self.selcx.infcx, &ct) | |
| { | |
| return ct; | |
| } | |
| let uv = match ct.kind() { | |
| ty::ConstKind::Unevaluated(uv) => uv, | |
| _ => return ct.super_fold_with(self), | |
| }; | |
| // Note that the AssocConst and Const cases are unreachable on stable, | |
| // unless a `min_generic_const_args` feature gate error has already | |
| // been emitted earlier in compilation. | |
| // | |
| // That's because we can only end up with an Unevaluated ty::Const for a const item | |
| // if it was marked with `type const`. Using this attribute without the mgca | |
| // feature gate causes a parse error. | |
| let ct = match tcx.def_kind(uv.def) { | |
| DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) { | |
| DefKind::Trait => self.normalize_trait_projection(uv.into()).expect_const(), | |
| DefKind::Impl { of_trait: false } => { | |
| self.normalize_inherent_projection(uv.into()).expect_const() | |
| } | |
| kind => unreachable!( | |
| "unexpected `DefKind` for const alias' resolution's parent def: {:?}", | |
| kind | |
| ), | |
| }, | |
| DefKind::Const => self.normalize_free_alias(uv.into()).expect_const(), | |
| DefKind::AnonConst => { | |
| let ct = ct.super_fold_with(self); | |
| super::with_replaced_escaping_bound_vars( | |
| self.selcx.infcx, | |
| &mut self.universes, | |
| ct, | |
| |ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env), | |
| ) | |
| } | |
| kind => { | |
| unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind) | |
| } | |
| }; | |
| // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be | |
| // unnormalized after const evaluation returns. | |
| ct.super_fold_with(self) | |
| } | |
| #[inline] | |
| fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { | |
| if p.allow_normalization() && needs_normalization(self.selcx.infcx, &p) { | |
| p.super_fold_with(self) | |
| } else { | |
| p | |
| } | |
| } | |
| } |