Skip to content

implement deep normalization via the new solver #113086

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 3, 2023
Prev Previous commit
Next Next commit
review
lcnr committed Jul 3, 2023
commit 412c6e0b0737245b029d5a3f7b1189ff7da7c7f0
2 changes: 2 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
@@ -83,6 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// version (resolve_vars_if_possible), this version will
/// also select obligations if it seems useful, in an effort
/// to get more type information.
// FIXME(-Ztrait-solver=next): A lot of the calls to this method should
// probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
26 changes: 12 additions & 14 deletions compiler/rustc_trait_selection/src/solve/normalize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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 crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::at::At;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -13,22 +12,23 @@ use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable};
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};

use super::FulfillmentCtxt;

/// 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() };
let fulfill_cx = FulfillmentCtxt::new();
let mut folder = NormalizationFolder { at, 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>,
fulfill_cx: FulfillmentCtxt<'tcx>,
depth: usize,
universes: Vec<Option<UniverseIndex>>,
}
@@ -163,16 +163,14 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
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);
}
let data = match *ty.kind() {
ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::UserFacing => {
alias_ty
}
_ => return ty.try_super_fold_with(self),
};

if data.has_escaping_bound_vars() {
let (data, mapped_regions, mapped_types, mapped_consts) =
7 changes: 6 additions & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -409,7 +409,12 @@ pub fn normalize_param_env_or_error<'tcx>(
)
}

/// Normalize a type and process all resulting obligations, returning any errors
/// Normalize a type and process all resulting obligations, returning any errors.
///
/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize`
/// which has the same behavior with the new solver. Because using a separate
/// fulfillment context worsens caching in the old solver, `At::deeply_normalize`
/// is still lazy with the old solver as it otherwise negatively impacts perf.
#[instrument(skip_all)]
pub fn fully_normalize<'tcx, T>(
infcx: &InferCtxt<'tcx>,
8 changes: 7 additions & 1 deletion compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
@@ -58,12 +58,18 @@ pub trait NormalizeExt<'tcx> {

/// 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 normalize opaque types with `Reveal::All`.
/// 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(-Ztrait-solver=next): 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<T: TypeFoldable<TyCtxt<'tcx>>>(
self,
value: T,