rust/compiler/rustc_trait_selection/src/solve/normalize.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

295 lines
10 KiB
Rust
Raw Normal View History

2024-06-03 13:27:48 +00:00
use std::fmt::Debug;
2024-06-01 18:12:34 +00:00
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::at::At;
use rustc_infer::traits::solve::Goal;
2024-06-03 13:27:48 +00:00
use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine};
use rustc_middle::traits::ObligationCause;
2024-05-13 14:00:38 +00:00
use rustc_middle::ty::{
self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt, UniverseIndex,
};
use tracing::instrument;
2024-06-01 18:12:34 +00:00
use super::{FulfillmentCtxt, NextSolverError};
use crate::error_reporting::InferCtxtErrorExt;
use crate::error_reporting::traits::OverflowCause;
2024-06-01 19:00:02 +00:00
use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
2023-06-30 08:57:17 +00:00
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
2024-06-03 13:27:48 +00:00
pub fn deeply_normalize<'tcx, T, E>(at: At<'_, 'tcx>, value: T) -> Result<T, Vec<E>>
where
2024-06-01 18:12:34 +00:00
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
2024-06-03 13:27:48 +00:00
{
2023-07-13 18:27:40 +00:00
assert!(!value.has_escaping_bound_vars());
deeply_normalize_with_skipped_universes(at, value, vec![])
}
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
2023-07-13 18:27:40 +00:00
///
/// Additionally takes a list of universes which represents the binders which have been
2023-07-23 19:30:52 +00:00
/// entered before passing `value` to the function. This is currently needed for
/// `normalize_erasing_regions`, which skips binders as it walks through a type.
2024-06-03 13:27:48 +00:00
pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
2024-06-03 13:27:48 +00:00
) -> Result<T, Vec<E>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
2025-04-28 02:53:41 +00:00
let (value, coroutine_goals) =
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at, value, universes,
)?;
assert_eq!(coroutine_goals, vec![]);
Ok(value)
}
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
///
/// Additionally takes a list of universes which represents the binders which have been
/// entered before passing `value` to the function. This is currently needed for
/// `normalize_erasing_regions`, which skips binders as it walks through a type.
///
2025-04-28 02:53:41 +00:00
/// This returns a set of stalled obligations involving coroutines if the typing mode of
/// the underlying infcx has any stalled coroutine def ids.
pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'tcx, T, E>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>>
2024-06-03 13:27:48 +00:00
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
2023-07-03 07:24:02 +00:00
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
2025-04-28 02:53:41 +00:00
let mut folder = NormalizationFolder {
at,
fulfill_cx,
depth: 0,
universes,
stalled_coroutine_goals: vec![],
};
let value = value.try_fold_with(&mut folder)?;
let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
2025-04-28 02:53:41 +00:00
if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) }
}
2024-06-03 13:27:48 +00:00
struct NormalizationFolder<'me, 'tcx, E> {
at: At<'me, 'tcx>,
2024-06-01 18:12:34 +00:00
fulfill_cx: FulfillmentCtxt<'tcx, E>,
depth: usize,
universes: Vec<Option<UniverseIndex>>,
2025-04-28 02:53:41 +00:00
stalled_coroutine_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
2024-06-03 13:27:48 +00:00
impl<'tcx, E> NormalizationFolder<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
fn normalize_alias_term(
&mut self,
alias_term: ty::Term<'tcx>,
) -> Result<ty::Term<'tcx>, Vec<E>> {
let infcx = self.at.infcx;
let tcx = infcx.tcx;
let recursion_limit = tcx.recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
let term = alias_term.to_alias_term().unwrap();
self.at.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(term),
self.at.cause.span,
true,
|_| {},
);
}
self.depth += 1;
let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span);
let obligation = Obligation::new(
tcx,
self.at.cause.clone(),
self.at.param_env,
ty::PredicateKind::AliasRelate(
alias_term.into(),
infer_term.into(),
ty::AliasRelationDirection::Equate,
),
);
self.fulfill_cx.register_predicate_obligation(infcx, obligation);
self.select_all_and_stall_coroutine_predicates()?;
// Alias is guaranteed to be fully structurally resolved,
// so we can super fold here.
let term = infcx.resolve_vars_if_possible(infer_term);
// super-folding the `term` will directly fold the `Ty` or `Const` so
// we have to match on the term and super-fold them manually.
let result = match term.kind() {
ty::TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(),
ty::TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(),
};
self.depth -= 1;
Ok(result)
}
2025-04-22 23:31:22 +00:00
fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec<E>> {
let errors = self.fulfill_cx.select_where_possible(self.at.infcx);
if !errors.is_empty() {
return Err(errors);
}
2025-04-28 02:53:41 +00:00
self.stalled_coroutine_goals.extend(
2025-04-22 23:31:22 +00:00
self.fulfill_cx
.drain_stalled_obligations_for_coroutines(self.at.infcx)
.into_iter()
.map(|obl| obl.as_goal()),
);
let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx);
if !errors.is_empty() {
return Err(errors);
}
Ok(())
}
}
2024-06-03 13:27:48 +00:00
impl<'tcx, E> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>> + Debug,
2024-06-01 18:12:34 +00:00
{
type Error = Vec<E>;
2024-06-18 23:13:54 +00:00
fn cx(&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)
}
#[instrument(level = "trace", skip(self), ret)]
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
let infcx = self.at.infcx;
debug_assert_eq!(ty, infcx.shallow_resolve(ty));
if !ty.has_aliases() {
return Ok(ty);
}
let ty::Alias(..) = *ty.kind() else { return ty.try_super_fold_with(self) };
if ty.has_escaping_bound_vars() {
let (ty, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty);
let result =
ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type();
Ok(PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
result,
))
} else {
Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ty.into()))?.expect_type())
}
}
#[instrument(level = "trace", skip(self), ret)]
fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
let infcx = self.at.infcx;
debug_assert_eq!(ct, infcx.shallow_resolve_const(ct));
if !ct.has_aliases() {
return Ok(ct);
}
let ty::ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) };
if ct.has_escaping_bound_vars() {
let (ct, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct);
let result =
ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const();
Ok(PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
result,
))
} else {
Ok(ensure_sufficient_stack(|| self.normalize_alias_term(ct.into()))?.expect_const())
}
}
}
// Deeply normalize a value and return it
pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
t: T,
) -> T {
t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder {
at: infcx.at(&ObligationCause::dummy(), param_env),
})
}
struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> {
at: At<'a, 'tcx>,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> {
2024-06-18 23:13:54 +00:00
fn cx(&self) -> TyCtxt<'tcx> {
self.at.infcx.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
let infcx = self.at.infcx;
2025-04-28 02:53:41 +00:00
let result: Result<_, Vec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
self.at,
ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
)
});
match result {
Ok((ty, _)) => ty,
Err(_) => ty.super_fold_with(self),
}
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
let infcx = self.at.infcx;
2025-04-28 02:53:41 +00:00
let result: Result<_, Vec<ScrubbedTraitError<'tcx>>> = infcx.commit_if_ok(|_| {
deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
self.at,
ct,
vec![None; ct.outer_exclusive_binder().as_usize()],
)
});
match result {
Ok((ct, _)) => ct,
Err(_) => ct.super_fold_with(self),
}
}
}