From 763aaef670f7ac1e00003beec760188d9987173d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 11 Jul 2020 09:49:22 +0200 Subject: [PATCH] Move `ty::Const` and `ty::ConstKind` into their own modules --- src/librustc_middle/ty/consts.rs | 292 +++++++++++++++-------- src/librustc_middle/ty/consts/int.rs | 111 +++++++++ src/librustc_middle/ty/consts/kind.rs | 135 +++++++++++ src/librustc_middle/ty/mod.rs | 6 +- src/librustc_middle/ty/sty.rs | 327 +------------------------- 5 files changed, 445 insertions(+), 426 deletions(-) create mode 100644 src/librustc_middle/ty/consts/int.rs create mode 100644 src/librustc_middle/ty/consts/kind.rs diff --git a/src/librustc_middle/ty/consts.rs b/src/librustc_middle/ty/consts.rs index ced0429deab..d861b932367 100644 --- a/src/librustc_middle/ty/consts.rs +++ b/src/librustc_middle/ty/consts.rs @@ -1,111 +1,203 @@ -use crate::mir::interpret::truncate; -use rustc_target::abi::Size; +use crate::mir::interpret::ConstValue; +use crate::mir::interpret::{LitToConstInput, Scalar}; +use crate::ty::subst::InternalSubsts; +use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{ParamEnv, ParamEnvAnd}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_macros::HashStable; -#[derive(Copy, Clone)] -/// A type for representing any integer. Only used for printing. -// FIXME: Use this for the integer-tree representation needed for type level ints and -// const generics? -pub struct ConstInt { - /// Number of bytes of the integer. Only 1, 2, 4, 8, 16 are legal values. - size: u8, - /// Whether the value is of a signed integer type. - signed: bool, - /// Whether the value is a `usize` or `isize` type. - is_ptr_sized_integral: bool, - /// Raw memory of the integer. All bytes beyond the `size` are unused and must be zero. - raw: u128, +mod int; +mod kind; + +pub use int::*; +pub use kind::*; + +/// Typed constant value. +#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(HashStable)] +pub struct Const<'tcx> { + pub ty: Ty<'tcx>, + + pub val: ConstKind<'tcx>, } -impl ConstInt { - pub fn new(raw: u128, size: Size, signed: bool, is_ptr_sized_integral: bool) -> Self { - assert!(raw <= truncate(u128::MAX, size)); - Self { raw, size: size.bytes() as u8, signed, is_ptr_sized_integral } +#[cfg(target_arch = "x86_64")] +static_assert_size!(Const<'_>, 48); + +impl<'tcx> Const<'tcx> { + /// Literals and const generic parameters are eagerly converted to a constant, everything else + /// becomes `Unevaluated`. + pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) } -} -impl std::fmt::Debug for ConstInt { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self { size, signed, raw, is_ptr_sized_integral } = *self; - if signed { - let bit_size = size * 8; - let min = 1u128 << (bit_size - 1); - let max = min - 1; - if raw == min { - match (size, is_ptr_sized_integral) { - (_, true) => write!(fmt, "isize::MIN"), - (1, _) => write!(fmt, "i8::MIN"), - (2, _) => write!(fmt, "i16::MIN"), - (4, _) => write!(fmt, "i32::MIN"), - (8, _) => write!(fmt, "i64::MIN"), - (16, _) => write!(fmt, "i128::MIN"), - _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), - } - } else if raw == max { - match (size, is_ptr_sized_integral) { - (_, true) => write!(fmt, "isize::MAX"), - (1, _) => write!(fmt, "i8::MAX"), - (2, _) => write!(fmt, "i16::MAX"), - (4, _) => write!(fmt, "i32::MAX"), - (8, _) => write!(fmt, "i64::MAX"), - (16, _) => write!(fmt, "i128::MAX"), - _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + pub fn from_opt_const_arg_anon_const( + tcx: TyCtxt<'tcx>, + def: ty::WithOptConstParam, + ) -> &'tcx Self { + debug!("Const::from_anon_const(def={:?})", def); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); + + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def.did.to_def_id()), + "from_anon_const can only process anonymous constants" + ), + }; + + let expr = &tcx.hir().body(body_id).value; + + let ty = tcx.type_of(def.def_id_for_type_of()); + + let lit_input = match expr.kind { + hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { + hir::ExprKind::Lit(ref lit) => { + Some(LitToConstInput { lit: &lit.node, ty, neg: true }) } + _ => None, + }, + _ => None, + }; + + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { + return c; } else { - match size { - 1 => write!(fmt, "{}", raw as i8)?, - 2 => write!(fmt, "{}", raw as i16)?, - 4 => write!(fmt, "{}", raw as i32)?, - 8 => write!(fmt, "{}", raw as i64)?, - 16 => write!(fmt, "{}", raw as i128)?, - _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), - } - if fmt.alternate() { - match (size, is_ptr_sized_integral) { - (_, true) => write!(fmt, "_isize")?, - (1, _) => write!(fmt, "_i8")?, - (2, _) => write!(fmt, "_i16")?, - (4, _) => write!(fmt, "_i32")?, - (8, _) => write!(fmt, "_i64")?, - (16, _) => write!(fmt, "_i128")?, - _ => bug!(), - } - } - Ok(()) - } - } else { - let max = truncate(u128::MAX, Size::from_bytes(size)); - if raw == max { - match (size, is_ptr_sized_integral) { - (_, true) => write!(fmt, "usize::MAX"), - (1, _) => write!(fmt, "u8::MAX"), - (2, _) => write!(fmt, "u16::MAX"), - (4, _) => write!(fmt, "u32::MAX"), - (8, _) => write!(fmt, "u64::MAX"), - (16, _) => write!(fmt, "u128::MAX"), - _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), - } - } else { - match size { - 1 => write!(fmt, "{}", raw as u8)?, - 2 => write!(fmt, "{}", raw as u16)?, - 4 => write!(fmt, "{}", raw as u32)?, - 8 => write!(fmt, "{}", raw as u64)?, - 16 => write!(fmt, "{}", raw as u128)?, - _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), - } - if fmt.alternate() { - match (size, is_ptr_sized_integral) { - (_, true) => write!(fmt, "_usize")?, - (1, _) => write!(fmt, "_u8")?, - (2, _) => write!(fmt, "_u16")?, - (4, _) => write!(fmt, "_u32")?, - (8, _) => write!(fmt, "_u64")?, - (16, _) => write!(fmt, "_u128")?, - _ => bug!(), - } - } - Ok(()) + tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); } } + + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = match &expr.kind { + hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { + block.expr.as_ref().unwrap() + } + _ => expr, + }; + + use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; + let val = match expr.kind { + ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { + // Find the name and index of the const parameter by indexing the generics of + // the parent item and construct a `ParamConst`. + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let item_id = tcx.hir().get_parent_node(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id.to_def_id()); + let index = + generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; + let name = tcx.hir().name(hir_id); + ty::ConstKind::Param(ty::ParamConst::new(index, name)) + } + _ => ty::ConstKind::Unevaluated( + def.to_global(), + InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), + None, + ), + }; + + tcx.mk_const(ty::Const { val, ty }) + } + + #[inline] + /// Interns the given value as a constant. + pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + tcx.mk_const(Self { val: ConstKind::Value(val), ty }) + } + + #[inline] + /// Interns the given scalar as a constant. + pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { + Self::from_value(tcx, ConstValue::Scalar(val), ty) + } + + #[inline] + /// Creates a constant with the given integer value and interns it. + pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { + let size = tcx + .layout_of(ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) + } + + #[inline] + /// Creates an interned zst constant. + pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { + Self::from_scalar(tcx, Scalar::zst(), ty) + } + + #[inline] + /// Creates an interned bool constant. + pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { + Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) + } + + #[inline] + /// Creates an interned usize constant. + pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { + Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) + } + + #[inline] + /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of + /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it + /// contains const generic parameters or pointers). + pub fn try_eval_bits( + &self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option { + assert_eq!(self.ty, ty); + let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; + // if `ty` does not depend on generic parameters, use an empty param_env + self.val.eval(tcx, param_env).try_to_bits(size) + } + + #[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.val.try_eval(tcx, param_env) { + match val { + Ok(val) => Const::from_value(tcx, val, self.ty), + Err(ErrorReported) => tcx.const_error(self.ty), + } + } else { + self + } + } + + #[inline] + pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val.eval(tcx, param_env).try_to_bool() + } + + #[inline] + pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { + self.val.eval(tcx, param_env).try_to_machine_usize(tcx) + } + + #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. + pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { + self.try_eval_bits(tcx, param_env, ty) + .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) + } + + #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. + pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { + self.try_eval_usize(tcx, param_env) + .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) } } diff --git a/src/librustc_middle/ty/consts/int.rs b/src/librustc_middle/ty/consts/int.rs new file mode 100644 index 00000000000..ced0429deab --- /dev/null +++ b/src/librustc_middle/ty/consts/int.rs @@ -0,0 +1,111 @@ +use crate::mir::interpret::truncate; +use rustc_target::abi::Size; + +#[derive(Copy, Clone)] +/// A type for representing any integer. Only used for printing. +// FIXME: Use this for the integer-tree representation needed for type level ints and +// const generics? +pub struct ConstInt { + /// Number of bytes of the integer. Only 1, 2, 4, 8, 16 are legal values. + size: u8, + /// Whether the value is of a signed integer type. + signed: bool, + /// Whether the value is a `usize` or `isize` type. + is_ptr_sized_integral: bool, + /// Raw memory of the integer. All bytes beyond the `size` are unused and must be zero. + raw: u128, +} + +impl ConstInt { + pub fn new(raw: u128, size: Size, signed: bool, is_ptr_sized_integral: bool) -> Self { + assert!(raw <= truncate(u128::MAX, size)); + Self { raw, size: size.bytes() as u8, signed, is_ptr_sized_integral } + } +} + +impl std::fmt::Debug for ConstInt { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { size, signed, raw, is_ptr_sized_integral } = *self; + if signed { + let bit_size = size * 8; + let min = 1u128 << (bit_size - 1); + let max = min - 1; + if raw == min { + match (size, is_ptr_sized_integral) { + (_, true) => write!(fmt, "isize::MIN"), + (1, _) => write!(fmt, "i8::MIN"), + (2, _) => write!(fmt, "i16::MIN"), + (4, _) => write!(fmt, "i32::MIN"), + (8, _) => write!(fmt, "i64::MIN"), + (16, _) => write!(fmt, "i128::MIN"), + _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + } + } else if raw == max { + match (size, is_ptr_sized_integral) { + (_, true) => write!(fmt, "isize::MAX"), + (1, _) => write!(fmt, "i8::MAX"), + (2, _) => write!(fmt, "i16::MAX"), + (4, _) => write!(fmt, "i32::MAX"), + (8, _) => write!(fmt, "i64::MAX"), + (16, _) => write!(fmt, "i128::MAX"), + _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + } + } else { + match size { + 1 => write!(fmt, "{}", raw as i8)?, + 2 => write!(fmt, "{}", raw as i16)?, + 4 => write!(fmt, "{}", raw as i32)?, + 8 => write!(fmt, "{}", raw as i64)?, + 16 => write!(fmt, "{}", raw as i128)?, + _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + } + if fmt.alternate() { + match (size, is_ptr_sized_integral) { + (_, true) => write!(fmt, "_isize")?, + (1, _) => write!(fmt, "_i8")?, + (2, _) => write!(fmt, "_i16")?, + (4, _) => write!(fmt, "_i32")?, + (8, _) => write!(fmt, "_i64")?, + (16, _) => write!(fmt, "_i128")?, + _ => bug!(), + } + } + Ok(()) + } + } else { + let max = truncate(u128::MAX, Size::from_bytes(size)); + if raw == max { + match (size, is_ptr_sized_integral) { + (_, true) => write!(fmt, "usize::MAX"), + (1, _) => write!(fmt, "u8::MAX"), + (2, _) => write!(fmt, "u16::MAX"), + (4, _) => write!(fmt, "u32::MAX"), + (8, _) => write!(fmt, "u64::MAX"), + (16, _) => write!(fmt, "u128::MAX"), + _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + } + } else { + match size { + 1 => write!(fmt, "{}", raw as u8)?, + 2 => write!(fmt, "{}", raw as u16)?, + 4 => write!(fmt, "{}", raw as u32)?, + 8 => write!(fmt, "{}", raw as u64)?, + 16 => write!(fmt, "{}", raw as u128)?, + _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed), + } + if fmt.alternate() { + match (size, is_ptr_sized_integral) { + (_, true) => write!(fmt, "_usize")?, + (1, _) => write!(fmt, "_u8")?, + (2, _) => write!(fmt, "_u16")?, + (4, _) => write!(fmt, "_u32")?, + (8, _) => write!(fmt, "_u64")?, + (16, _) => write!(fmt, "_u128")?, + _ => bug!(), + } + } + Ok(()) + } + } + } +} diff --git a/src/librustc_middle/ty/consts/kind.rs b/src/librustc_middle/ty/consts/kind.rs new file mode 100644 index 00000000000..d1deea4a90e --- /dev/null +++ b/src/librustc_middle/ty/consts/kind.rs @@ -0,0 +1,135 @@ +use crate::mir::interpret::ConstValue; +use crate::mir::interpret::Scalar; +use crate::mir::Promoted; +use crate::ty::subst::{InternalSubsts, SubstsRef}; +use crate::ty::ParamEnv; +use crate::ty::{self, TyCtxt, TypeFoldable}; +use rustc_errors::ErrorReported; +use rustc_hir::def_id::DefId; +use rustc_macros::HashStable; +use rustc_target::abi::Size; + +/// Represents a constant in Rust. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] +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), + + /// 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(ty::WithOptConstParam, SubstsRef<'tcx>, Option), + + /// Used to hold computed value. + Value(ConstValue<'tcx>), + + /// A placeholder for a const which could not be computed; this is + /// propagated to avoid useless error messages. + Error(ty::sty::DelaySpanBugEmitted), +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(ConstKind<'_>, 40); + +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_bits(self, size: Size) -> Option { + self.try_to_value()?.try_to_bits(size) + } + + #[inline] + pub fn try_to_bool(self) -> Option { + self.try_to_value()?.try_to_bool() + } + + #[inline] + pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option { + self.try_to_value()?.try_to_machine_usize(tcx) + } +} + +/// An inference variable for a const, for use in const generics. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] +pub enum InferConst<'tcx> { + /// Infer the value of the const. + Var(ty::ConstVid<'tcx>), + /// A fresh const variable. See `infer::freshen` for more details. + Fresh(u32), +} + +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(tcx, param_env).and_then(Result::ok).map(ConstKind::Value).unwrap_or(self) + } + + #[inline] + /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary + // return None + pub fn try_eval( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ) -> Option, ErrorReported>> { + if let ConstKind::Unevaluated(def, substs, promoted) = self { + use crate::mir::interpret::ErrorHandled; + + let param_env_and_substs = param_env.with_reveal_all().and(substs); + + // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` + // also does later, but we want to do it before checking for + // inference variables. + let param_env_and_substs = tcx.erase_regions(¶m_env_and_substs); + + // 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_substs = if param_env_and_substs.needs_infer() { + tcx.param_env(def.did).and(InternalSubsts::identity_for_item(tcx, def.did)) + } else { + param_env_and_substs + }; + + // FIXME(eddyb) maybe the `const_eval_*` methods should take + // `ty::ParamEnvAnd` instead of having them separate. + let (param_env, substs) = param_env_and_substs.into_parts(); + // try to resolve e.g. associated constants to their definition on an impl, and then + // evaluate the const. + match tcx.const_eval_resolve(param_env, def, substs, promoted, 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(val)), + Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, + Err(ErrorHandled::Reported(e)) => Some(Err(e)), + } + } else { + None + } + } +} diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index fe2a60401b4..b3cfcc0f4c2 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -60,9 +60,9 @@ pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNER pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region}; pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; -pub use self::sty::{Const, ConstKind, ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; -pub use self::sty::{ExistentialPredicate, InferConst, InferTy, ParamConst, ParamTy, ProjectionTy}; +pub use self::sty::{ExistentialPredicate, InferTy, ParamConst, ParamTy, ProjectionTy}; +pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; pub use crate::ty::diagnostics::*; @@ -87,7 +87,7 @@ pub use self::trait_def::TraitDef; pub use self::query::queries; -pub use self::consts::ConstInt; +pub use self::consts::{Const, ConstInt, ConstKind, InferConst}; pub mod adjustment; pub mod binding; diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index 930bdd168b7..2f17db62233 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -6,24 +6,20 @@ use self::InferTy::*; use self::TyKind::*; use crate::infer::canonical::Canonical; -use crate::mir::interpret::ConstValue; -use crate::mir::interpret::{LitToConstInput, Scalar}; -use crate::mir::Promoted; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::{ self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable, WithConstness, }; -use crate::ty::{List, ParamEnv, ParamEnvAnd, TyS}; +use crate::ty::{List, ParamEnv, TyS}; use polonius_engine::Atom; use rustc_ast::ast; use rustc_data_structures::captures::Captures; -use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; use rustc_macros::HashStable; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_target::abi::{Size, VariantIdx}; +use rustc_target::abi::VariantIdx; use rustc_target::spec::abi; use std::borrow::Cow; use std::cmp::Ordering; @@ -1122,7 +1118,7 @@ impl<'tcx> ParamConst { ParamConst::new(def.index, def.name) } - pub fn to_const(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + pub fn to_const(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { tcx.mk_const_param(self.index, self.name, ty) } } @@ -2193,318 +2189,3 @@ impl<'tcx> TyS<'tcx> { tcx.layout_of(tcx.param_env(did).and(self)).map(|layout| layout.is_zst()).unwrap_or(false) } } - -/// Typed constant value. -#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] -#[derive(HashStable)] -pub struct Const<'tcx> { - pub ty: Ty<'tcx>, - - pub val: ConstKind<'tcx>, -} - -#[cfg(target_arch = "x86_64")] -static_assert_size!(Const<'_>, 48); - -impl<'tcx> Const<'tcx> { - /// Literals and const generic parameters are eagerly converted to a constant, everything else - /// becomes `Unevaluated`. - pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { - Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id)) - } - - pub fn from_opt_const_arg_anon_const( - tcx: TyCtxt<'tcx>, - def: ty::WithOptConstParam, - ) -> &'tcx Self { - debug!("Const::from_anon_const(def={:?})", def); - - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - - let body_id = match tcx.hir().get(hir_id) { - hir::Node::AnonConst(ac) => ac.body, - _ => span_bug!( - tcx.def_span(def.did.to_def_id()), - "from_anon_const can only process anonymous constants" - ), - }; - - let expr = &tcx.hir().body(body_id).value; - - let ty = tcx.type_of(def.def_id_for_type_of()); - - let lit_input = match expr.kind { - hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { - hir::ExprKind::Lit(ref lit) => { - Some(LitToConstInput { lit: &lit.node, ty, neg: true }) - } - _ => None, - }, - _ => None, - }; - - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return c; - } else { - tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); - } - } - - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = match &expr.kind { - hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { - block.expr.as_ref().unwrap() - } - _ => expr, - }; - - use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; - let val = match expr.kind { - ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { - // Find the name and index of the const parameter by indexing the generics of - // the parent item and construct a `ParamConst`. - let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); - let item_id = tcx.hir().get_parent_node(hir_id); - let item_def_id = tcx.hir().local_def_id(item_id); - let generics = tcx.generics_of(item_def_id.to_def_id()); - let index = - generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; - let name = tcx.hir().name(hir_id); - ty::ConstKind::Param(ty::ParamConst::new(index, name)) - } - _ => ty::ConstKind::Unevaluated( - def.to_global(), - InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), - None, - ), - }; - - tcx.mk_const(ty::Const { val, ty }) - } - - #[inline] - /// Interns the given value as a constant. - pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - tcx.mk_const(Self { val: ConstKind::Value(val), ty }) - } - - #[inline] - /// Interns the given scalar as a constant. - pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_value(tcx, ConstValue::Scalar(val), ty) - } - - #[inline] - /// Creates a constant with the given integer value and interns it. - pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { - let size = tcx - .layout_of(ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; - Self::from_scalar(tcx, Scalar::from_uint(bits, size), ty.value) - } - - #[inline] - /// Creates an interned zst constant. - pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_scalar(tcx, Scalar::zst(), ty) - } - - #[inline] - /// Creates an interned bool constant. - pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { - Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) - } - - #[inline] - /// Creates an interned usize constant. - pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { - Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) - } - - #[inline] - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - pub fn try_eval_bits( - &self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ty: Ty<'tcx>, - ) -> Option { - assert_eq!(self.ty, ty); - let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; - // if `ty` does not depend on generic parameters, use an empty param_env - self.val.eval(tcx, param_env).try_to_bits(size) - } - - #[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.val.try_eval(tcx, param_env) { - match val { - Ok(val) => Const::from_value(tcx, val, self.ty), - Err(ErrorReported) => tcx.const_error(self.ty), - } - } else { - self - } - } -} - -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(tcx, param_env).and_then(Result::ok).map(ConstKind::Value).unwrap_or(self) - } - - #[inline] - /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary - // return None - pub fn try_eval( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option, ErrorReported>> { - if let ConstKind::Unevaluated(def, substs, promoted) = self { - use crate::mir::interpret::ErrorHandled; - - let param_env_and_substs = param_env.with_reveal_all().and(substs); - - // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` - // also does later, but we want to do it before checking for - // inference variables. - let param_env_and_substs = tcx.erase_regions(¶m_env_and_substs); - - // 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_substs = if param_env_and_substs.needs_infer() { - tcx.param_env(def.did).and(InternalSubsts::identity_for_item(tcx, def.did)) - } else { - param_env_and_substs - }; - - // FIXME(eddyb) maybe the `const_eval_*` methods should take - // `ty::ParamEnvAnd` instead of having them separate. - let (param_env, substs) = param_env_and_substs.into_parts(); - // try to resolve e.g. associated constants to their definition on an impl, and then - // evaluate the const. - match tcx.const_eval_resolve(param_env, def, substs, promoted, 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(val)), - Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None, - Err(ErrorHandled::Reported(e)) => Some(Err(e)), - } - } else { - None - } - } -} - -impl<'tcx> Const<'tcx> { - #[inline] - pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_bool() - } - - #[inline] - pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.val.eval(tcx, param_env).try_to_machine_usize(tcx) - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. - pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { - self.try_eval_bits(tcx, param_env, ty) - .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) - } - - #[inline] - /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. - pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { - self.try_eval_usize(tcx, param_env) - .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) - } -} - -/// Represents a constant in Rust. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] -#[derive(HashStable)] -pub enum ConstKind<'tcx> { - /// A const generic parameter. - Param(ParamConst), - - /// Infer the value of the const. - Infer(InferConst<'tcx>), - - /// Bound const variable, used only when preparing a trait query. - Bound(DebruijnIndex, BoundVar), - - /// A placeholder const - universally quantified higher-ranked const. - Placeholder(ty::PlaceholderConst), - - /// 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(ty::WithOptConstParam, SubstsRef<'tcx>, Option), - - /// Used to hold computed value. - Value(ConstValue<'tcx>), - - /// A placeholder for a const which could not be computed; this is - /// propagated to avoid useless error messages. - Error(DelaySpanBugEmitted), -} - -#[cfg(target_arch = "x86_64")] -static_assert_size!(ConstKind<'_>, 40); - -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_bits(self, size: Size) -> Option { - self.try_to_value()?.try_to_bits(size) - } - - #[inline] - pub fn try_to_bool(self) -> Option { - self.try_to_value()?.try_to_bool() - } - - #[inline] - pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_value()?.try_to_machine_usize(tcx) - } -} - -/// An inference variable for a const, for use in const generics. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] -#[derive(HashStable)] -pub enum InferConst<'tcx> { - /// Infer the value of the const. - Var(ConstVid<'tcx>), - /// A fresh const variable. See `infer::freshen` for more details. - Fresh(u32), -}