Skip to content

Reduce the amount of interning and layout_of calls in const eval. #74202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 16, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/librustc_middle/mir/interpret/value.rs
Original file line number Diff line number Diff line change
@@ -60,6 +60,18 @@ impl<'tcx> ConstValue<'tcx> {
self.try_to_scalar()?.to_bits(size).ok()
}

pub fn try_to_bool(&self) -> Option<bool> {
match self.try_to_bits(Size::from_bytes(1))? {
0 => Some(false),
1 => Some(true),
_ => None,
}
}

pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
Some(self.try_to_bits(tcx.data_layout.pointer_size)? as u64)
}

pub fn try_to_bits_for_ty(
&self,
tcx: TyCtxt<'tcx>,
292 changes: 192 additions & 100 deletions src/librustc_middle/ty/consts.rs
Original file line number Diff line number Diff line change
@@ -1,111 +1,203 @@
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,
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;

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<LocalDefId>,
) -> &'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(())
tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const");
}
} 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(())
}

// 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<u128> {
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]
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
self.val.eval(tcx, param_env).try_to_bool()
}

#[inline]
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> {
self.val.eval(tcx, param_env).try_to_machine_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.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]
/// 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))
}
}
111 changes: 111 additions & 0 deletions src/librustc_middle/ty/consts/int.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
}
}
135 changes: 135 additions & 0 deletions src/librustc_middle/ty/consts/kind.rs
Original file line number Diff line number Diff line change
@@ -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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longer-term I feel like this is really the thing that ought to be called Const, and the existing Const should become ConstAndTy or so... but that is for a separate PR.

/// 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<DefId>, SubstsRef<'tcx>, Option<Promoted>),

/// 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<ConstValue<'tcx>> {
if let ConstKind::Value(val) = self { Some(val) } else { None }
}

#[inline]
pub fn try_to_scalar(self) -> Option<Scalar> {
self.try_to_value()?.try_to_scalar()
}

#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_value()?.try_to_bits(size)
}

#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_value()?.try_to_bool()
}

#[inline]
pub fn try_to_machine_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
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(super) fn try_eval(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<Result<ConstValue<'tcx>, 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(&param_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<SubstsRef>` 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
}
}
}
6 changes: 3 additions & 3 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
286 changes: 4 additions & 282 deletions src/librustc_middle/ty/sty.rs
Original file line number Diff line number Diff line change
@@ -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,277 +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<LocalDefId>,
) -> &'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<u128> {
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.eval(tcx, param_env).val.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 ConstKind::Unevaluated(def, substs, promoted) = self.val {
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(&param_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<SubstsRef>` 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) => Const::from_value(tcx, val, self.ty),
Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self,
Err(ErrorHandled::Reported(ErrorReported)) => tcx.const_error(self.ty),
}
} else {
self
}
}

#[inline]
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
self.try_eval_bits(tcx, param_env, tcx.types.bool).and_then(|v| match v {
0 => Some(false),
1 => Some(true),
_ => None,
})
}

#[inline]
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> {
self.try_eval_bits(tcx, param_env, tcx.types.usize).map(|v| v as u64)
}

#[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.eval_bits(tcx, param_env, tcx.types.usize) as u64
}
}

/// 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<DefId>, SubstsRef<'tcx>, Option<Promoted>),

/// 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_scalar(&self) -> Option<Scalar> {
if let ConstKind::Value(val) = self { val.try_to_scalar() } else { None }
}

#[inline]
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
if let ConstKind::Value(val) = self { val.try_to_bits(size) } else { None }
}
}

/// 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),
}