diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3ccf78567ed..296b63a8292 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1425,17 +1425,16 @@ fn lookup_inherent_assoc_ty( vec![] }; - let (impl_, (assoc_item, def_scope)) = - crate::traits::project::with_replaced_escaping_bound_vars( - infcx, - &mut universes, - self_ty, - |self_ty| { - self.select_inherent_assoc_type_candidates( - infcx, name, span, self_ty, param_env, candidates, - ) - }, - )?; + let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars( + infcx, + &mut universes, + self_ty, + |self_ty| { + self.select_inherent_assoc_type_candidates( + infcx, name, span, self_ty, param_env, candidates, + ) + }, + )?; self.check_assoc_ty(assoc_item, name, def_scope, block, span); diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index d87cc89954a..b07702e8421 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,6 +1,6 @@ use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -205,10 +205,9 @@ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { } fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { - let reveal = self.at.param_env.reveal(); let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve(ct)); - if !needs_normalization(&ct, reveal) { + if !ct.has_projections() { return Ok(ct); } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7ad94274634..fd981130af8 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,5 +1,6 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::normalize::normalize_with_depth_to; use rustc_data_structures::captures::Captures; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; @@ -312,7 +313,7 @@ fn process_obligation( if obligation.predicate.has_projections() { let mut obligations = Vec::new(); - let predicate = crate::traits::project::try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( &mut self.selcx, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7caaccab63b..9eec60ea06c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -9,6 +9,7 @@ pub mod error_reporting; mod fulfill; pub mod misc; +pub mod normalize; mod object_safety; pub mod outlives_bounds; pub mod project; @@ -40,17 +41,15 @@ use std::fmt::Debug; use std::ops::ControlFlow; -pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; - pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::normalize::NormalizeExt; pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::object_safety_violations_for_assoc_item; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::NormalizeExt; pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; @@ -68,6 +67,7 @@ }; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; +pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; @@ -119,9 +119,7 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound (note that this is -/// conservative towards *no impl*, which is the opposite of the -/// `evaluate` methods). +/// `bound` or is not known to meet bound. pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -129,50 +127,8 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) -} - -/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? -/// -/// Ping me on zulip if you want to use this method and need help with finding -/// an appropriate replacement. -#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] -fn pred_known_to_hold_modulo_regions<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pred: impl ToPredicate<'tcx>, -) -> bool { - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); - - let result = infcx.evaluate_obligation_no_overflow(&obligation); - debug!(?result); - - if result.must_apply_modulo_regions() { - true - } else if result.may_apply() { - // Sometimes obligations are ambiguous because the recursive evaluator - // is not smart enough, so we fall back to fulfillment when we're not certain - // that an obligation holds or not. Even still, we must make sure that - // the we do no inference in the process of checking this obligation. - let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - infcx.probe(|_| { - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligation(obligation); - - let errors = ocx.select_all_or_error(); - match errors.as_slice() { - // Only known to hold if we did no inference. - [] => infcx.shallow_resolve(goal) == goal, - - errors => { - debug!(?errors); - false - } - } - }) - } else { - false - } + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } #[instrument(level = "debug", skip(tcx, elaborated_env))] diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs new file mode 100644 index 00000000000..ac62f875fb9 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -0,0 +1,423 @@ +//! Deeply normalize types using the old trait solver. +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_infer::infer::at::At; +use rustc_infer::infer::InferOk; +use rustc_infer::traits::PredicateObligation; +use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine}; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; +use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt}; + +use super::error_reporting::TypeErrCtxtExt; +use super::SelectionContext; +use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; + +#[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: Vec::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): This has the same behavior as `traits::fully_normalize` + /// in the new solver, but because of performance reasons, we currently reuse an + /// existing fulfillment context in the old solver. Once we also eagerly prove goals with + /// the old solver or have removed the old solver, remove `traits::fully_normalize` and + /// rename this function to `At::fully_normalize`. + fn deeply_normalize>>( + self, + value: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result>> { + if self.infcx.next_trait_solver() { + crate::solve::deeply_normalize(self, value) + } else { + let value = self + .normalize(value) + .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); + let errors = fulfill_cx.select_where_possible(self.infcx); + let value = self.infcx.resolve_vars_if_possible(value); + if errors.is_empty() { Ok(value) } else { 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 = Vec::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 Vec>, +) -> 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(|| normalizer.fold(value)); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); + result +} + +pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( + value: &T, + reveal: Reveal, +) -> bool { + match reveal { + Reveal::UserFacing => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION, + ), + Reveal::All => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_TY_OPAQUE + | ty::TypeFlags::HAS_CT_PROJECTION, + ), + } +} + +struct AssocTypeNormalizer<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + obligations: &'a mut Vec>, + 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 Vec>, + ) -> 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(&value, self.param_env.reveal()) { + value + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx> { + fn interner(&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(&ty, self.param_env.reveal()) { + return ty; + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => 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.param_env.reveal() { + Reveal::UserFacing => ty.super_fold_with(self), + + Reveal::All => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, + ); + } + + let args = data.args.fold_with(self); + let generic_ty = self.interner().type_of(data.def_id); + let concrete_ty = generic_ty.instantiate(self.interner(), args); + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty + } + } + } + + ty::Projection if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + let data = data.fold_with(self); + let normalized_ty = project::normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty.ty().unwrap() + } + + ty::Projection => { + // 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 (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let normalized_ty = project::opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .map(|term| term.ty().unwrap()) + .map(|normalized_ty| { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_ty, + ) + }) + .unwrap_or_else(|| ty.super_fold_with(self)); + + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } + ty::Weak => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + false, + |diag| { + diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + }, + ); + } + + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( + |(mut predicate, span)| { + if data.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, data.def_id) + }); + Obligation::new(infcx.tcx, cause, self.param_env, predicate) + }, + ), + ); + self.depth += 1; + let res = infcx + .tcx + .type_of(data.def_id) + .instantiate(infcx.tcx, data.args) + .fold_with(self); + self.depth -= 1; + res + } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` + // here like `ty::Projection`? + project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = project::normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } + } + } + + #[instrument(skip(self), level = "debug")] + fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { + let tcx = self.selcx.tcx(); + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { + constant + } else { + let constant = constant.super_fold_with(self); + debug!(?constant, ?self.param_env); + with_replaced_escaping_bound_vars( + self.selcx.infcx, + &mut self.universes, + constant, + |constant| constant.normalize(tcx, self.param_env), + ) + } + } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 279c0003187..9c532ea4d8d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,5 +1,7 @@ //! Code for projecting associated types out of trait references. +use std::ops::ControlFlow; + use super::check_args_compatible; use super::specialization_graph; use super::translate_args; @@ -18,8 +20,9 @@ use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk}; -use crate::traits::error_reporting::TypeErrCtxtExt as _; +use crate::infer::{BoundRegionConversionTime, InferOk}; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; @@ -27,21 +30,14 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::FulfillmentError; -use rustc_infer::traits::ObligationCauseCode; -use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; -use std::collections::BTreeMap; -use std::ops::ControlFlow; - pub use rustc_middle::traits::Reveal; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -52,55 +48,6 @@ pub(super) struct InProgress; -#[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: Vec::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. This only normalizes opaque types with `Reveal::All`. - /// - /// 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): This has the same behavior as `traits::fully_normalize` - /// in the new solver, but because of performance reasons, we currently reuse an - /// existing fulfillment context in the old solver. Once we also eagerly prove goals with - /// the old solver or have removed the old solver, remove `traits::fully_normalize` and - /// rename this function to `At::fully_normalize`. - fn deeply_normalize>>( - self, - value: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Result>> { - if self.infcx.next_trait_solver() { - crate::solve::deeply_normalize(self, value) - } else { - let value = self - .normalize(value) - .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_where_possible(self.infcx); - let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } - } - } -} - /// When attempting to resolve `::Name` ... #[derive(Debug)] pub enum ProjectionError<'tcx> { @@ -338,770 +285,6 @@ fn project_and_unify_type<'cx, 'tcx>( } } -/// 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 = Vec::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 Vec>, -) -> 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(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub(crate) fn try_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 Vec>, -) -> T -where - T: TypeFoldable>, -{ - debug!(obligations.len = obligations.len()); - let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement( - selcx, - param_env, - cause, - depth, - obligations, - ); - let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!(?result, obligations.len = normalizer.obligations.len()); - debug!(?normalizer.obligations,); - result -} - -pub(crate) fn needs_normalization<'tcx, T: TypeVisitable>>( - value: &T, - reveal: Reveal, -) -> bool { - match reveal { - Reveal::UserFacing => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - Reveal::All => value.has_type_flags( - ty::TypeFlags::HAS_TY_PROJECTION - | ty::TypeFlags::HAS_TY_INHERENT - | ty::TypeFlags::HAS_TY_OPAQUE - | ty::TypeFlags::HAS_CT_PROJECTION, - ), - } -} - -struct AssocTypeNormalizer<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - obligations: &'a mut Vec>, - depth: usize, - universes: Vec>, - /// If true, when a projection is unable to be completed, an inference - /// variable will be created and an obligation registered to project to that - /// inference variable. Also, constants will be eagerly evaluated. - eager_inference_replacement: bool, -} - -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 Vec>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - debug_assert!(!selcx.infcx.next_trait_solver()); - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: true, - } - } - - fn new_without_eager_inference_replacement( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, - obligations: &'a mut Vec>, - ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { - AssocTypeNormalizer { - selcx, - param_env, - cause, - obligations, - depth, - universes: vec![], - eager_inference_replacement: false, - } - } - - 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(&value, self.param_env.reveal()) { - value - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx> { - fn interner(&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(&ty, self.param_env.reveal()) { - return ty; - } - - let (kind, data) = match *ty.kind() { - ty::Alias(kind, alias_ty) => (kind, alias_ty), - _ => 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.param_env.reveal() { - Reveal::UserFacing => ty.super_fold_with(self), - - Reveal::All => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - true, - |_| {}, - ); - } - - let args = data.args.fold_with(self); - let generic_ty = self.interner().type_of(data.def_id); - let concrete_ty = generic_ty.instantiate(self.interner(), args); - self.depth += 1; - let folded_ty = self.fold_ty(concrete_ty); - self.depth -= 1; - folded_ty - } - } - } - - ty::Projection if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - let data = data.fold_with(self); - let normalized_ty = if self.eager_inference_replacement { - normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } else { - opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .unwrap_or_else(|| ty.super_fold_with(self).into()) - }; - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty.ty().unwrap() - } - - ty::Projection => { - // 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 (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let normalized_ty = opt_normalize_projection_type( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - .ok() - .flatten() - .map(|term| term.ty().unwrap()) - .map(|normalized_ty| { - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - normalized_ty, - ) - }) - .unwrap_or_else(|| ty.super_fold_with(self)); - - debug!( - ?self.depth, - ?ty, - ?normalized_ty, - obligations.len = ?self.obligations.len(), - "AssocTypeNormalizer: normalized type" - ); - normalized_ty - } - ty::Weak => { - let recursion_limit = self.interner().recursion_limit(); - if !recursion_limit.value_within_limit(self.depth) { - self.selcx.infcx.err_ctxt().report_overflow_error( - &ty, - self.cause.span, - false, - |diag| { - diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); - }, - ); - } - - let infcx = self.selcx.infcx; - self.obligations.extend( - infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map( - |(mut predicate, span)| { - if data.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, data.def_id) - }); - Obligation::new(infcx.tcx, cause, self.param_env, predicate) - }, - ), - ); - self.depth += 1; - let res = infcx - .tcx - .type_of(data.def_id) - .instantiate(infcx.tcx, data.args) - .fold_with(self); - self.depth -= 1; - res - } - - ty::Inherent if !data.has_escaping_bound_vars() => { - // This branch is *mostly* just an optimization: when we don't - // have escaping bound vars, we don't need to replace them with - // placeholders (see branch below). *Also*, we know that we can - // register an obligation to *later* project, since we know - // there won't be bound vars there. - - let data = data.fold_with(self); - - // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` - // here like `ty::Projection`? - normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ) - } - - ty::Inherent => { - let infcx = self.selcx.infcx; - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let data = data.fold_with(self); - let ty = normalize_inherent_projection( - self.selcx, - self.param_env, - data, - self.cause.clone(), - self.depth, - self.obligations, - ); - - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - &self.universes, - ty, - ) - } - } - } - - #[instrument(skip(self), level = "debug")] - fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs - || !needs_normalization(&constant, self.param_env.reveal()) - { - constant - } else { - let constant = constant.super_fold_with(self); - debug!(?constant, ?self.param_env); - with_replaced_escaping_bound_vars( - self.selcx.infcx, - &mut self.universes, - constant, - |constant| constant.normalize(tcx, self.param_env), - ) - } - } - - #[inline] - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { - p.super_fold_with(self) - } else { - p - } - } -} - -pub struct BoundVarReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - // These three maps track the bound variable that were replaced by placeholders. It might be - // nice to remove these since we already have the `kind` in the placeholder; we really just need - // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: BTreeMap, - mapped_types: BTreeMap, - mapped_consts: BTreeMap, - // The current depth relative to *this* folding, *not* the entire normalization. In other words, - // the depth of binders we've passed here. - current_index: ty::DebruijnIndex, - // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: - // we don't actually create a universe until we see a bound var we have to replace. - universe_indices: &'me mut Vec>, -} - -/// Executes `f` on `value` after replacing all escaping bound variables with placeholders -/// and then replaces these placeholders with the original bound variables in the result. -/// -/// In most places, bound variables should be replaced right when entering a binder, making -/// this function unnecessary. However, normalization currently does not do that, so we have -/// to do this lazily. -/// -/// You should not add any additional uses of this function, at least not without first -/// discussing it with t-types. -/// -/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during -/// normalization as well, at which point this function will be unnecessary and can be removed. -pub fn with_replaced_escaping_bound_vars< - 'a, - 'tcx, - T: TypeFoldable>, - R: TypeFoldable>, ->( - infcx: &'a InferCtxt<'tcx>, - universe_indices: &'a mut Vec>, - value: T, - f: impl FnOnce(T) -> R, -) -> R { - if value.has_escaping_bound_vars() { - let (value, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); - let result = f(value); - PlaceholderReplacer::replace_placeholders( - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - result, - ) - } else { - f(value) - } -} - -impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { - /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that - /// use a binding level above `universe_indices.len()`, we fail. - pub fn replace_bound_vars>>( - infcx: &'me InferCtxt<'tcx>, - universe_indices: &'me mut Vec>, - value: T, - ) -> ( - T, - BTreeMap, - BTreeMap, - BTreeMap, - ) { - let mapped_regions: BTreeMap = BTreeMap::new(); - let mapped_types: BTreeMap = BTreeMap::new(); - let mapped_consts: BTreeMap = BTreeMap::new(); - - let mut replacer = BoundVarReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - current_index: ty::INNERMOST, - universe_indices, - }; - - let value = value.fold_with(&mut replacer); - - (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) - } - - fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { - let infcx = self.infcx; - let index = - self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; - let universe = self.universe_indices[index].unwrap_or_else(|| { - for i in self.universe_indices.iter_mut().take(index + 1) { - *i = i.or_else(|| Some(infcx.create_next_universe())) - } - self.universe_indices[index].unwrap() - }); - universe - } -} - -impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReBound(debruijn, _) - if debruijn.as_usize() - >= self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderRegion { universe, bound: br }; - self.mapped_regions.insert(p, br); - ty::Region::new_placeholder(self.infcx.tcx, p) - } - _ => r, - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderType { universe, bound: bound_ty }; - self.mapped_types.insert(p, bound_ty); - Ty::new_placeholder(self.infcx.tcx, p) - } - _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), - _ => t, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match ct.kind() { - ty::ConstKind::Bound(debruijn, _) - if debruijn.as_usize() + 1 - > self.current_index.as_usize() + self.universe_indices.len() => - { - bug!( - "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", - self.universe_indices - ); - } - ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { - let universe = self.universe_for(debruijn); - let p = ty::PlaceholderConst { universe, bound: bound_const }; - self.mapped_consts.insert(p, bound_const); - ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) - } - _ => ct.super_fold_with(self), - } - } - - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } - } -} - -/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. -pub struct PlaceholderReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap, - mapped_types: BTreeMap, - mapped_consts: BTreeMap, - universe_indices: &'me [Option], - current_index: ty::DebruijnIndex, -} - -impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { - pub fn replace_placeholders>>( - infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap, - mapped_types: BTreeMap, - mapped_consts: BTreeMap, - universe_indices: &'me [Option], - value: T, - ) -> T { - let mut replacer = PlaceholderReplacer { - infcx, - mapped_regions, - mapped_types, - mapped_consts, - universe_indices, - current_index: ty::INNERMOST, - }; - value.fold_with(&mut replacer) - } -} - -impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - if !t.has_placeholders() && !t.has_infer() { - return t; - } - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r1 = match *r0 { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r0, - }; - - let r2 = match *r1 { - ty::RePlaceholder(p) => { - let replace_var = self.mapped_regions.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Region::new_bound(self.interner(), db, *replace_var) - } - None => r1, - } - } - _ => r1, - }; - - debug!(?r0, ?r1, ?r2, "fold_region"); - - r2 - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.infcx.shallow_resolve(ty); - match *ty.kind() { - ty::Placeholder(p) => { - let replace_var = self.mapped_types.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - Ty::new_bound(self.infcx.tcx, db, *replace_var) - } - None => { - if ty.has_infer() { - ty.super_fold_with(self) - } else { - ty - } - } - } - } - - _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), - _ => ty, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - let ct = self.infcx.shallow_resolve(ct); - if let ty::ConstKind::Placeholder(p) = ct.kind() { - let replace_var = self.mapped_consts.get(&p); - match replace_var { - Some(replace_var) => { - let index = self - .universe_indices - .iter() - .position(|u| matches!(u, Some(pu) if *pu == p.universe)) - .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); - let db = ty::DebruijnIndex::from_usize( - self.universe_indices.len() - index + self.current_index.as_usize() - 1, - ); - ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) - } - None => { - if ct.has_infer() { - ct.super_fold_with(self) - } else { - ct - } - } - } - } else { - ct.super_fold_with(self) - } - } -} - /// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that @@ -1146,7 +329,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] -fn opt_normalize_projection_type<'a, 'b, 'tcx>( +pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, @@ -1250,14 +433,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); let mut result = if projected_term.has_projections() { - let mut normalizer = AssocTypeNormalizer::new( + let normalized_ty = normalize_with_depth_to( selcx, param_env, cause, depth + 1, + projected_term, &mut projected_obligations, ); - let normalized_ty = normalizer.fold(projected_term); Normalized { value: normalized_ty, obligations: projected_obligations } } else { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 0f6c0abd280..26da065246d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -6,7 +6,8 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::normalize::needs_normalization; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -335,7 +336,7 @@ fn try_fold_const( let constant = constant.try_super_fold_with(self)?; debug!(?constant, ?self.param_env); - Ok(crate::traits::project::with_replaced_escaping_bound_vars( + Ok(crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, constant, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e4a70c537d2..f76be876948 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -18,7 +18,7 @@ }; use rustc_span::def_id::DefId; -use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::vtable::{ count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5bcf46a96ed..65cb9e1c1f4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -8,7 +8,6 @@ use super::coherence::{self, Conflict}; use super::const_evaluatable; use super::project; -use super::project::normalize_with_depth_to; use super::project::ProjectionTyObligation; use super::util; use super::util::closure_trait_ref_and_return_type; @@ -22,7 +21,8 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt; use crate::traits::error_reporting::TypeErrCtxtExt; -use crate::traits::project::try_normalize_with_depth_to; +use crate::traits::normalize::normalize_with_depth; +use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; @@ -1070,7 +1070,7 @@ fn evaluate_trait_predicate_recursively<'o>( && fresh_trait_pred.is_global() { let mut nested_obligations = Vec::new(); - let predicate = try_normalize_with_depth_to( + let predicate = normalize_with_depth_to( this, param_env, obligation.cause.clone(), @@ -1662,7 +1662,7 @@ fn match_normalize_trait_ref( } let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), @@ -1718,7 +1718,7 @@ pub(super) fn match_projection_projections( ); let infer_projection = if potentially_unnormalized_candidates { ensure_sufficient_stack(|| { - project::normalize_with_depth_to( + normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), @@ -2383,7 +2383,7 @@ fn collect_predicates_for_types( let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty); let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, param_env, cause.clone(), @@ -2480,7 +2480,7 @@ fn match_impl( let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index af172eb0713..23c86a2feef 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,11 +1,14 @@ +use std::collections::BTreeMap; + use super::NormalizeExt; use super::{ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Diagnostic; use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferOk; +use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::Span; use smallvec::SmallVec; @@ -382,3 +385,336 @@ fn check_args_compatible_inner<'tcx>( let args = &args[0..generics.count().min(args.len())]; check_args_compatible_inner(tcx, generics, args) } + +/// Executes `f` on `value` after replacing all escaping bound variables with placeholders +/// and then replaces these placeholders with the original bound variables in the result. +/// +/// In most places, bound variables should be replaced right when entering a binder, making +/// this function unnecessary. However, normalization currently does not do that, so we have +/// to do this lazily. +/// +/// You should not add any additional uses of this function, at least not without first +/// discussing it with t-types. +/// +/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during +/// normalization as well, at which point this function will be unnecessary and can be removed. +pub fn with_replaced_escaping_bound_vars< + 'a, + 'tcx, + T: TypeFoldable>, + R: TypeFoldable>, +>( + infcx: &'a InferCtxt<'tcx>, + universe_indices: &'a mut Vec>, + value: T, + f: impl FnOnce(T) -> R, +) -> R { + if value.has_escaping_bound_vars() { + let (value, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); + let result = f(value); + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + result, + ) + } else { + f(value) + } +} + +pub struct BoundVarReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'me mut Vec>, +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. + pub fn replace_bound_vars>>( + infcx: &'me InferCtxt<'tcx>, + universe_indices: &'me mut Vec>, + value: T, + ) -> ( + T, + BTreeMap, + BTreeMap, + BTreeMap, + ) { + let mapped_regions: BTreeMap = BTreeMap::new(); + let mapped_types: BTreeMap = BTreeMap::new(); + let mapped_consts: BTreeMap = BTreeMap::new(); + + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + current_index: ty::INNERMOST, + universe_indices, + }; + + let value = value.fold_with(&mut replacer); + + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe + } +} + +impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReBound(debruijn, _) + if debruijn.as_usize() + >= self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderRegion { universe, bound: br }; + self.mapped_regions.insert(p, br); + ty::Region::new_placeholder(self.infcx.tcx, p) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderType { universe, bound: bound_ty }; + self.mapped_types.insert(p, bound_ty); + Ty::new_placeholder(self.infcx.tcx, p) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderConst { universe, bound: bound_const }; + self.mapped_consts.insert(p, bound_const); + ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, + universe_indices: &'me [Option], + current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders>>( + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap, + mapped_types: BTreeMap, + mapped_consts: BTreeMap, + universe_indices: &'me [Option], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: ty::INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r1 = match *r0 { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r0, + }; + + let r2 = match *r1 { + ty::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Region::new_bound(self.interner(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.infcx.shallow_resolve(ty); + match *ty.kind() { + ty::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.tcx, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + let ct = self.infcx.shallow_resolve(ct); + if let ty::ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index c727ef53d55..15059bc6613 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -313,7 +313,7 @@ fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec( goal: CanonicalProjectionGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { debug!("normalize_provider(goal={:#?})", goal); - tcx.infer_ctxt().enter_canonical_trait_query( &goal, |ocx, ParamEnvAnd { param_env, value: goal }| { + debug_assert!(!ocx.infcx.next_trait_solver()); let selcx = &mut SelectionContext::new(ocx.infcx); let cause = ObligationCause::dummy(); let mut obligations = vec![]; @@ -45,23 +45,22 @@ fn normalize_projection_ty<'tcx>( // are recursive (given some generic parameters of the opaque's type variables). // In that case, we may only realize a cycle error when calling // `normalize_erasing_regions` in mono. - if !ocx.infcx.next_trait_solver() { - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - // Rustdoc may attempt to normalize type alias types which are not - // well-formed. Rustdoc also normalizes types that are just not - // well-formed, since we don't do as much HIR analysis (checking - // that impl vars are constrained by the signature, for example). - if !tcx.sess.opts.actually_rustdoc { - for error in &errors { - if let FulfillmentErrorCode::Cycle(cycle) = &error.code { - ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); - } + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + // Rustdoc may attempt to normalize type alias types which are not + // well-formed. Rustdoc also normalizes types that are just not + // well-formed, since we don't do as much HIR analysis (checking + // that impl vars are constrained by the signature, for example). + if !tcx.sess.opts.actually_rustdoc { + for error in &errors { + if let FulfillmentErrorCode::Cycle(cycle) = &error.code { + ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); } } - return Err(NoSolution); } + return Err(NoSolution); } + // FIXME(associated_const_equality): All users of normalize_projection_ty expected // a type, but there is the possibility it could've been a const now. Maybe change // it to a Term later?