add deep normalization via the new solver
This commit is contained in:
parent
4d42de6d1b
commit
42067596c2
6 changed files with 281 additions and 6 deletions
|
@ -40,6 +40,7 @@ pub enum DefineOpaqueTypes {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct At<'a, 'tcx> {
|
pub struct At<'a, 'tcx> {
|
||||||
pub infcx: &'a InferCtxt<'tcx>,
|
pub infcx: &'a InferCtxt<'tcx>,
|
||||||
pub cause: &'a ObligationCause<'tcx>,
|
pub cause: &'a ObligationCause<'tcx>,
|
||||||
|
|
|
@ -26,6 +26,7 @@ mod canonicalize;
|
||||||
mod eval_ctxt;
|
mod eval_ctxt;
|
||||||
mod fulfill;
|
mod fulfill;
|
||||||
pub mod inspect;
|
pub mod inspect;
|
||||||
|
mod normalize;
|
||||||
mod opaques;
|
mod opaques;
|
||||||
mod project_goals;
|
mod project_goals;
|
||||||
mod search_graph;
|
mod search_graph;
|
||||||
|
@ -34,6 +35,7 @@ mod weak_types;
|
||||||
|
|
||||||
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
|
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
|
||||||
pub use fulfill::FulfillmentCtxt;
|
pub use fulfill::FulfillmentCtxt;
|
||||||
|
pub(crate) use normalize::deeply_normalize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum SolverMode {
|
enum SolverMode {
|
||||||
|
|
222
compiler/rustc_trait_selection/src/solve/normalize.rs
Normal file
222
compiler/rustc_trait_selection/src/solve/normalize.rs
Normal file
|
@ -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<TyCtxt<'tcx>>>(
|
||||||
|
at: At<'_, 'tcx>,
|
||||||
|
value: T,
|
||||||
|
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
|
||||||
|
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::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<Option<UniverseIndex>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> NormalizationFolder<'_, 'tcx> {
|
||||||
|
fn normalize_alias_ty(
|
||||||
|
&mut self,
|
||||||
|
alias: AliasTy<'tcx>,
|
||||||
|
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||||
|
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<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||||
|
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<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
|
||||||
|
type Error = Vec<FulfillmentError<'tcx>>;
|
||||||
|
|
||||||
|
fn interner(&self) -> TyCtxt<'tcx> {
|
||||||
|
self.at.infcx.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||||
|
&mut self,
|
||||||
|
t: ty::Binder<'tcx, T>,
|
||||||
|
) -> Result<ty::Binder<'tcx, T>, 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<Ty<'tcx>, 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<ty::Const<'tcx>, 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,8 @@ use rustc_span::Span;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
|
||||||
|
|
||||||
pub use self::FulfillmentErrorCode::*;
|
pub use self::FulfillmentErrorCode::*;
|
||||||
pub use self::ImplSource::*;
|
pub use self::ImplSource::*;
|
||||||
pub use self::ObligationCauseCode::*;
|
pub use self::ObligationCauseCode::*;
|
||||||
|
|
|
@ -20,6 +20,7 @@ use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
|
||||||
use crate::traits::error_reporting::TypeErrCtxtExt as _;
|
use crate::traits::error_reporting::TypeErrCtxtExt as _;
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
use crate::traits::select::ProjectionMatchesProjection;
|
use crate::traits::select::ProjectionMatchesProjection;
|
||||||
|
use crate::traits::TraitEngineExt as _;
|
||||||
use rustc_data_structures::sso::SsoHashSet;
|
use rustc_data_structures::sso::SsoHashSet;
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
|
@ -28,7 +29,10 @@ use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_infer::infer::at::At;
|
use rustc_infer::infer::at::At;
|
||||||
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
||||||
use rustc_infer::infer::DefineOpaqueTypes;
|
use rustc_infer::infer::DefineOpaqueTypes;
|
||||||
|
use rustc_infer::traits::FulfillmentError;
|
||||||
use rustc_infer::traits::ObligationCauseCode;
|
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::traits::select::OverflowError;
|
||||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||||
use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
|
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
|
/// This normalization should be used when the type contains inference variables or the
|
||||||
/// projection may be fallible.
|
/// projection may be fallible.
|
||||||
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
|
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&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<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||||
|
self,
|
||||||
|
value: T,
|
||||||
|
) -> Result<T, Vec<FulfillmentError<'tcx>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
|
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
|
||||||
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
|
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
|
||||||
let mut selcx = SelectionContext::new(self.infcx);
|
if self.infcx.next_trait_solver() {
|
||||||
let Normalized { value, obligations } =
|
InferOk { value, obligations: Vec::new() }
|
||||||
normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
|
} else {
|
||||||
InferOk { value, obligations }
|
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<T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||||
|
self,
|
||||||
|
value: T,
|
||||||
|
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
|
||||||
|
if self.infcx.next_trait_solver() {
|
||||||
|
crate::solve::deeply_normalize(self, value)
|
||||||
|
} else {
|
||||||
|
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::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) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> {
|
||||||
///
|
///
|
||||||
/// After codegen, when lifetimes do not matter, it is preferable to instead
|
/// After codegen, when lifetimes do not matter, it is preferable to instead
|
||||||
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
|
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
|
||||||
fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
|
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
|
||||||
where
|
where
|
||||||
T: TypeFoldable<TyCtxt<'tcx>>;
|
T: TypeFoldable<TyCtxt<'tcx>>;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
|
||||||
/// normalizing, but for now should be used only when we actually
|
/// normalizing, but for now should be used only when we actually
|
||||||
/// know that normalization will succeed, since error reporting
|
/// know that normalization will succeed, since error reporting
|
||||||
/// and other details are still "under development".
|
/// and other details are still "under development".
|
||||||
fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
|
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
|
||||||
where
|
where
|
||||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
|
||||||
self.param_env,
|
self.param_env,
|
||||||
self.cause,
|
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()) {
|
if !needs_normalization(&value, self.param_env.reveal()) {
|
||||||
return Ok(Normalized { value, obligations: vec![] });
|
return Ok(Normalized { value, obligations: vec![] });
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue