move `ConstKind` to typeir and move inherent impls to `Const`

This commit is contained in:
Boxy 2023-07-04 11:46:01 +01:00
parent 9227ff28af
commit 8ac3ffe834
9 changed files with 413 additions and 222 deletions

View File

@ -1,6 +1,6 @@
use crate::middle::resolve_bound_vars as rbv;
use crate::mir::interpret::LitToConstInput;
use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use crate::mir::interpret::{AllocId, ConstValue, LitToConstInput, Scalar};
use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
use rustc_data_structures::intern::Interned;
use rustc_error_messages::MultiSpan;
use rustc_hir as hir;
@ -14,9 +14,13 @@ mod valtree;
pub use int::*;
pub use kind::*;
use rustc_span::ErrorGuaranteed;
use rustc_span::DUMMY_SP;
use rustc_target::abi::Size;
pub use valtree::*;
use super::sty::ConstKind;
/// Use this rather than `ConstData`, whenever possible.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
#[rustc_pass_by_value]
@ -32,6 +36,16 @@ pub struct ConstData<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstData<'_>, 40);
enum EvalMode {
Typeck,
Mir,
}
enum EvalResult<'tcx> {
ValTree(ty::ValTree<'tcx>),
ConstVal(ConstValue<'tcx>),
}
impl<'tcx> Const<'tcx> {
#[inline]
pub fn ty(self) -> Ty<'tcx> {
@ -293,12 +307,12 @@ impl<'tcx> Const<'tcx> {
assert_eq!(self.ty(), ty);
let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
self.kind().eval(tcx, param_env).try_to_bits(size)
self.eval(tcx, param_env).try_to_bits(size)
}
#[inline]
pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
self.kind().eval(tcx, param_env).try_to_bool()
self.eval(tcx, param_env).try_to_bool()
}
#[inline]
@ -307,14 +321,14 @@ impl<'tcx> Const<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<u64> {
self.kind().eval(tcx, param_env).try_to_target_usize(tcx)
self.eval(tcx, param_env).try_to_target_usize(tcx)
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
/// unevaluated constant.
pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) {
if let Some(val) = self.try_eval_for_typeck(tcx, param_env) {
match val {
Ok(val) => ty::Const::new_value(tcx, val, self.ty()),
Err(guar) => ty::Const::new_error(tcx, guar, self.ty()),
@ -339,6 +353,138 @@ impl<'tcx> Const<'tcx> {
.unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
/// return `None`.
// FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
pub fn try_eval_for_mir(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
/// return `None`.
// FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
pub fn try_eval_for_typeck(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
#[inline]
fn try_eval_inner(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
eval_mode: EvalMode,
) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
if let ConstKind::Unevaluated(unevaluated) = self.kind() {
use crate::mir::interpret::ErrorHandled;
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
// also does later, but we want to do it before checking for
// inference variables.
// Note that we erase regions *before* calling `with_reveal_all_normalized`,
// so that we don't try to invoke this query with
// any region variables.
// HACK(eddyb) when the query key would contain inference variables,
// attempt using identity substs and `ParamEnv` instead, that will succeed
// when the expression doesn't depend on any parameters.
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
// we can call `infcx.const_eval_resolve` which handles inference variables.
let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
def: unevaluated.def,
substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
})
} else {
tcx.erase_regions(param_env)
.with_reveal_all_normalized(tcx)
.and(tcx.erase_regions(unevaluated))
};
// FIXME(eddyb) maybe the `const_eval_*` methods should take
// `ty::ParamEnvAnd` instead of having them separate.
let (param_env, unevaluated) = param_env_and.into_parts();
// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
match eval_mode {
EvalMode::Typeck => {
match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
// and we use the original type, so nothing from `substs`
// (which may be identity substs, see above),
// can leak through `val` into the const we return.
Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
Err(ErrorHandled::TooGeneric) => None,
Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
}
}
EvalMode::Mir => {
match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
// and we use the original type, so nothing from `substs`
// (which may be identity substs, see above),
// can leak through `val` into the const we return.
Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
Err(ErrorHandled::TooGeneric) => None,
Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
}
}
}
} else {
None
}
}
#[inline]
pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
if let ConstKind::Value(val) = self.kind() { Some(val) } else { None }
}
#[inline]
pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
self.try_to_value()?.try_to_scalar()
}
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
self.try_to_value()?.try_to_scalar_int()
}
#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
}
#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_scalar_int()?.try_into().ok()
}
#[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_value()?.try_to_target_usize(tcx)
}
pub fn is_ct_infer(self) -> bool {
matches!(self.kind(), ty::ConstKind::Infer(_))
}

