diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 44cdb9b1cd8..25b62d911d0 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -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 { - 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 { - 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, 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, 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, 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> { + if let ConstKind::Value(val) = self.kind() { Some(val) } else { None } + } + + #[inline] + pub fn try_to_scalar(self) -> Option> { + self.try_to_value()?.try_to_scalar() + } + + #[inline] + pub fn try_to_scalar_int(self) -> Option { + self.try_to_value()?.try_to_scalar_int() + } + + #[inline] + pub fn try_to_bits(self, size: Size) -> Option { + self.try_to_scalar_int()?.to_bits(size).ok() + } + + #[inline] + pub fn try_to_bool(self) -> Option { + self.try_to_scalar_int()?.try_into().ok() + } + + #[inline] + pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { + self.try_to_value()?.try_to_target_usize(tcx) + } + pub fn is_ct_infer(self) -> bool { matches!(self.kind(), ty::ConstKind::Infer(_)) } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 1dd4f8a2437..61b02e058e4 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -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> 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> { - if let ConstKind::Value(val) = self { Some(val) } else { None } - } - - #[inline] - pub fn try_to_scalar(self) -> Option> { - self.try_to_value()?.try_to_scalar() - } - - #[inline] - pub fn try_to_scalar_int(self) -> Option { - self.try_to_value()?.try_to_scalar_int() - } - - #[inline] - pub fn try_to_bits(self, size: Size) -> Option { - self.try_to_scalar_int()?.to_bits(size).ok() - } - - #[inline] - pub fn try_to_bool(self) -> Option { - self.try_to_scalar_int()?.try_into().ok() - } - - #[inline] - pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { - 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 HashStable 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, 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, 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, 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 - } - } -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e50331bc461..83e0c849c55 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 48388be2f46..b443708b22c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 15fb94fe090..fa724832718 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -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 { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index bb6d49e1773..9046570699e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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>; pub type RegionKind<'tcx> = IrRegionKind>; +pub type ConstKind<'tcx> = IrConstKind>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index ae16fbb162e..878a6b784ed 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -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; diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs index 553d7f31b2d..1e42175f6e3 100644 --- a/compiler/rustc_type_ir/src/structural_impls.rs +++ b/compiler/rustc_type_ir/src/structural_impls.rs @@ -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 fmt::Debug for ConstKind { + 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:?}"), + } + } +} diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index f621673f1d6..b696f9b9b59 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -876,6 +876,224 @@ where } } +/// Represents a constant in Rust. +// #[derive(derive_more::From)] +pub enum ConstKind { + /// 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(value: &ConstKind) -> 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 hash::Hash for ConstKind { + fn hash(&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 HashStable for ConstKind +where + I::ParamConst: HashStable, + I::InferConst: HashStable, + I::BoundConst: HashStable, + I::PlaceholderConst: HashStable, + I::AliasConst: HashStable, + I::ValueConst: HashStable, + I::ErrorGuaranteed: HashStable, + I::ExprConst: HashStable, +{ + 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> Decodable for ConstKind +where + I::ParamConst: Decodable, + I::InferConst: Decodable, + I::BoundConst: Decodable, + I::PlaceholderConst: Decodable, + I::AliasConst: Decodable, + I::ValueConst: Decodable, + I::ErrorGuaranteed: Decodable, + I::ExprConst: Decodable, +{ + 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> Encodable for ConstKind +where + I::ParamConst: Encodable, + I::InferConst: Encodable, + I::BoundConst: Encodable, + I::PlaceholderConst: Encodable, + I::AliasConst: Encodable, + I::ValueConst: Encodable, + I::ErrorGuaranteed: Encodable, + I::ExprConst: Encodable, +{ + 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 PartialOrd for ConstKind { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ConstKind { + 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 PartialEq for ConstKind { + 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 Eq for ConstKind {} + +impl Clone for ConstKind { + 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