diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index f1a187d2677..433735e827b 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -40,6 +40,7 @@ pub enum DefineOpaqueTypes { No, } +#[derive(Clone, Copy)] pub struct At<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, pub cause: &'a ObligationCause<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 49fecedc0ca..769d002fcc0 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -26,6 +26,7 @@ mod canonicalize; mod eval_ctxt; mod fulfill; pub mod inspect; +mod normalize; mod opaques; mod project_goals; mod search_graph; @@ -34,6 +35,7 @@ mod weak_types; pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt}; pub use fulfill::FulfillmentCtxt; +pub(crate) use normalize::deeply_normalize; #[derive(Debug, Clone, Copy)] enum SolverMode { diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs new file mode 100644 index 00000000000..420a6ee8978 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -0,0 +1,222 @@ +use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{needs_normalization, TraitEngineExt as _}; +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}; +use rustc_infer::traits::TraitEngineExt; +use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable>>( + at: At<'_, 'tcx>, + value: T, +) -> Result>> { + let mut fulfill_cx = >::new(&at.infcx); + let mut folder = + NormalizationFolder { at, fulfill_cx: &mut *fulfill_cx, depth: 0, universes: Vec::new() }; + + value.try_fold_with(&mut folder) +} + +struct NormalizationFolder<'me, 'tcx> { + at: At<'me, 'tcx>, + fulfill_cx: &'me mut dyn TraitEngine<'tcx>, + depth: usize, + universes: Vec>, +} + +impl<'tcx> NormalizationFolder<'_, 'tcx> { + fn normalize_alias_ty( + &mut self, + alias: AliasTy<'tcx>, + ) -> Result, Vec>> { + let infcx = self.at.infcx; + let tcx = infcx.tcx; + let recursion_limit = tcx.recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.at.infcx.err_ctxt().report_overflow_error( + &alias.to_ty(tcx), + self.at.cause.span, + true, + |_| {}, + ); + } + + self.depth += 1; + + let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.at.cause.span, + }); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: alias, + term: new_infer_ty.into(), + }), + ); + + // Do not emit an error if normalization is known to fail but instead + // keep the projection unnormalized. This is the case for projections + // with a `T: Trait` where-clause and opaque types outside of the defining + // scope. + let result = if infcx.predicate_may_hold(&obligation) { + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + let ty = infcx.resolve_vars_if_possible(new_infer_ty); + ty.try_fold_with(self)? + } else { + alias.to_ty(tcx).try_super_fold_with(self)? + }; + + self.depth -= 1; + Ok(result) + } + + fn normalize_unevaluated_const( + &mut self, + ty: Ty<'tcx>, + uv: ty::UnevaluatedConst<'tcx>, + ) -> Result, Vec>> { + let infcx = self.at.infcx; + let tcx = infcx.tcx; + let recursion_limit = tcx.recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.at.infcx.err_ctxt().report_overflow_error( + &tcx.mk_const(uv, ty), + self.at.cause.span, + true, + |_| {}, + ); + } + + self.depth += 1; + + let new_infer_ct = infcx.next_const_var( + ty, + ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: self.at.cause.span, + }, + ); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: tcx.mk_alias_ty(uv.def, uv.substs), + term: new_infer_ct.into(), + }), + ); + + let result = if infcx.predicate_may_hold(&obligation) { + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + let ct = infcx.resolve_vars_if_possible(new_infer_ct); + ct.try_fold_with(self)? + } else { + tcx.mk_const(uv, ty).try_super_fold_with(self)? + }; + + self.depth -= 1; + Ok(result) + } +} + +impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { + type Error = Vec>; + + fn interner(&self) -> TyCtxt<'tcx> { + self.at.infcx.tcx + } + + fn try_fold_binder>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> Result, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { + let reveal = self.at.param_env.reveal(); + let infcx = self.at.infcx; + if !needs_normalization(&ty, reveal) { + return Ok(ty); + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => return ty.try_super_fold_with(self), + }; + + // We don't normalize opaque types unless we have + // `Reveal::All`, even if we're in the defining scope. + if matches!(kind, ty::Opaque) && reveal == Reveal::UserFacing { + return ty.try_super_fold_with(self); + } + + if data.has_escaping_bound_vars() { + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?; + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &mut self.universes, + result, + )) + } else { + ensure_sufficient_stack(|| self.normalize_alias_ty(data)) + } + } + + 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; + if !needs_normalization(&ct, reveal) { + return Ok(ct); + } + + let uv = match ct.kind() { + ty::ConstKind::Unevaluated(ct) => ct, + _ => return ct.try_super_fold_with(self), + }; + + if uv.has_escaping_bound_vars() { + let (uv, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); + let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?; + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &mut self.universes, + result, + )) + } else { + ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv)) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5dc5ddbddbd..89aefc0de53 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -40,6 +40,8 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; +pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; + pub use self::FulfillmentErrorCode::*; pub use self::ImplSource::*; pub use self::ObligationCauseCode::*; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8fd77335074..e4eea14ef74 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -20,6 +20,7 @@ use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; +use crate::traits::TraitEngineExt as _; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; @@ -28,7 +29,10 @@ 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_infer::traits::TraitEngineExt as _; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; @@ -53,14 +57,48 @@ pub trait NormalizeExt<'tcx> { /// This normalization should be used when the type contains inference variables or the /// projection may be fallible. fn normalize>>(&self, t: T) -> InferOk<'tcx, T>; + + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. Unlike other normalization routines, this errors + /// in case normalization fails or is ambiguous. + /// + /// In the old solver this simply uses `normalize` and errors in + /// case of ambiguity. The new solver only normalizes in this function and + /// `normalize` is a noop. + /// + /// This only normalize opaque types with `Reveal::All`. + fn deeply_normalize>>( + self, + value: T, + ) -> Result>>; } impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> { fn normalize>>(&self, value: T) -> InferOk<'tcx, T> { - 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 } + 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 } + } + } + + fn deeply_normalize>>( + self, + value: T, + ) -> Result>> { + if self.infcx.next_trait_solver() { + crate::solve::deeply_normalize(self, value) + } else { + let mut fulfill_cx = >::new(&self.infcx); + let value = self + .normalize(value) + .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); + let errors = fulfill_cx.select_all_or_error(self.infcx); + if errors.is_empty() { Ok(value) } else { Err(errors) } + } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index edad519cec2..7fe79fd865c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> { /// /// After codegen, when lifetimes do not matter, it is preferable to instead /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure. - fn query_normalize(&self, value: T) -> Result, NoSolution> + fn query_normalize(self, value: T) -> Result, NoSolution> where T: TypeFoldable>; } @@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> { /// normalizing, but for now should be used only when we actually /// know that normalization will succeed, since error reporting /// and other details are still "under development". - fn query_normalize(&self, value: T) -> Result, NoSolution> + fn query_normalize(self, value: T) -> Result, NoSolution> where T: TypeFoldable>, { @@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> { self.param_env, self.cause, ); + + if self.infcx.next_trait_solver() { + match crate::solve::deeply_normalize(self, value) { + Ok(value) => return Ok(Normalized { value, obligations: vec![] }), + Err(_errors) => { + return Err(NoSolution); + } + } + } + if !needs_normalization(&value, self.param_env.reveal()) { return Ok(Normalized { value, obligations: vec![] }); }