View File

@ -1,17 +1,12 @@
use super::Const;
use crate::mir;
use crate::mir::interpret::{AllocId, ConstValue, Scalar};
use crate::ty::abstract_const::CastKind;
use crate::ty::subst::{InternalSubsts, SubstsRef};
use crate::ty::ParamEnv;
use crate::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
use crate::ty::sty::ConstKind;
use crate::ty::subst::SubstsRef;
use crate::ty::{self, List, Ty};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_target::abi::Size;
use super::ScalarInt;
/// An unevaluated (potentially generic) constant used in the type-system.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
@ -41,39 +36,6 @@ impl<'tcx> UnevaluatedConst<'tcx> {
}
}
/// Represents a constant in Rust.
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
#[derive(derive_more::From)]
pub enum ConstKind<'tcx> {
/// A const generic parameter.
Param(ty::ParamConst),
/// Infer the value of the const.
Infer(InferConst<'tcx>),
/// Bound const variable, used only when preparing a trait query.
Bound(ty::DebruijnIndex, ty::BoundVar),
/// A placeholder const - universally quantified higher-ranked const.
Placeholder(ty::PlaceholderConst<'tcx>),
/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
/// variants when the code is monomorphic enough for that.
Unevaluated(UnevaluatedConst<'tcx>),
/// Used to hold computed value.
Value(ty::ValTree<'tcx>),
/// A placeholder for a const which could not be computed; this is
/// propagated to avoid useless error messages.
#[from(ignore)]
Error(ErrorGuaranteed),
/// Expr which contains an expression which has partially evaluated items.
Expr(Expr<'tcx>),
}
impl<'tcx> From<ty::ConstVid<'tcx>> for ConstKind<'tcx> {
fn from(const_vid: ty::ConstVid<'tcx>) -> Self {
InferConst::Var(const_vid).into()
@ -95,38 +57,6 @@ static_assert_size!(Expr<'_>, 24);
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstKind<'_>, 32);
impl<'tcx> ConstKind<'tcx> {
#[inline]
pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
if let ConstKind::Value(val) = self { Some(val) } else { None }
}
#[inline]
pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
self.try_to_value()?.try_to_scalar()
}
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
self.try_to_value()?.try_to_scalar_int()
}
#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
}
#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_scalar_int()?.try_into().ok()
}
#[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_value()?.try_to_target_usize(tcx)
}
}
/// An inference variable for a const, for use in const generics.
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
pub enum InferConst<'tcx> {
@ -144,124 +74,3 @@ impl<CTX> HashStable<CTX> for InferConst<'_> {
}
}
}
enum EvalMode {
Typeck,
Mir,
}
enum EvalResult<'tcx> {
ValTree(ty::ValTree<'tcx>),
ConstVal(ConstValue<'tcx>),
}
impl<'tcx> ConstKind<'tcx> {
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
/// unevaluated constant.
pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
/// return `None`.
// FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
pub fn try_eval_for_mir(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
/// return `None`.
// FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
pub fn try_eval_for_typeck(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
Some(Err(e)) => Some(Err(e)),
None => None,
}
}
#[inline]
fn try_eval_inner(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
eval_mode: EvalMode,
) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
if let ConstKind::Unevaluated(unevaluated) = self {
use crate::mir::interpret::ErrorHandled;
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
// also does later, but we want to do it before checking for
// inference variables.
// Note that we erase regions *before* calling `with_reveal_all_normalized`,
// so that we don't try to invoke this query with
// any region variables.
// HACK(eddyb) when the query key would contain inference variables,
// attempt using identity substs and `ParamEnv` instead, that will succeed
// when the expression doesn't depend on any parameters.
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
// we can call `infcx.const_eval_resolve` which handles inference variables.
let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
def: unevaluated.def,
substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
})
} else {
tcx.erase_regions(param_env)
.with_reveal_all_normalized(tcx)
.and(tcx.erase_regions(unevaluated))
};
// FIXME(eddyb) maybe the `const_eval_*` methods should take
// `ty::ParamEnvAnd` instead of having them separate.
let (param_env, unevaluated) = param_env_and.into_parts();
// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
match eval_mode {
EvalMode::Typeck => {
match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
// and we use the original type, so nothing from `substs`
// (which may be identity substs, see above),
// can leak through `val` into the const we return.
Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
Err(ErrorHandled::TooGeneric) => None,
Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
}
}
EvalMode::Mir => {
match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
// and we use the original type, so nothing from `substs`
// (which may be identity substs, see above),
// can leak through `val` into the const we return.
Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
Err(ErrorHandled::TooGeneric) => None,
Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
}
}
}
} else {
None
}
}
}

