Move ty::Const
and ty::ConstKind
into their own modules
This commit is contained in:
parent
5c0e172f91
commit
763aaef670
@ -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<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(())
|
||||
}
|
||||
} 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<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]
|
||||
/// 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<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]
|
||||
/// 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
src/librustc_middle/ty/consts/int.rs
Normal file
111
src/librustc_middle/ty/consts/int.rs
Normal file
@ -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
src/librustc_middle/ty/consts/kind.rs
Normal file
135
src/librustc_middle/ty/consts/kind.rs
Normal file
@ -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<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 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(¶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<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
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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<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.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<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(¶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<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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Const<'tcx> {
|
||||
#[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]
|
||||
/// 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<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_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(ConstVid<'tcx>),
|
||||
/// A fresh const variable. See `infer::freshen` for more details.
|
||||
Fresh(u32),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user