//! Deeply normalize types using the old trait solver. use rustc_data_structures::stack::ensure_sufficient_stack; 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>>(&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( self, value: T, fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> Result> where T: TypeFoldable>, 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.select_all_or_error(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>, { 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>, { 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>>( 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>, } 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>>(&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(crate::fluent_generated::trait_selection_ty_alias_overflow); }, ); } // 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 { // FIXME(mgca): once const items are actual aliases defined as equal to type system consts // this should instead use that rather than evaluating. super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env) .super_fold_with(self) .into() }; self.depth -= 1; res } } impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.selcx.tcx() } fn fold_binder>>( &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(::One<'a, Box::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() || !needs_normalization(self.selcx.infcx, &ct) { return ct; } // Doing "proper" normalization of const aliases is inherently cyclic until const items // are real aliases instead of having bodies. We gate proper const alias handling behind // mgca to avoid breaking stable code, though this should become the "main" codepath long // before mgca is stabilized. // // FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items // are represented. if tcx.features().min_generic_const_args() { let uv = match ct.kind() { ty::ConstKind::Unevaluated(uv) => uv, _ => return ct.super_fold_with(self), }; 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()), DefKind::Impl { of_trait: false } => { self.normalize_inherent_projection(uv.into()) } kind => unreachable!( "unexpected `DefKind` for const alias' resolution's parent def: {:?}", kind ), }, DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()), 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.expect_const().super_fold_with(self) } else { let ct = ct.super_fold_with(self); return super::with_replaced_escaping_bound_vars( self.selcx.infcx, &mut self.universes, ct, |ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env), ) .super_fold_with(self); // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be // unnormalized after const evaluation returns. } } #[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 } } }