View File

@ -108,6 +108,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type PredicateKind = ty::PredicateKind<'tcx>;
type AllocId = crate::mir::interpret::AllocId;
type InferConst = ty::InferConst<'tcx>;
type AliasConst = ty::UnevaluatedConst<'tcx>;
type ParamConst = ty::ParamConst;
type BoundConst = ty::BoundVar;
type PlaceholderConst = ty::PlaceholderConst<'tcx>;
type ValueConst = ty::ValTree<'tcx>;
type ExprConst = ty::Expr<'tcx>;
type EarlyBoundRegion = ty::EarlyBoundRegion;
type BoundRegion = ty::BoundRegion;
type FreeRegion = ty::FreeRegion;

View File

@ -81,7 +81,7 @@ pub use self::closure::{
CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{
Const, ConstData, ConstInt, ConstKind, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
};
pub use self::context::{
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed,
@ -93,7 +93,7 @@ pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*;
pub use self::sty::{
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid,
BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstKind, ConstVid,
EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
FreeRegion, GenSig, GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts,
InlineConstSubstsParts, ParamConst, ParamTy, PolyExistentialPredicate,

View File

@ -241,24 +241,6 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
}
}
impl<'tcx> fmt::Debug for ty::ConstKind<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ty::ConstKind::*;
match self {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{var:?}"),
Bound(debruijn, var) => rustc_type_ir::debug_bound_var(f, *debruijn, *var),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
f.debug_tuple("Unevaluated").field(&uv.substs).field(&uv.def).finish()
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{expr:?}"),
}
}
}
impl fmt::Debug for ty::BoundTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {

View File

@ -33,6 +33,7 @@ use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt;
use rustc_type_ir::sty::TyKind::*;
use rustc_type_ir::ConstKind as IrConstKind;
use rustc_type_ir::RegionKind as IrRegionKind;
use rustc_type_ir::TyKind as IrTyKind;
@ -40,6 +41,7 @@ use rustc_type_ir::TyKind as IrTyKind;
#[rustc_diagnostic_item = "TyKind"]
pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]

View File

@ -57,11 +57,19 @@ pub trait Interner: Sized {
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderType: Clone + Debug + Hash + Ord;
type InferTy: Clone + Debug + Hash + Ord;
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
type AllocId: Clone + Debug + Hash + Ord;
type InferConst: Clone + Debug + Hash + Ord;
type AliasConst: Clone + Debug + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord;
type ParamConst: Clone + Debug + Hash + Ord;
type BoundConst: Clone + Debug + Hash + Ord;
type InferTy: Clone + Debug + Hash + Ord;
type ValueConst: Clone + Debug + Hash + Ord;
type ExprConst: Clone + Debug + Hash + Ord;
type EarlyBoundRegion: Clone + Debug + Hash + Ord;
type BoundRegion: Clone + Debug + Hash + Ord;
type FreeRegion: Clone + Debug + Hash + Ord;

View File

@ -4,7 +4,7 @@
use crate::fold::{FallibleTypeFolder, TypeFoldable};
use crate::visit::{TypeVisitable, TypeVisitor};
use crate::{FloatTy, IntTy, Interner, UintTy};
use crate::{ConstKind, FloatTy, IntTy, Interner, UintTy};
use rustc_data_structures::functor::IdFunctor;
use rustc_data_structures::sync::Lrc;
use rustc_index::{Idx, IndexVec};
@ -182,3 +182,21 @@ impl fmt::Debug for FloatTy {
write!(f, "{}", self.name_str())
}
}
impl<I: Interner> fmt::Debug for ConstKind<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ConstKind::*;
match self {
Param(param) => write!(f, "{param:?}"),
Infer(var) => write!(f, "{var:?}"),
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => {
write!(f, "{uv:?}")
}
Value(valtree) => write!(f, "{valtree:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{expr:?}"),
}
}
}

