1
Fork 0

add deep normalization via the new solver

This commit is contained in:
lcnr 2023-06-27 23:11:37 +02:00
parent 4d42de6d1b
commit 42067596c2
6 changed files with 281 additions and 6 deletions

View file

@ -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 {

View 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))
}
}
}

View file

@ -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::*;

View file

@ -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<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> {
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&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<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) }
}
}
}

View file

@ -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<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
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
/// know that normalization will succeed, since error reporting
/// 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
T: TypeFoldable<TyCtxt<'tcx>>,
{
@ -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![] });
}