blob: 791f27a97896dc97abdf3ca7492f7d44b5cbda5f [file] [log] [blame]
//! Methods for normalizing when you don't care about regions (and
//! aren't doing type inference). If either of those things don't
//! apply to you, use `infcx.normalize(...)`.
//!
//! The methods in this file use a `TypeFolder` to recursively process
//! contents, invoking the underlying
//! `normalize_generic_arg_after_erasing_regions` query for each type
//! or constant found within. (This underlying query is what is cached.)
use crate::traits::query::NoSolution;
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder};
use crate::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
#[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)]
pub enum NormalizationError<'tcx> {
Type(Ty<'tcx>),
Const(ty::Const<'tcx>),
}
impl<'tcx> NormalizationError<'tcx> {
pub fn get_type_for_failure(&self) -> String {
match self {
NormalizationError::Type(t) => format!("{t}"),
NormalizationError::Const(c) => format!("{c}"),
}
}
}
impl<'tcx> TyCtxt<'tcx> {
/// Erase the regions in `value` and then fully normalize all the
/// types found within. The result will also have regions erased.
///
/// This should only be used outside of type inference. For example,
/// it assumes that normalization will succeed.
#[tracing::instrument(level = "debug", skip(self, param_env), ret)]
pub fn normalize_erasing_regions<T>(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
debug!(
"normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
std::any::type_name::<T>(),
value,
param_env,
);
// Erase first before we do the real query -- this keeps the
// cache from being too polluted.
let value = self.erase_regions(value);
debug!(?value);
if !value.has_aliases() {
value
} else {
value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, param_env })
}
}
/// Tries to erase the regions in `value` and then fully normalize all the
/// types found within. The result will also have regions erased.
///
/// Contrary to `normalize_erasing_regions` this function does not assume that normalization
/// succeeds.
pub fn try_normalize_erasing_regions<T>(
self,
param_env: ty::ParamEnv<'tcx>,
value: T,
) -> Result<T, NormalizationError<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
debug!(
"try_normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
std::any::type_name::<T>(),
value,
param_env,
);
// Erase first before we do the real query -- this keeps the
// cache from being too polluted.
let value = self.erase_regions(value);
debug!(?value);
if !value.has_aliases() {
Ok(value)
} else {
let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env);
value.try_fold_with(&mut folder)
}
}
/// If you have a `Binder<'tcx, T>`, you can do this to strip out the
/// late-bound regions and then normalize the result, yielding up
/// a `T` (with regions erased). This is appropriate when the
/// binder is being instantiated at the call site.
///
/// N.B., currently, higher-ranked type bounds inhibit
/// normalization. Therefore, each time we erase them in
/// codegen, we need to normalize the contents.
// FIXME(@lcnr): This method should not be necessary, we now normalize
// inside of binders. We should be able to only use
// `tcx.instantiate_bound_regions_with_erased`. Same for the `try_X`
// variant.
#[tracing::instrument(level = "debug", skip(self, param_env))]
pub fn normalize_erasing_late_bound_regions<T>(
self,
param_env: ty::ParamEnv<'tcx>,
value: ty::Binder<'tcx, T>,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = self.instantiate_bound_regions_with_erased(value);
self.normalize_erasing_regions(param_env, value)
}
/// If you have a `Binder<'tcx, T>`, you can do this to strip out the
/// late-bound regions and then normalize the result, yielding up
/// a `T` (with regions erased). This is appropriate when the
/// binder is being instantiated at the call site.
///
/// N.B., currently, higher-ranked type bounds inhibit
/// normalization. Therefore, each time we erase them in
/// codegen, we need to normalize the contents.
pub fn try_normalize_erasing_late_bound_regions<T>(
self,
param_env: ty::ParamEnv<'tcx>,
value: ty::Binder<'tcx, T>,
) -> Result<T, NormalizationError<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = self.instantiate_bound_regions_with_erased(value);
self.try_normalize_erasing_regions(param_env, value)
}
/// Monomorphizes a type from the AST by first applying the
/// in-scope instantiations and then normalizing any associated
/// types.
/// Panics if normalization fails. In case normalization might fail
/// use `try_instantiate_and_normalize_erasing_regions` instead.
#[instrument(level = "debug", skip(self))]
pub fn instantiate_and_normalize_erasing_regions<T>(
self,
param_args: GenericArgsRef<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: EarlyBinder<T>,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let instantiated = value.instantiate(self, param_args);
self.normalize_erasing_regions(param_env, instantiated)
}
/// Monomorphizes a type from the AST by first applying the
/// in-scope instantiations and then trying to normalize any associated
/// types. Contrary to `instantiate_and_normalize_erasing_regions` this does
/// not assume that normalization succeeds.
#[instrument(level = "debug", skip(self))]
pub fn try_instantiate_and_normalize_erasing_regions<T>(
self,
param_args: GenericArgsRef<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: EarlyBinder<T>,
) -> Result<T, NormalizationError<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let instantiated = value.instantiate(self, param_args);
self.try_normalize_erasing_regions(param_env, instantiated)
}
}
struct NormalizeAfterErasingRegionsFolder<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
fn normalize_generic_arg_after_erasing_regions(
&self,
arg: ty::GenericArg<'tcx>,
) -> ty::GenericArg<'tcx> {
let arg = self.param_env.and(arg);
self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!(
"Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead",
arg.value
))
}
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for NormalizeAfterErasingRegionsFolder<'tcx> {
fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
}
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
}
}
struct TryNormalizeAfterErasingRegionsFolder<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> TryNormalizeAfterErasingRegionsFolder<'tcx> {
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
TryNormalizeAfterErasingRegionsFolder { tcx, param_env }
}
#[instrument(skip(self), level = "debug")]
fn try_normalize_generic_arg_after_erasing_regions(
&self,
arg: ty::GenericArg<'tcx>,
) -> Result<ty::GenericArg<'tcx>, NoSolution> {
let arg = self.param_env.and(arg);
debug!(?arg);
self.tcx.try_normalize_generic_arg_after_erasing_regions(arg)
}
}
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for TryNormalizeAfterErasingRegionsFolder<'tcx> {
type Error = NormalizationError<'tcx>;
fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) {
Ok(t) => Ok(t.expect_ty()),
Err(_) => Err(NormalizationError::Type(ty)),
}
}
fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
match self.try_normalize_generic_arg_after_erasing_regions(c.into()) {
Ok(t) => Ok(t.expect_const()),
Err(_) => Err(NormalizationError::Const(c)),
}
}
}