View File

@ -876,6 +876,224 @@ where
}
}
/// Represents a constant in Rust.
// #[derive(derive_more::From)]
pub enum ConstKind<I: Interner> {
/// A const generic parameter.
Param(I::ParamConst),
/// Infer the value of the const.
Infer(I::InferConst),
/// Bound const variable, used only when preparing a trait query.
Bound(DebruijnIndex, I::BoundConst),
/// A placeholder const - universally quantified higher-ranked const.
Placeholder(I::PlaceholderConst),
/// An unnormalized const item such as an anon const or assoc const or free const item.
/// Right now anything other than anon consts does not actually work properly but this
/// should
Unevaluated(I::AliasConst),
/// Used to hold computed value.
Value(I::ValueConst),
/// A placeholder for a const which could not be computed; this is
/// propagated to avoid useless error messages.
Error(I::ErrorGuaranteed),
/// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent
/// const arguments such as `N + 1` or `foo(N)`
Expr(I::ExprConst),
}
const fn const_kind_discriminant<I: Interner>(value: &ConstKind<I>) -> usize {
match value {
ConstKind::Param(_) => 0,
ConstKind::Infer(_) => 1,
ConstKind::Bound(_, _) => 2,
ConstKind::Placeholder(_) => 3,
ConstKind::Unevaluated(_) => 4,
ConstKind::Value(_) => 5,
ConstKind::Error(_) => 6,
ConstKind::Expr(_) => 7,
}
}
impl<I: Interner> hash::Hash for ConstKind<I> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
const_kind_discriminant(self).hash(state);
match self {
ConstKind::Param(p) => p.hash(state),
ConstKind::Infer(i) => i.hash(state),
ConstKind::Bound(d, b) => {
d.hash(state);
b.hash(state);
}
ConstKind::Placeholder(p) => p.hash(state),
ConstKind::Unevaluated(u) => u.hash(state),
ConstKind::Value(v) => v.hash(state),
ConstKind::Error(e) => e.hash(state),
ConstKind::Expr(e) => e.hash(state),
}
}
}
impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for ConstKind<I>
where
I::ParamConst: HashStable<CTX>,
I::InferConst: HashStable<CTX>,
I::BoundConst: HashStable<CTX>,
I::PlaceholderConst: HashStable<CTX>,
I::AliasConst: HashStable<CTX>,
I::ValueConst: HashStable<CTX>,
I::ErrorGuaranteed: HashStable<CTX>,
I::ExprConst: HashStable<CTX>,
{
fn hash_stable(
&self,
hcx: &mut CTX,
hasher: &mut rustc_data_structures::stable_hasher::StableHasher,
) {
const_kind_discriminant(self).hash_stable(hcx, hasher);
match self {
ConstKind::Param(p) => p.hash_stable(hcx, hasher),
ConstKind::Infer(i) => i.hash_stable(hcx, hasher),
ConstKind::Bound(d, b) => {
d.hash_stable(hcx, hasher);
b.hash_stable(hcx, hasher);
}
ConstKind::Placeholder(p) => p.hash_stable(hcx, hasher),
ConstKind::Unevaluated(u) => u.hash_stable(hcx, hasher),
ConstKind::Value(v) => v.hash_stable(hcx, hasher),
ConstKind::Error(e) => e.hash_stable(hcx, hasher),
ConstKind::Expr(e) => e.hash_stable(hcx, hasher),
}
}
}
impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
where
I::ParamConst: Decodable<D>,
I::InferConst: Decodable<D>,
I::BoundConst: Decodable<D>,
I::PlaceholderConst: Decodable<D>,
I::AliasConst: Decodable<D>,
I::ValueConst: Decodable<D>,
I::ErrorGuaranteed: Decodable<D>,
I::ExprConst: Decodable<D>,
{
fn decode(d: &mut D) -> Self {
match Decoder::read_usize(d) {
0 => ConstKind::Param(Decodable::decode(d)),
1 => ConstKind::Infer(Decodable::decode(d)),
2 => ConstKind::Bound(Decodable::decode(d), Decodable::decode(d)),
3 => ConstKind::Placeholder(Decodable::decode(d)),
4 => ConstKind::Unevaluated(Decodable::decode(d)),
5 => ConstKind::Value(Decodable::decode(d)),
6 => ConstKind::Error(Decodable::decode(d)),
7 => ConstKind::Expr(Decodable::decode(d)),
_ => panic!(
"{}",
format!(
"invalid enum variant tag while decoding `{}`, expected 0..{}",
"ConstKind", 8,
)
),
}
}
}
impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
where
I::ParamConst: Encodable<E>,
I::InferConst: Encodable<E>,
I::BoundConst: Encodable<E>,
I::PlaceholderConst: Encodable<E>,
I::AliasConst: Encodable<E>,
I::ValueConst: Encodable<E>,
I::ErrorGuaranteed: Encodable<E>,
I::ExprConst: Encodable<E>,
{
fn encode(&self, e: &mut E) {
let disc = const_kind_discriminant(self);
match self {
ConstKind::Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
ConstKind::Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)),
ConstKind::Bound(d, b) => e.emit_enum_variant(disc, |e| {
d.encode(e);
b.encode(e);
}),
ConstKind::Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
ConstKind::Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)),
ConstKind::Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)),
ConstKind::Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)),
ConstKind::Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)),
}
}
}
impl<I: Interner> PartialOrd for ConstKind<I> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<I: Interner> Ord for ConstKind<I> {
fn cmp(&self, other: &Self) -> Ordering {
const_kind_discriminant(self)
.cmp(&const_kind_discriminant(other))
.then_with(|| match (self, other) {
(ConstKind::Param(p1), ConstKind::Param(p2)) => p1.cmp(p2),
(ConstKind::Infer(i1), ConstKind::Infer(i2)) => i1.cmp(i2),
(ConstKind::Bound(d1, b1), ConstKind::Bound(d2, b2)) => d1.cmp(d2).then_with(|| b1.cmp(b2)),
(ConstKind::Placeholder(p1), ConstKind::Placeholder(p2)) => p1.cmp(p2),
(ConstKind::Unevaluated(u1), ConstKind::Unevaluated(u2)) => u1.cmp(u2),
(ConstKind::Value(v1), ConstKind::Value(v2)) => v1.cmp(v2),
(ConstKind::Error(e1), ConstKind::Error(e2)) => e1.cmp(e2),
(ConstKind::Expr(e1), ConstKind::Expr(e2)) => e1.cmp(e2),
_ => {
debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}");
Ordering::Equal
}
})
}
}
impl<I: Interner> PartialEq for ConstKind<I> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Param(l0), Self::Param(r0)) => l0 == r0,
(Self::Infer(l0), Self::Infer(r0)) => l0 == r0,
(Self::Bound(l0, l1), Self::Bound(r0, r1)) => l0 == r0 && l1 == r1,
(Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0,
(Self::Unevaluated(l0), Self::Unevaluated(r0)) => l0 == r0,
(Self::Value(l0), Self::Value(r0)) => l0 == r0,
(Self::Error(l0), Self::Error(r0)) => l0 == r0,
(Self::Expr(l0), Self::Expr(r0)) => l0 == r0,
_ => false,
}
}
}
impl<I: Interner> Eq for ConstKind<I> {}
impl<I: Interner> Clone for ConstKind<I> {
fn clone(&self) -> Self {
match self {
Self::Param(arg0) => Self::Param(arg0.clone()),
Self::Infer(arg0) => Self::Infer(arg0.clone()),
Self::Bound(arg0, arg1) => Self::Bound(arg0.clone(), arg1.clone()),
Self::Placeholder(arg0) => Self::Placeholder(arg0.clone()),
Self::Unevaluated(arg0) => Self::Unevaluated(arg0.clone()),
Self::Value(arg0) => Self::Value(arg0.clone()),
Self::Error(arg0) => Self::Error(arg0.clone()),
Self::Expr(arg0) => Self::Expr(arg0.clone()),
}
}
}
/// Representation of regions. Note that the NLL checker uses a distinct
/// representation of regions. For this reason, it internally replaces all the
/// regions with inference variables -- the index of the variable is then used