Auto merge of #82936 - oli-obk:valtree, r=RalfJung,lcnr,matthewjasper

Implement (but don't use) valtree and refactor in preparation of use

This PR does not cause any functional change. It refactors various things that are needed to make valtrees possible. This refactoring got big enough that I decided I'd want it reviewed as a PR instead of trying to make one huge PR with all the changes.

cc `@rust-lang/wg-const-eval` on the following commits:

* 2027184 implement valtree
* eeecea9 fallible Scalar -> ScalarInt
* 042f663 ScalarInt convenience methods

cc `@eddyb` on ef04a6d

cc `@rust-lang/wg-mir-opt` for cf1700c (`mir::Constant` can now represent either a `ConstValue` or a `ty::Const`, and it is totally possible to have two different representations for the same value)
This commit is contained in:
bors 2021-03-16 22:42:56 +00:00
commit e655fb6221
56 changed files with 783 additions and 331 deletions

View File

@ -8,7 +8,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{ use rustc_middle::mir::interpret::{
read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar, read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar,
}; };
use rustc_middle::ty::{Const, ConstKind}; use rustc_middle::ty::ConstKind;
use cranelift_codegen::ir::GlobalValueData; use cranelift_codegen::ir::GlobalValueData;
use cranelift_module::*; use cranelift_module::*;
@ -39,7 +39,10 @@ impl ConstantCx {
pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
let mut all_constants_ok = true; let mut all_constants_ok = true;
for constant in &fx.mir.required_consts { for constant in &fx.mir.required_consts {
let const_ = fx.monomorphize(constant.literal); let const_ = match fx.monomorphize(constant.literal) {
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(..) => continue,
};
match const_.val { match const_.val {
ConstKind::Value(_) => {} ConstKind::Value(_) => {}
ConstKind::Unevaluated(def, ref substs, promoted) => { ConstKind::Unevaluated(def, ref substs, promoted) => {
@ -113,19 +116,17 @@ pub(crate) fn codegen_constant<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>, fx: &mut FunctionCx<'_, '_, 'tcx>,
constant: &Constant<'tcx>, constant: &Constant<'tcx>,
) -> CValue<'tcx> { ) -> CValue<'tcx> {
let const_ = fx.monomorphize(constant.literal); let const_ = match fx.monomorphize(constant.literal) {
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty),
};
let const_val = match const_.val { let const_val = match const_.val {
ConstKind::Value(const_val) => const_val, ConstKind::Value(const_val) => const_val,
ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => { ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => {
assert!(substs.is_empty()); assert!(substs.is_empty());
assert!(promoted.is_none()); assert!(promoted.is_none());
return codegen_static_ref( return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx);
fx,
def.did,
fx.layout_of(fx.monomorphize(&constant.literal.ty)),
)
.to_cvalue(fx);
} }
ConstKind::Unevaluated(def, ref substs, promoted) => { ConstKind::Unevaluated(def, ref substs, promoted) => {
match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) { match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) {
@ -422,11 +423,14 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
pub(crate) fn mir_operand_get_const_val<'tcx>( pub(crate) fn mir_operand_get_const_val<'tcx>(
fx: &FunctionCx<'_, '_, 'tcx>, fx: &FunctionCx<'_, '_, 'tcx>,
operand: &Operand<'tcx>, operand: &Operand<'tcx>,
) -> Option<&'tcx Const<'tcx>> { ) -> Option<ConstValue<'tcx>> {
match operand { match operand {
Operand::Copy(_) | Operand::Move(_) => None, Operand::Copy(_) | Operand::Move(_) => None,
Operand::Constant(const_) => { Operand::Constant(const_) => match const_.literal {
Some(fx.monomorphize(const_.literal).eval(fx.tcx, ParamEnv::reveal_all())) ConstantKind::Ty(const_) => {
} fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val.try_to_value()
}
ConstantKind::Val(val, _) => Some(val),
},
} }
} }

View File

@ -53,7 +53,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
}; };
llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) { llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) {
let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const"); let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const");
let flt_cc = match kind_const.val.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) { let flt_cc = match kind_const.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) {
0 => FloatCC::Equal, 0 => FloatCC::Equal,
1 => FloatCC::LessThan, 1 => FloatCC::LessThan,
2 => FloatCC::LessThanOrEqual, 2 => FloatCC::LessThanOrEqual,
@ -84,7 +84,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
llvm.x86.sse2.psrli.d, (c a, o imm8) { llvm.x86.sse2.psrli.d, (c a, o imm8) {
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
_ => fx.bcx.ins().iconst(types::I32, 0), _ => fx.bcx.ins().iconst(types::I32, 0),
}; };
@ -94,7 +94,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
llvm.x86.sse2.pslli.d, (c a, o imm8) { llvm.x86.sse2.pslli.d, (c a, o imm8) {
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
_ => fx.bcx.ins().iconst(types::I32, 0), _ => fx.bcx.ins().iconst(types::I32, 0),
}; };

View File

@ -85,8 +85,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
use rustc_middle::mir::interpret::*; use rustc_middle::mir::interpret::*;
let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const"); let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
let idx_bytes = match idx_const.val { let idx_bytes = match idx_const {
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => { ConstValue::ByRef { alloc, offset } => {
let ptr = Pointer::new(AllocId(0 /* dummy */), offset); let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */); let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */);
alloc.get_bytes(fx, ptr, size).unwrap() alloc.get_bytes(fx, ptr, size).unwrap()
@ -130,7 +130,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
); );
}; };
let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
if idx >= lane_count.into() { if idx >= lane_count.into() {
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
@ -159,7 +159,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return; return;
}; };
let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
if idx >= lane_count.into() { if idx >= lane_count.into() {
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));

View File

@ -231,7 +231,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
let check = match terminator.kind { let check = match terminator.kind {
mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => {
match *c.literal.ty.kind() { match *c.ty().kind() {
ty::FnDef(did, _) => Some((did, args)), ty::FnDef(did, _) => Some((did, args)),
_ => None, _ => None,
} }

View File

@ -635,12 +635,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
if let mir::Operand::Constant(constant) = arg { if let mir::Operand::Constant(constant) = arg {
let c = self.eval_mir_constant(constant); let c = self.eval_mir_constant(constant);
let (llval, ty) = self.simd_shuffle_indices( let (llval, ty) =
&bx, self.simd_shuffle_indices(&bx, constant.span, constant.ty(), c);
constant.span,
constant.literal.ty,
c,
);
return OperandRef { return OperandRef {
val: Immediate(llval), val: Immediate(llval),
layout: bx.layout_of(ty), layout: bx.layout_of(ty),
@ -830,7 +826,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let const_value = self let const_value = self
.eval_mir_constant(constant) .eval_mir_constant(constant)
.unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved"));
let ty = constant.literal.ty; let ty = constant.ty();
let size = bx.layout_of(ty).size; let size = bx.layout_of(ty).size;
let scalar = match const_value { let scalar = match const_value {
ConstValue::Scalar(s) => s, ConstValue::Scalar(s) => s,
@ -864,7 +860,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} }
mir::InlineAsmOperand::SymFn { ref value } => { mir::InlineAsmOperand::SymFn { ref value } => {
let literal = self.monomorphize(value.literal); let literal = self.monomorphize(value.literal);
if let ty::FnDef(def_id, substs) = *literal.ty.kind() { if let ty::FnDef(def_id, substs) = *literal.ty().kind() {
let instance = ty::Instance::resolve_for_fn_ptr( let instance = ty::Instance::resolve_for_fn_ptr(
bx.tcx(), bx.tcx(),
ty::ParamEnv::reveal_all(), ty::ParamEnv::reveal_all(),

View File

@ -16,7 +16,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
constant: &mir::Constant<'tcx>, constant: &mir::Constant<'tcx>,
) -> Result<OperandRef<'tcx, Bx::Value>, ErrorHandled> { ) -> Result<OperandRef<'tcx, Bx::Value>, ErrorHandled> {
let val = self.eval_mir_constant(constant)?; let val = self.eval_mir_constant(constant)?;
let ty = self.monomorphize(constant.literal.ty); let ty = self.monomorphize(constant.ty());
Ok(OperandRef::from_const(bx, val, ty)) Ok(OperandRef::from_const(bx, val, ty))
} }
@ -24,7 +24,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&self, &self,
constant: &mir::Constant<'tcx>, constant: &mir::Constant<'tcx>,
) -> Result<ConstValue<'tcx>, ErrorHandled> { ) -> Result<ConstValue<'tcx>, ErrorHandled> {
match self.monomorphize(constant.literal).val { let ct = self.monomorphize(constant.literal);
let ct = match ct {
mir::ConstantKind::Ty(ct) => ct,
mir::ConstantKind::Val(val, _) => return Ok(val),
};
match ct.val {
ty::ConstKind::Unevaluated(def, substs, promoted) => self ty::ConstKind::Unevaluated(def, substs, promoted) => self
.cx .cx
.tcx() .tcx()

View File

@ -372,7 +372,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(var_ty, var_kind) (var_ty, var_kind)
} }
mir::VarDebugInfoContents::Const(c) => { mir::VarDebugInfoContents::Const(c) => {
let ty = self.monomorphize(c.literal.ty); let ty = self.monomorphize(c.ty());
(ty, VariableKind::LocalVariable) (ty, VariableKind::LocalVariable)
} }
}; };

View File

@ -1,4 +1,4 @@
use std::convert::TryFrom; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use rustc_apfloat::{ use rustc_apfloat::{
@ -8,12 +8,12 @@ use rustc_apfloat::{
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt};
use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
/// Represents the result of const evaluation via the `eval_to_allocation` query. /// Represents the result of const evaluation via the `eval_to_allocation` query.
#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)] #[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
pub struct ConstAlloc<'tcx> { pub struct ConstAlloc<'tcx> {
// the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
// (so you can use `AllocMap::unwrap_memory`). // (so you can use `AllocMap::unwrap_memory`).
@ -47,6 +47,27 @@ pub enum ConstValue<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstValue<'_>, 32); static_assert_size!(ConstValue<'_>, 32);
impl From<Scalar> for ConstValue<'tcx> {
fn from(s: Scalar) -> Self {
Self::Scalar(s)
}
}
impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> {
type Lifted = ConstValue<'tcx>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ConstValue<'tcx>> {
Some(match self {
ConstValue::Scalar(s) => ConstValue::Scalar(s),
ConstValue::Slice { data, start, end } => {
ConstValue::Slice { data: tcx.lift(data)?, start, end }
}
ConstValue::ByRef { alloc, offset } => {
ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset }
}
})
}
}
impl<'tcx> ConstValue<'tcx> { impl<'tcx> ConstValue<'tcx> {
#[inline] #[inline]
pub fn try_to_scalar(&self) -> Option<Scalar> { pub fn try_to_scalar(&self) -> Option<Scalar> {
@ -56,20 +77,20 @@ impl<'tcx> ConstValue<'tcx> {
} }
} }
pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
Some(self.try_to_scalar()?.assert_int())
}
pub fn try_to_bits(&self, size: Size) -> Option<u128> { pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok() self.try_to_scalar_int()?.to_bits(size).ok()
} }
pub fn try_to_bool(&self) -> Option<bool> { pub fn try_to_bool(&self) -> Option<bool> {
match self.try_to_bits(Size::from_bytes(1))? { self.try_to_scalar_int()?.try_into().ok()
0 => Some(false),
1 => Some(true),
_ => None,
}
} }
pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> { pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
Some(self.try_to_bits(tcx.data_layout.pointer_size)? as u64) self.try_to_scalar_int()?.try_to_machine_usize(tcx).ok()
} }
pub fn try_to_bits_for_ty( pub fn try_to_bits_for_ty(
@ -503,6 +524,13 @@ impl<Tag> From<Pointer<Tag>> for Scalar<Tag> {
} }
} }
impl<Tag> From<ScalarInt> for Scalar<Tag> {
#[inline(always)]
fn from(ptr: ScalarInt) -> Self {
Scalar::Int(ptr)
}
}
#[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)]
pub enum ScalarMaybeUninit<Tag = ()> { pub enum ScalarMaybeUninit<Tag = ()> {
Scalar(Scalar<Tag>), Scalar(Scalar<Tag>),

View File

@ -11,12 +11,12 @@ use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, UserTypeAnnotationIndex}; use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind}; use rustc_hir::{self, GeneratorKind};
use rustc_target::abi::VariantIdx; use rustc_target::abi::{Size, VariantIdx};
use polonius_engine::Atom; use polonius_engine::Atom;
pub use rustc_ast::Mutability; pub use rustc_ast::Mutability;
@ -30,6 +30,7 @@ use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::asm::InlineAsmRegOrRegClass;
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{self, Debug, Display, Formatter, Write}; use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{ControlFlow, Index, IndexMut}; use std::ops::{ControlFlow, Index, IndexMut};
use std::slice; use std::slice;
@ -2032,7 +2033,7 @@ impl<'tcx> Operand<'tcx> {
Operand::Constant(box Constant { Operand::Constant(box Constant {
span, span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(tcx, ty), literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)),
}) })
} }
@ -2063,7 +2064,7 @@ impl<'tcx> Operand<'tcx> {
Operand::Constant(box Constant { Operand::Constant(box Constant {
span, span,
user_ty: None, user_ty: None,
literal: ty::Const::from_scalar(tcx, val, ty), literal: ConstantKind::Val(val.into(), ty),
}) })
} }
@ -2405,12 +2406,21 @@ pub struct Constant<'tcx> {
/// Needed for NLL to impose user-given type constraints. /// Needed for NLL to impose user-given type constraints.
pub user_ty: Option<UserTypeAnnotationIndex>, pub user_ty: Option<UserTypeAnnotationIndex>,
pub literal: &'tcx ty::Const<'tcx>, pub literal: ConstantKind<'tcx>,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
pub enum ConstantKind<'tcx> {
/// This constant came from the type system
Ty(&'tcx ty::Const<'tcx>),
/// This constant cannot go back into the type system, as it represents
/// something the type system cannot handle (e.g. pointers).
Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
} }
impl Constant<'tcx> { impl Constant<'tcx> {
pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> { pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
match self.literal.val.try_to_scalar() { match self.literal.const_for_ty()?.val.try_to_scalar() {
Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) {
GlobalAlloc::Static(def_id) => { GlobalAlloc::Static(def_id) => {
assert!(!tcx.is_thread_local_static(def_id)); assert!(!tcx.is_thread_local_static(def_id));
@ -2421,6 +2431,94 @@ impl Constant<'tcx> {
_ => None, _ => None,
} }
} }
pub fn ty(&self) -> Ty<'tcx> {
self.literal.ty()
}
}
impl From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> {
fn from(ct: &'tcx ty::Const<'tcx>) -> Self {
Self::Ty(ct)
}
}
impl ConstantKind<'tcx> {
/// Returns `None` if the constant is not trivially safe for use in the type system.
pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> {
match self {
ConstantKind::Ty(c) => Some(c),
ConstantKind::Val(..) => None,
}
}
pub fn ty(&self) -> Ty<'tcx> {
match self {
ConstantKind::Ty(c) => c.ty,
ConstantKind::Val(_, ty) => ty,
}
}
#[inline]
pub fn try_to_value(self) -> Option<interpret::ConstValue<'tcx>> {
match self {
ConstantKind::Ty(c) => c.val.try_to_value(),
ConstantKind::Val(val, _) => Some(val),
}
}
#[inline]
pub fn try_to_scalar(self) -> Option<Scalar> {
self.try_to_value()?.try_to_scalar()
}
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
Some(self.try_to_value()?.try_to_scalar()?.assert_int())
}
#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
}
#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_scalar_int()?.try_into().ok()
}
#[inline]
pub fn try_eval_bits(
&self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
match self {
Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty),
Self::Val(val, t) => {
assert_eq!(*t, ty);
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
val.try_to_bits(size)
}
}
}
#[inline]
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
match self {
Self::Ty(ct) => ct.try_eval_bool(tcx, param_env),
Self::Val(val, _) => val.try_to_bool(),
}
}
#[inline]
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<u64> {
match self {
Self::Ty(ct) => ct.try_eval_usize(tcx, param_env),
Self::Val(val, _) => val.try_to_machine_usize(tcx),
}
}
} }
/// A collection of projections into user types. /// A collection of projections into user types.
@ -2606,11 +2704,14 @@ impl<'tcx> Debug for Constant<'tcx> {
impl<'tcx> Display for Constant<'tcx> { impl<'tcx> Display for Constant<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self.literal.ty.kind() { match self.ty().kind() {
ty::FnDef(..) => {} ty::FnDef(..) => {}
_ => write!(fmt, "const ")?, _ => write!(fmt, "const ")?,
} }
pretty_print_const(self.literal, fmt, true) match self.literal {
ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true),
}
} }
} }
@ -2629,6 +2730,23 @@ fn pretty_print_const(
}) })
} }
fn pretty_print_const_value(
val: interpret::ConstValue<'tcx>,
ty: Ty<'tcx>,
fmt: &mut Formatter<'_>,
print_types: bool,
) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
ty::tls::with(|tcx| {
let val = tcx.lift(val).unwrap();
let ty = tcx.lift(ty).unwrap();
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const_value(val, ty, print_types)?;
Ok(())
})
}
impl<'tcx> graph::DirectedGraph for Body<'tcx> { impl<'tcx> graph::DirectedGraph for Body<'tcx> {
type Node = BasicBlock; type Node = BasicBlock;
} }

View File

@ -227,7 +227,7 @@ impl<'tcx> Operand<'tcx> {
{ {
match self { match self {
&Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty, &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
&Operand::Constant(ref c) => c.literal.ty, &Operand::Constant(ref c) => c.literal.ty(),
} }
} }
} }

View File

@ -342,6 +342,23 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
} }
} }
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.literal.visit_with(visitor) self.literal.visit_with(visitor)?;
self.user_ty.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ConstantKind<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
match self {
ConstantKind::Ty(c) => ConstantKind::Ty(c.fold_with(folder)),
ConstantKind::Val(v, t) => ConstantKind::Val(v, t.fold_with(folder)),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match *self {
ConstantKind::Ty(c) => c.visit_with(visitor),
ConstantKind::Val(_, t) => t.visit_with(visitor),
}
} }
} }

View File

@ -871,7 +871,10 @@ macro_rules! make_mir_visitor {
self.visit_span(span); self.visit_span(span);
drop(user_ty); // no visit method for this drop(user_ty); // no visit method for this
self.visit_const(literal, location); match literal {
ConstantKind::Ty(ct) => self.visit_const(ct, location),
ConstantKind::Val(_, t) => self.visit_ty(t, TyContext::Location(location)),
}
} }
fn super_span(&mut self, _span: & $($mutability)? Span) { fn super_span(&mut self, _span: & $($mutability)? Span) {

View File

@ -785,6 +785,14 @@ rustc_queries! {
cache_on_disk_if { true } cache_on_disk_if { true }
} }
/// Convert an evaluated constant to a type level constant or
/// return `None` if that is not possible.
query const_to_valtree(
key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>>
) -> Option<ty::ValTree<'tcx>> {
desc { "destructure constant" }
}
/// Destructure a constant ADT or array into its variant index and its /// Destructure a constant ADT or array into its variant index and its
/// field values. /// field values.
query destructure_const( query destructure_const(

View File

@ -333,6 +333,16 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> {
} }
} }
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] {
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
Ok(decoder.tcx().arena.alloc_from_iter(
(0..decoder.read_usize()?)
.map(|_| Decodable::decode(decoder))
.collect::<Result<Vec<_>, _>>()?,
))
}
}
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation { impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation {
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?))

View File

@ -10,9 +10,11 @@ use rustc_macros::HashStable;
mod int; mod int;
mod kind; mod kind;
mod valtree;
pub use int::*; pub use int::*;
pub use kind::*; pub use kind::*;
pub use valtree::*;
/// Typed constant value. /// Typed constant value.
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]

View File

@ -5,6 +5,8 @@ use rustc_target::abi::{Size, TargetDataLayout};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use crate::ty::TyCtxt;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
/// A type for representing any integer. Only used for printing. /// A type for representing any integer. Only used for printing.
pub struct ConstInt { pub struct ConstInt {
@ -239,6 +241,11 @@ impl ScalarInt {
Err(self.size()) Err(self.size())
} }
} }
#[inline]
pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
}
} }
macro_rules! from { macro_rules! from {
@ -277,6 +284,18 @@ macro_rules! try_from {
from!(u8, u16, u32, u64, u128, bool); from!(u8, u16, u32, u64, u128, bool);
try_from!(u8, u16, u32, u64, u128); try_from!(u8, u16, u32, u64, u128);
impl TryFrom<ScalarInt> for bool {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
0 => Ok(false),
1 => Ok(true),
_ => Err(Size::from_bytes(1)),
})
}
}
impl From<char> for ScalarInt { impl From<char> for ScalarInt {
#[inline] #[inline]
fn from(c: char) -> Self { fn from(c: char) -> Self {

View File

@ -1,3 +1,5 @@
use std::convert::TryInto;
use crate::mir::interpret::ConstValue; use crate::mir::interpret::ConstValue;
use crate::mir::interpret::Scalar; use crate::mir::interpret::Scalar;
use crate::mir::Promoted; use crate::mir::Promoted;
@ -9,6 +11,8 @@ use rustc_hir::def_id::DefId;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_target::abi::Size; use rustc_target::abi::Size;
use super::ScalarInt;
/// Represents a constant in Rust. /// Represents a constant in Rust.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
#[derive(HashStable)] #[derive(HashStable)]
@ -51,14 +55,19 @@ impl<'tcx> ConstKind<'tcx> {
self.try_to_value()?.try_to_scalar() self.try_to_value()?.try_to_scalar()
} }
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
Some(self.try_to_value()?.try_to_scalar()?.assert_int())
}
#[inline] #[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> { pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_value()?.try_to_bits(size) self.try_to_scalar_int()?.to_bits(size).ok()
} }
#[inline] #[inline]
pub fn try_to_bool(self) -> Option<bool> { pub fn try_to_bool(self) -> Option<bool> {
self.try_to_value()?.try_to_bool() self.try_to_scalar_int()?.try_into().ok()
} }
#[inline] #[inline]

View File

@ -0,0 +1,34 @@
use super::ScalarInt;
use rustc_macros::HashStable;
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
#[derive(HashStable)]
/// This datastructure is used to represent the value of constants used in the type system.
///
/// We explicitly choose a different datastructure from the way values are processed within
/// CTFE, as in the type system equal values (according to their `PartialEq`) must also have
/// equal representation (`==` on the rustc data structure, e.g. `ValTree`) and vice versa.
/// Since CTFE uses `AllocId` to represent pointers, it often happens that two different
/// `AllocId`s point to equal values. So we may end up with different representations for
/// two constants whose value is `&42`. Furthermore any kind of struct that has padding will
/// have arbitrary values within that padding, even if the values of the struct are the same.
///
/// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ValTree`.
pub enum ValTree<'tcx> {
/// ZSTs, integers, `bool`, `char` are represented as scalars.
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
/// of these types have the same representation.
Leaf(ScalarInt),
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
/// listing their fields' values in order.
/// Enums are represented by storing their discriminant as a field, followed by all
/// the fields of the variant.
Branch(&'tcx [ValTree<'tcx>]),
}
impl ValTree<'tcx> {
pub fn zst() -> Self {
Self::Branch(&[])
}
}

View File

@ -95,6 +95,8 @@ pub struct CtxtInterners<'tcx> {
projs: InternedSet<'tcx, List<ProjectionKind>>, projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>, place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
const_: InternedSet<'tcx, Const<'tcx>>, const_: InternedSet<'tcx, Const<'tcx>>,
/// Const allocations.
allocation: InternedSet<'tcx, Allocation>,
} }
impl<'tcx> CtxtInterners<'tcx> { impl<'tcx> CtxtInterners<'tcx> {
@ -112,6 +114,7 @@ impl<'tcx> CtxtInterners<'tcx> {
projs: Default::default(), projs: Default::default(),
place_elems: Default::default(), place_elems: Default::default(),
const_: Default::default(), const_: Default::default(),
allocation: Default::default(),
} }
} }
@ -1041,9 +1044,6 @@ pub struct GlobalCtxt<'tcx> {
/// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes
const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>, const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>,
/// Stores the value of constants (and deduplicates the actual memory)
allocation_interner: ShardedHashMap<&'tcx Allocation, ()>,
/// Stores memory for globals (statics/consts). /// Stores memory for globals (statics/consts).
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
@ -1086,7 +1086,10 @@ impl<'tcx> TyCtxt<'tcx> {
} }
pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation { pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation {
self.allocation_interner.intern(alloc, |alloc| self.arena.alloc(alloc)) self.interners
.allocation
.intern(alloc, |alloc| Interned(self.interners.arena.alloc(alloc)))
.0
} }
/// Allocates a read-only byte or string literal for `mir::interpret`. /// Allocates a read-only byte or string literal for `mir::interpret`.
@ -1205,7 +1208,6 @@ impl<'tcx> TyCtxt<'tcx> {
layout_interner: Default::default(), layout_interner: Default::default(),
stability_interner: Default::default(), stability_interner: Default::default(),
const_stability_interner: Default::default(), const_stability_interner: Default::default(),
allocation_interner: Default::default(),
alloc_map: Lock::new(interpret::AllocMap::new()), alloc_map: Lock::new(interpret::AllocMap::new()),
output_filenames: Arc::new(output_filenames.clone()), output_filenames: Arc::new(output_filenames.clone()),
} }
@ -1641,6 +1643,7 @@ macro_rules! nop_list_lift {
nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {type_; Ty<'a> => Ty<'tcx>}
nop_lift! {region; Region<'a> => Region<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>}
nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>}
nop_lift! {allocation; &'a Allocation => &'tcx Allocation}
nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>}
nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
@ -1931,7 +1934,7 @@ impl<'tcx> TyCtxt<'tcx> {
"Const Stability interner: #{}", "Const Stability interner: #{}",
self.0.const_stability_interner.len() self.0.const_stability_interner.len()
)?; )?;
writeln!(fmt, "Allocation interner: #{}", self.0.allocation_interner.len())?; writeln!(fmt, "Allocation interner: #{}", self.0.interners.allocation.len())?;
writeln!(fmt, "Layout interner: #{}", self.0.layout_interner.len())?; writeln!(fmt, "Layout interner: #{}", self.0.layout_interner.len())?;
Ok(()) Ok(())
@ -2032,6 +2035,26 @@ impl<'tcx> Borrow<Const<'tcx>> for Interned<'tcx, Const<'tcx>> {
} }
} }
impl<'tcx> Borrow<Allocation> for Interned<'tcx, Allocation> {
fn borrow<'a>(&'a self) -> &'a Allocation {
&self.0
}
}
impl<'tcx> PartialEq for Interned<'tcx, Allocation> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'tcx> Eq for Interned<'tcx, Allocation> {}
impl<'tcx> Hash for Interned<'tcx, Allocation> {
fn hash<H: Hasher>(&self, s: &mut H) {
self.0.hash(s)
}
}
macro_rules! direct_interners { macro_rules! direct_interners {
($($name:ident: $method:ident($ty:ty),)+) => { ($($name:ident: $method:ident($ty:ty),)+) => {
$(impl<'tcx> PartialEq for Interned<'tcx, $ty> { $(impl<'tcx> PartialEq for Interned<'tcx, $ty> {

View File

@ -55,7 +55,7 @@ pub use rustc_type_ir::*;
pub use self::binding::BindingMode; pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*; pub use self::binding::BindingMode::*;
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt}; pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, ValTree};
pub use self::context::{ pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt, CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,

View File

@ -956,32 +956,40 @@ pub trait PrettyPrinter<'tcx>:
} }
fn pretty_print_const_scalar( fn pretty_print_const_scalar(
mut self, self,
scalar: Scalar, scalar: Scalar,
ty: Ty<'tcx>, ty: Ty<'tcx>,
print_ty: bool, print_ty: bool,
) -> Result<Self::Const, Self::Error> {
match scalar {
Scalar::Ptr(ptr) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty),
}
}
fn pretty_print_const_scalar_ptr(
mut self,
ptr: Pointer,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> { ) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self); define_scoped_cx!(self);
match (scalar, &ty.kind()) { match ty.kind() {
// Byte strings (&[u8; N]) // Byte strings (&[u8; N])
( ty::Ref(
Scalar::Ptr(ptr), _,
ty::Ref( ty::TyS {
_, kind:
ty::TyS { ty::Array(
kind: ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. },
ty::Array( ty::Const {
ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, val: ty::ConstKind::Value(ConstValue::Scalar(int)), ..
ty::Const { },
val: ty::ConstKind::Value(ConstValue::Scalar(int)), ),
.. ..
}, },
), _,
..
},
_,
),
) => match self.tcx().get_global_alloc(ptr.alloc_id) { ) => match self.tcx().get_global_alloc(ptr.alloc_id) {
Some(GlobalAlloc::Memory(alloc)) => { Some(GlobalAlloc::Memory(alloc)) => {
let bytes = int.assert_bits(self.tcx().data_layout.pointer_size); let bytes = int.assert_bits(self.tcx().data_layout.pointer_size);
@ -997,39 +1005,7 @@ pub trait PrettyPrinter<'tcx>:
Some(GlobalAlloc::Function(_)) => p!("<function>"), Some(GlobalAlloc::Function(_)) => p!("<function>"),
None => p!("<dangling pointer>"), None => p!("<dangling pointer>"),
}, },
// Bool ty::FnPtr(_) => {
(Scalar::Int(int), ty::Bool) if int == ScalarInt::FALSE => p!("false"),
(Scalar::Int(int), ty::Bool) if int == ScalarInt::TRUE => p!("true"),
// Float
(Scalar::Int(int), ty::Float(ty::FloatTy::F32)) => {
p!(write("{}f32", Single::try_from(int).unwrap()))
}
(Scalar::Int(int), ty::Float(ty::FloatTy::F64)) => {
p!(write("{}f64", Double::try_from(int).unwrap()))
}
// Int
(Scalar::Int(int), ty::Uint(_) | ty::Int(_)) => {
let int =
ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral());
if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) }
}
// Char
(Scalar::Int(int), ty::Char) if char::try_from(int).is_ok() => {
p!(write("{:?}", char::try_from(int).unwrap()))
}
// Raw pointers
(Scalar::Int(int), ty::RawPtr(_) | ty::FnPtr(_)) => {
let data = int.assert_bits(self.tcx().data_layout.pointer_size);
self = self.typed_value(
|mut this| {
write!(this, "0x{:x}", data)?;
Ok(this)
},
|this| this.print_type(ty),
" as ",
)?;
}
(Scalar::Ptr(ptr), ty::FnPtr(_)) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings" // FIXME: We should probably have a helper method to share code with the "Byte strings"
// printing above (which also has to handle pointers to all sorts of things). // printing above (which also has to handle pointers to all sorts of things).
match self.tcx().get_global_alloc(ptr.alloc_id) { match self.tcx().get_global_alloc(ptr.alloc_id) {
@ -1043,12 +1019,61 @@ pub trait PrettyPrinter<'tcx>:
_ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?, _ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?,
} }
} }
// Any pointer values not covered by a branch above
_ => {
self = self.pretty_print_const_pointer(ptr, ty, print_ty)?;
}
}
Ok(self)
}
fn pretty_print_const_scalar_int(
mut self,
int: ScalarInt,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);
match ty.kind() {
// Bool
ty::Bool if int == ScalarInt::FALSE => p!("false"),
ty::Bool if int == ScalarInt::TRUE => p!("true"),
// Float
ty::Float(ty::FloatTy::F32) => {
p!(write("{}f32", Single::try_from(int).unwrap()))
}
ty::Float(ty::FloatTy::F64) => {
p!(write("{}f64", Double::try_from(int).unwrap()))
}
// Int
ty::Uint(_) | ty::Int(_) => {
let int =
ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral());
if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) }
}
// Char
ty::Char if char::try_from(int).is_ok() => {
p!(write("{:?}", char::try_from(int).unwrap()))
}
// Raw pointers
ty::RawPtr(_) | ty::FnPtr(_) => {
let data = int.assert_bits(self.tcx().data_layout.pointer_size);
self = self.typed_value(
|mut this| {
write!(this, "0x{:x}", data)?;
Ok(this)
},
|this| this.print_type(ty),
" as ",
)?;
}
// For function type zsts just printing the path is enough // For function type zsts just printing the path is enough
(Scalar::Int(int), ty::FnDef(d, s)) if int == ScalarInt::ZST => { ty::FnDef(d, s) if int == ScalarInt::ZST => {
p!(print_value_path(*d, s)) p!(print_value_path(*d, s))
} }
// Nontrivial types with scalar bit representation // Nontrivial types with scalar bit representation
(Scalar::Int(int), _) => { _ => {
let print = |mut this: Self| { let print = |mut this: Self| {
if int.size() == Size::ZERO { if int.size() == Size::ZERO {
write!(this, "transmute(())")?; write!(this, "transmute(())")?;
@ -1063,10 +1088,6 @@ pub trait PrettyPrinter<'tcx>:
print(self)? print(self)?
}; };
} }
// Any pointer values not covered by a branch above
(Scalar::Ptr(p), _) => {
self = self.pretty_print_const_pointer(p, ty, print_ty)?;
}
} }
Ok(self) Ok(self)
} }

View File

@ -14,8 +14,8 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife
use crate::middle::stability::{self, DeprecationEntry}; use crate::middle::stability::{self, DeprecationEntry};
use crate::mir; use crate::mir;
use crate::mir::interpret::GlobalId; use crate::mir::interpret::GlobalId;
use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput};
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult}; use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit; use crate::mir::mono::CodegenUnit;
use crate::traits::query::{ use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,

View File

@ -4,13 +4,12 @@
//! types or regions but can be other things. Examples of type relations are //! types or regions but can be other things. Examples of type relations are
//! subtyping, type equality, etc. //! subtyping, type equality, etc.
use crate::mir::interpret::{get_slice_bytes, ConstValue}; use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar};
use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::error::{ExpectedFound, TypeError};
use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_hir as ast; use rustc_hir as ast;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_span::DUMMY_SP;
use rustc_target::spec::abi; use rustc_target::spec::abi;
use std::iter; use std::iter;
@ -498,104 +497,41 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
let tcx = relation.tcx(); let tcx = relation.tcx();
let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()).val; // FIXME(oli-obk): once const generics can have generic types, this assertion
// will likely get triggered. Move to `normalize_erasing_regions` at that point.
assert_eq!(
tcx.erase_regions(a.ty),
tcx.erase_regions(b.ty),
"cannot relate constants of different types"
);
// FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`. let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env());
// We could probably always assert it early, as const generic parameters let a = eagerly_eval(a);
// are not allowed to depend on other generic parameters, i.e. are concrete. let b = eagerly_eval(b);
// (although there could be normalization differences)
// Currently, the values that can be unified are primitive types, // Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding // and those that derive both `PartialEq` and `Eq`, corresponding
// to structural-match types. // to structural-match types.
let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) { let is_match = match (a.val, b.val) {
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
// The caller should handle these cases! // The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
} }
(ty::ConstKind::Error(d), _) | (_, ty::ConstKind::Error(d)) => Ok(ty::ConstKind::Error(d)), (ty::ConstKind::Error(_), _) => return Ok(a),
(_, ty::ConstKind::Error(_)) => return Ok(b),
(ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) => a_p.index == b_p.index,
return Ok(a); (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
}
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) if p1 == p2 => {
return Ok(a);
}
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => {
let new_val = match (a_val, b_val) { check_const_value_eq(relation, a_val, b_val, a, b)?
(ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => {
if a_val == b_val {
Ok(ConstValue::Scalar(a_val))
} else if let ty::FnPtr(_) = a.ty.kind() {
let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn();
let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn();
if a_instance == b_instance {
Ok(ConstValue::Scalar(a_val))
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
(ConstValue::Slice { .. }, ConstValue::Slice { .. }) => {
let a_bytes = get_slice_bytes(&tcx, a_val);
let b_bytes = get_slice_bytes(&tcx, b_val);
if a_bytes == b_bytes {
Ok(a_val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
(ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
match a.ty.kind() {
ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
let a_destructured = tcx.destructure_const(relation.param_env().and(a));
let b_destructured = tcx.destructure_const(relation.param_env().and(b));
// Both the variant and each field have to be equal.
if a_destructured.variant == b_destructured.variant {
for (a_field, b_field) in
a_destructured.fields.iter().zip(b_destructured.fields.iter())
{
relation.consts(a_field, b_field)?;
}
Ok(a_val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
// FIXME(const_generics): There are probably some `TyKind`s
// which should be handled here.
_ => {
tcx.sess.delay_span_bug(
DUMMY_SP,
&format!("unexpected consts: a: {:?}, b: {:?}", a, b),
);
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
}
_ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))),
};
new_val.map(ty::ConstKind::Value)
} }
( (
ty::ConstKind::Unevaluated(a_def, a_substs, None), ty::ConstKind::Unevaluated(a_def, a_substs, None),
ty::ConstKind::Unevaluated(b_def, b_substs, None), ty::ConstKind::Unevaluated(b_def, b_substs, None),
) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => { ) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => {
if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) { tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs)))
Ok(a.val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
} }
// While this is slightly incorrect, it shouldn't matter for `min_const_generics` // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
@ -607,11 +543,64 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
) if a_def == b_def && a_promoted == b_promoted => { ) if a_def == b_def && a_promoted == b_promoted => {
let substs = let substs =
relation.relate_with_variance(ty::Variance::Invariant, a_substs, b_substs)?; relation.relate_with_variance(ty::Variance::Invariant, a_substs, b_substs)?;
Ok(ty::ConstKind::Unevaluated(a_def, substs, a_promoted)) return Ok(tcx.mk_const(ty::Const {
val: ty::ConstKind::Unevaluated(a_def, substs, a_promoted),
ty: a.ty,
}));
} }
_ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))), _ => false,
}; };
new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty })) if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) }
}
fn check_const_value_eq<R: TypeRelation<'tcx>>(
relation: &mut R,
a_val: ConstValue<'tcx>,
b_val: ConstValue<'tcx>,
// FIXME(oli-obk): these arguments should go away with valtrees
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
// FIXME(oli-obk): this should just be `bool` with valtrees
) -> RelateResult<'tcx, bool> {
let tcx = relation.tcx();
Ok(match (a_val, b_val) {
(ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => {
a_val == b_val
}
(ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => {
a_val == b_val
|| match (tcx.global_alloc(a_val.alloc_id), tcx.global_alloc(b_val.alloc_id)) {
(GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => {
a_instance == b_instance
}
_ => false,
}
}
(ConstValue::Slice { .. }, ConstValue::Slice { .. }) => {
get_slice_bytes(&tcx, a_val) == get_slice_bytes(&tcx, b_val)
}
(ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
let a_destructured = tcx.destructure_const(relation.param_env().and(a));
let b_destructured = tcx.destructure_const(relation.param_env().and(b));
// Both the variant and each field have to be equal.
if a_destructured.variant == b_destructured.variant {
for (a_field, b_field) in
a_destructured.fields.iter().zip(b_destructured.fields.iter())
{
relation.consts(a_field, b_field)?;
}
true
} else {
false
}
}
_ => false,
})
} }
impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>> { impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>> {

View File

@ -81,12 +81,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let terminator = self.body[location.block].terminator(); let terminator = self.body[location.block].terminator();
debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
if let TerminatorKind::Call { if let TerminatorKind::Call {
func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }), func: Operand::Constant(box Constant { literal, .. }),
args, args,
.. ..
} = &terminator.kind } = &terminator.kind
{ {
if let ty::FnDef(id, _) = *const_ty.kind() { if let ty::FnDef(id, _) = *literal.ty().kind() {
debug!("add_moved_or_invoked_closure_note: id={:?}", id); debug!("add_moved_or_invoked_closure_note: id={:?}", id);
if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() { let closure = match args.first() {

View File

@ -282,7 +282,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location); self.super_constant(constant, location);
let ty = self.sanitize_type(constant, constant.literal.ty); let ty = self.sanitize_type(constant, constant.literal.ty());
self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
let live_region_vid = let live_region_vid =
@ -296,7 +296,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
if let Some(annotation_index) = constant.user_ty { if let Some(annotation_index) = constant.user_ty {
if let Err(terr) = self.cx.relate_type_and_user_type( if let Err(terr) = self.cx.relate_type_and_user_type(
constant.literal.ty, constant.literal.ty(),
ty::Variance::Invariant, ty::Variance::Invariant,
&UserTypeProjection { base: annotation_index, projs: vec![] }, &UserTypeProjection { base: annotation_index, projs: vec![] },
location.to_locations(), location.to_locations(),
@ -308,13 +308,22 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
constant, constant,
"bad constant user type {:?} vs {:?}: {:?}", "bad constant user type {:?} vs {:?}: {:?}",
annotation, annotation,
constant.literal.ty, constant.literal.ty(),
terr, terr,
); );
} }
} else { } else {
let tcx = self.tcx(); let tcx = self.tcx();
if let ty::ConstKind::Unevaluated(def, substs, promoted) = constant.literal.val { let maybe_uneval = match constant.literal {
ConstantKind::Ty(ct) => match ct.val {
ty::ConstKind::Unevaluated(def, substs, promoted) => {
Some((def, substs, promoted))
}
_ => None,
},
_ => None,
};
if let Some((def, substs, promoted)) = maybe_uneval {
if let Some(promoted) = promoted { if let Some(promoted) = promoted {
let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
promoted: &Body<'tcx>, promoted: &Body<'tcx>,
@ -349,7 +358,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
location.to_locations(), location.to_locations(),
ConstraintCategory::Boring, ConstraintCategory::Boring,
self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
constant.literal.ty, constant.literal.ty(),
def.did, def.did,
UserSubsts { substs, user_self_ty: None }, UserSubsts { substs, user_self_ty: None },
)), )),
@ -367,7 +376,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
let unnormalized_ty = tcx.type_of(static_def_id); let unnormalized_ty = tcx.type_of(static_def_id);
let locations = location.to_locations(); let locations = location.to_locations();
let normalized_ty = self.cx.normalize(unnormalized_ty, locations); let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty; let literal_ty = constant.literal.ty().builtin_deref(true).unwrap().ty;
if let Err(terr) = self.cx.eq_types( if let Err(terr) = self.cx.eq_types(
normalized_ty, normalized_ty,
@ -379,7 +388,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
} }
} }
if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() { if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
self.cx.normalize_and_prove_instantiated_predicates( self.cx.normalize_and_prove_instantiated_predicates(
instantiated_predicates, instantiated_predicates,

View File

@ -3,12 +3,15 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use rustc_hir::Mutability; use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
mir::{self, interpret::ConstAlloc},
ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
use crate::interpret::{ use crate::interpret::{
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar, intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
}; };
mod error; mod error;
@ -35,6 +38,100 @@ pub(crate) fn const_caller_location(
ConstValue::Scalar(loc_place.ptr) ConstValue::Scalar(loc_place.ptr)
} }
/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let ecx = mk_eval_cx(
tcx, DUMMY_SP, param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
false,
);
let place = ecx.raw_const_to_mplace(raw).unwrap();
const_to_valtree_inner(&ecx, &place)
}
fn const_to_valtree_inner<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let branches = |n, variant| {
let place = match variant {
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
None => *place,
};
let variant =
variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
let fields = (0..n).map(|i| {
let field = ecx.mplace_field(&place, i).unwrap();
const_to_valtree_inner(ecx, &field)
});
// For enums, we preped their variant index before the variant's fields so we can figure out
// the variant again when just seeing a valtree.
let branches = variant.into_iter().chain(fields);
Some(ty::ValTree::Branch(
ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
))
};
match place.layout.ty.kind() {
ty::FnDef(..) => Some(ty::ValTree::zst()),
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
let val = ecx.read_immediate(&place.into()).unwrap();
let val = val.to_scalar().unwrap();
Some(ty::ValTree::Leaf(val.assert_int()))
}
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
// agree with runtime equality tests.
ty::FnPtr(_) | ty::RawPtr(_) => None,
ty::Ref(..) => unimplemented!("need to use deref_const"),
// Trait objects are not allowed in type level constants, as we have no concept for
// resolving their backing type, even if we can do that at const eval time. We may
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
// but it is unclear if this is useful.
ty::Dynamic(..) => None,
ty::Slice(_) | ty::Str => {
unimplemented!("need to find the backing data of the slice/str and recurse on that")
}
ty::Tuple(substs) => branches(substs.len(), None),
ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),
ty::Adt(def, _) => {
if def.variants.is_empty() {
bug!("uninhabited types should have errored and never gotten converted to valtree")
}
let variant = ecx.read_discriminant(&place.into()).unwrap().1;
branches(def.variants[variant].fields.len(), Some(variant))
}
ty::Never
| ty::Error(_)
| ty::Foreign(..)
| ty::Infer(ty::FreshIntTy(_))
| ty::Infer(ty::FreshFloatTy(_))
| ty::Projection(..)
| ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(..)
// FIXME(oli-obk): we could look behind opaque types
| ty::Opaque(..)
| ty::Infer(_)
// FIXME(oli-obk): we can probably encode closures just like structs
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..) => None,
}
}
/// This function uses `unwrap` copiously, because an already validated constant /// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is /// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g. /// invoked from the pretty printer, where it can receive enums with no variants and e.g.

View File

@ -689,7 +689,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let span = const_.span; let span = const_.span;
let const_ = let const_ =
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal); self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
self.const_to_op(const_, None).map_err(|err| { self.mir_const_to_op(&const_, None).map_err(|err| {
// If there was an error, set the span of the current frame to this constant. // If there was an error, set the span of the current frame to this constant.
// Avoiding doing this when evaluation succeeds. // Avoiding doing this when evaluation succeeds.
self.frame_mut().loc = Err(span); self.frame_mut().loc = Err(span);

View File

@ -532,7 +532,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
// checked yet. // checked yet.
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
self.const_to_op(val, layout)?
self.mir_const_to_op(&val, layout)?
} }
}; };
trace!("{:?}: {:?}", mir_op, *op); trace!("{:?}: {:?}", mir_op, *op);
@ -556,28 +557,45 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
val: &ty::Const<'tcx>, val: &ty::Const<'tcx>,
layout: Option<TyAndLayout<'tcx>>, layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
match val.val {
ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
ty::ConstKind::Unevaluated(def, substs, promoted) => {
let instance = self.resolve(def, substs)?;
Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into())
}
ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
}
ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout),
}
}
crate fn mir_const_to_op(
&self,
val: &mir::ConstantKind<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
match val {
mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout),
mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, None),
}
}
crate fn const_val_to_op(
&self,
val_val: ConstValue<'tcx>,
ty: Ty<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// Other cases need layout.
let tag_scalar = |scalar| -> InterpResult<'tcx, _> { let tag_scalar = |scalar| -> InterpResult<'tcx, _> {
Ok(match scalar { Ok(match scalar {
Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?),
Scalar::Int(int) => Scalar::Int(int), Scalar::Int(int) => Scalar::Int(int),
}) })
}; };
// Early-return cases. let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let val_val = match val.val {
ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
ty::ConstKind::Unevaluated(def, substs, promoted) => {
let instance = self.resolve(def, substs)?;
return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
}
ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
}
ty::ConstKind::Value(val_val) => val_val,
};
// Other cases need layout.
let layout =
from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?;
let op = match val_val { let op = match val_val {
ConstValue::ByRef { alloc, offset } => { ConstValue::ByRef { alloc, offset } => {
let id = self.tcx.create_memory_alloc(alloc); let id = self.tcx.create_memory_alloc(alloc);

View File

@ -531,7 +531,7 @@ where
base.offset(from_offset, meta, layout, self) base.offset(from_offset, meta, layout, self)
} }
pub(super) fn mplace_downcast( pub(crate) fn mplace_downcast(
&self, &self,
base: &MPlaceTy<'tcx, M::PointerTag>, base: &MPlaceTy<'tcx, M::PointerTag>,
variant: VariantIdx, variant: VariantIdx,

View File

@ -63,6 +63,10 @@ pub fn provide(providers: &mut Providers) {
let (param_env, value) = param_env_and_value.into_parts(); let (param_env, value) = param_env_and_value.into_parts();
const_eval::destructure_const(tcx, param_env, value) const_eval::destructure_const(tcx, param_env, value)
}; };
providers.const_to_valtree = |tcx, param_env_and_value| {
let (param_env, raw) = param_env_and_value.into_parts();
const_eval::const_to_valtree(tcx, param_env, raw)
};
providers.deref_const = |tcx, param_env_and_value| { providers.deref_const = |tcx, param_env_and_value| {
let (param_env, value) = param_env_and_value.into_parts(); let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_const(tcx, param_env, value) const_eval::deref_const(tcx, param_env, value)

View File

@ -684,7 +684,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
for op in operands { for op in operands {
match *op { match *op {
mir::InlineAsmOperand::SymFn { ref value } => { mir::InlineAsmOperand::SymFn { ref value } => {
let fn_ty = self.monomorphize(value.literal.ty); let fn_ty = self.monomorphize(value.literal.ty());
visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output);
} }
mir::InlineAsmOperand::SymStatic { def_id } => { mir::InlineAsmOperand::SymStatic { def_id } => {

View File

@ -421,7 +421,7 @@ impl CloneShimBuilder<'tcx> {
let func = Operand::Constant(box Constant { let func = Operand::Constant(box Constant {
span: self.span, span: self.span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(tcx, func_ty), literal: ty::Const::zero_sized(tcx, func_ty).into(),
}); });
let ref_loc = self.make_place( let ref_loc = self.make_place(
@ -478,7 +478,7 @@ impl CloneShimBuilder<'tcx> {
box Constant { box Constant {
span: self.span, span: self.span,
user_ty: None, user_ty: None,
literal: ty::Const::from_usize(self.tcx, value), literal: ty::Const::from_usize(self.tcx, value).into(),
} }
} }
@ -509,7 +509,7 @@ impl CloneShimBuilder<'tcx> {
Rvalue::Use(Operand::Constant(box Constant { Rvalue::Use(Operand::Constant(box Constant {
span: self.span, span: self.span,
user_ty: None, user_ty: None,
literal: len, literal: len.into(),
})), })),
))), ))),
]; ];
@ -768,7 +768,7 @@ fn build_call_shim<'tcx>(
Operand::Constant(box Constant { Operand::Constant(box Constant {
span, span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(tcx, ty), literal: ty::Const::zero_sized(tcx, ty).into(),
}), }),
rcvr.into_iter().collect::<Vec<_>>(), rcvr.into_iter().collect::<Vec<_>>(),
) )

View File

@ -246,25 +246,27 @@ where
}; };
// Check the qualifs of the value of `const` items. // Check the qualifs of the value of `const` items.
if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val { if let Some(ct) = constant.literal.const_for_ty() {
assert!(promoted.is_none()); if let ty::ConstKind::Unevaluated(def, _, promoted) = ct.val {
// Don't peek inside trait associated constants. assert!(promoted.is_none());
if cx.tcx.trait_of_item(def.did).is_none() { // Don't peek inside trait associated constants.
let qualifs = if let Some((did, param_did)) = def.as_const_arg() { if cx.tcx.trait_of_item(def.did).is_none() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
} else { cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
cx.tcx.at(constant.span).mir_const_qualif(def.did) } else {
}; cx.tcx.at(constant.span).mir_const_qualif(def.did)
};
if !Q::in_qualifs(&qualifs) { if !Q::in_qualifs(&qualifs) {
return false; return false;
}
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
} }
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
} }
} }
// Otherwise use the qualifs of the type. // Otherwise use the qualifs of the type.
Q::in_any_value_of_ty(cx, constant.literal.ty) Q::in_any_value_of_ty(cx, constant.literal.ty())
} }

View File

@ -13,9 +13,9 @@ use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
}; };
use rustc_middle::mir::{ use rustc_middle::mir::{
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind, AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
}; };
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::subst::{InternalSubsts, Subst};
@ -482,18 +482,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None; return None;
} }
match self.ecx.const_to_op(c.literal, None) { match self.ecx.mir_const_to_op(&c.literal, None) {
Ok(op) => Some(op), Ok(op) => Some(op),
Err(error) => { Err(error) => {
let tcx = self.ecx.tcx.at(c.span); let tcx = self.ecx.tcx.at(c.span);
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
if let Some(lint_root) = self.lint_root(source_info) { if let Some(lint_root) = self.lint_root(source_info) {
let lint_only = match c.literal.val { let lint_only = match c.literal {
// Promoteds must lint and not error as the user didn't ask for them ConstantKind::Ty(ct) => match ct.val {
ConstKind::Unevaluated(_, _, Some(_)) => true, // Promoteds must lint and not error as the user didn't ask for them
// Out of backwards compatibility we cannot report hard errors in unused ConstKind::Unevaluated(_, _, Some(_)) => true,
// generic functions using associated constants of the generic parameters. // Out of backwards compatibility we cannot report hard errors in unused
_ => c.literal.needs_subst(), // generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
},
ConstantKind::Val(_, ty) => ty.needs_subst(),
}; };
if lint_only { if lint_only {
// Out of backwards compatibility we cannot report hard errors in unused // Out of backwards compatibility we cannot report hard errors in unused
@ -803,7 +806,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
Operand::Constant(Box::new(Constant { Operand::Constant(Box::new(Constant {
span, span,
user_ty: None, user_ty: None,
literal: ty::Const::from_scalar(self.tcx, scalar, ty), literal: ty::Const::from_scalar(self.tcx, scalar, ty).into(),
})) }))
} }
@ -814,9 +817,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
source_info: SourceInfo, source_info: SourceInfo,
) { ) {
if let Rvalue::Use(Operand::Constant(c)) = rval { if let Rvalue::Use(Operand::Constant(c)) = rval {
if !matches!(c.literal.val, ConstKind::Unevaluated(..)) { match c.literal {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {}
return; _ => {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
}
} }
} }
@ -883,13 +889,17 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant { *rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
span: source_info.span, span: source_info.span,
user_ty: None, user_ty: None,
literal: self.ecx.tcx.mk_const(ty::Const { literal: self
ty, .ecx
val: ty::ConstKind::Value(ConstValue::ByRef { .tcx
alloc, .mk_const(ty::Const {
offset: Size::ZERO, ty,
}), val: ty::ConstKind::Value(ConstValue::ByRef {
}), alloc,
offset: Size::ZERO,
}),
})
.into(),
}))); })));
} }
} }

View File

@ -471,7 +471,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
Rvalue::Use(Operand::Constant(Box::new(Constant { Rvalue::Use(Operand::Constant(Box::new(Constant {
span, span,
user_ty: None, user_ty: None,
literal: ty::Const::from_bool(self.tcx, val), literal: ty::Const::from_bool(self.tcx, val).into(),
}))) })))
} }

View File

@ -989,7 +989,7 @@ fn insert_panic_block<'tcx>(
cond: Operand::Constant(box Constant { cond: Operand::Constant(box Constant {
span: body.span, span: body.span,
user_ty: None, user_ty: None,
literal: ty::Const::from_bool(tcx, false), literal: ty::Const::from_bool(tcx, false).into(),
}), }),
expected: true, expected: true,
msg: message, msg: message,

View File

@ -416,7 +416,7 @@ impl Inliner<'tcx> {
TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => {
if let ty::FnDef(def_id, substs) = if let ty::FnDef(def_id, substs) =
*callsite.callee.subst_mir(self.tcx, &f.literal.ty).kind() *callsite.callee.subst_mir(self.tcx, &f.literal.ty()).kind()
{ {
let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); let substs = self.tcx.normalize_erasing_regions(self.param_env, substs);
if let Ok(Some(instance)) = if let Ok(Some(instance)) =
@ -637,8 +637,11 @@ impl Inliner<'tcx> {
// `required_consts`, here we may not only have `ConstKind::Unevaluated` // `required_consts`, here we may not only have `ConstKind::Unevaluated`
// because we are calling `subst_and_normalize_erasing_regions`. // because we are calling `subst_and_normalize_erasing_regions`.
caller_body.required_consts.extend( caller_body.required_consts.extend(
callee_body.required_consts.iter().copied().filter(|&constant| { callee_body.required_consts.iter().copied().filter(|&ct| {
matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _)) match ct.literal.const_for_ty() {
Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_, _, _)),
None => true,
}
}), }),
); );
} }

View File

@ -79,7 +79,7 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> { fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> {
let a = a.constant()?; let a = a.constant()?;
if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None } if a.literal.ty().is_bool() { a.literal.try_to_bool() } else { None }
} }
/// Transform "&(*a)" ==> "a". /// Transform "&(*a)" ==> "a".
@ -110,12 +110,13 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Len(ref place) = *rvalue { if let Rvalue::Len(ref place) = *rvalue {
let place_ty = place.ty(self.local_decls, self.tcx).ty; let place_ty = place.ty(self.local_decls, self.tcx).ty;
if let ty::Array(_, len) = place_ty.kind() { if let ty::Array(_, len) = *place_ty.kind() {
if !self.should_combine(source_info, rvalue) { if !self.should_combine(source_info, rvalue) {
return; return;
} }
let constant = Constant { span: source_info.span, literal: len, user_ty: None }; let constant =
Constant { span: source_info.span, literal: len.into(), user_ty: None };
*rvalue = Rvalue::Use(Operand::Constant(box constant)); *rvalue = Rvalue::Use(Operand::Constant(box constant));
} }
} }

View File

@ -33,7 +33,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
Rvalue::Use(Operand::Constant(box Constant { Rvalue::Use(Operand::Constant(box Constant {
span: terminator.source_info.span, span: terminator.source_info.span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit), literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
})), })),
)), )),
}); });

View File

@ -93,8 +93,8 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))),
StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))),
) if lhs_f == lhs_s ) if lhs_f == lhs_s
&& f_c.literal.ty.is_bool() && f_c.literal.ty().is_bool()
&& s_c.literal.ty.is_bool() && s_c.literal.ty().is_bool()
&& f_c.literal.try_eval_bool(tcx, param_env).is_some() && f_c.literal.try_eval_bool(tcx, param_env).is_some()
&& s_c.literal.try_eval_bool(tcx, param_env).is_some() => {} && s_c.literal.try_eval_bool(tcx, param_env).is_some() => {}

View File

@ -921,7 +921,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let unit = Rvalue::Use(Operand::Constant(box Constant { let unit = Rvalue::Use(Operand::Constant(box Constant {
span: statement.source_info.span, span: statement.source_info.span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit), literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit).into(),
})); }));
mem::replace(rhs, unit) mem::replace(rhs, unit)
}, },
@ -998,20 +998,22 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
Operand::Constant(Box::new(Constant { Operand::Constant(Box::new(Constant {
span, span,
user_ty: None, user_ty: None,
literal: tcx.mk_const(ty::Const { literal: tcx
ty, .mk_const(ty::Const {
val: ty::ConstKind::Unevaluated( ty,
def, val: ty::ConstKind::Unevaluated(
InternalSubsts::for_item(tcx, def.did, |param, _| { def,
if let ty::GenericParamDefKind::Lifetime = param.kind { InternalSubsts::for_item(tcx, def.did, |param, _| {
tcx.lifetimes.re_erased.into() if let ty::GenericParamDefKind::Lifetime = param.kind {
} else { tcx.lifetimes.re_erased.into()
tcx.mk_param_from_def(param) } else {
} tcx.mk_param_from_def(param)
}), }
Some(promoted_id), }),
), Some(promoted_id),
}), ),
})
.into(),
})) }))
}; };
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
@ -1250,8 +1252,8 @@ crate fn is_const_fn_in_array_repeat_expression<'tcx>(
if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) = if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) =
&block.terminator &block.terminator
{ {
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, _) = *ty.kind() { if let ty::FnDef(def_id, _) = *literal.ty().kind() {
if let Some((destination_place, _)) = destination { if let Some((destination_place, _)) = destination {
if destination_place == place { if destination_place == place {
if is_const_fn(ccx.tcx, def_id) { if is_const_fn(ccx.tcx, def_id) {

View File

@ -14,10 +14,10 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) { fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
let const_kind = constant.literal.val; if let Some(ct) = constant.literal.const_for_ty() {
if let ConstKind::Unevaluated(_, _, _) = ct.val {
if let ConstKind::Unevaluated(_, _, _) = const_kind { self.required_consts.push(*constant);
self.required_consts.push(*constant); }
} }
} }
} }

View File

@ -205,7 +205,7 @@ impl PeekCall {
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
&terminator.kind &terminator.kind
{ {
if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() { if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() {
let sig = tcx.fn_sig(def_id); let sig = tcx.fn_sig(def_id);
let name = tcx.item_name(def_id); let name = tcx.item_name(def_id);
if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek { if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {

View File

@ -205,12 +205,12 @@ fn find_branch_value_info<'tcx>(
match (left, right) { match (left, right) {
(Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on)) (Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on))
| (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => { | (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => {
let branch_value_ty = branch_value.literal.ty; let branch_value_ty = branch_value.literal.ty();
// we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats // we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats
if !branch_value_ty.is_integral() && !branch_value_ty.is_char() { if !branch_value_ty.is_integral() && !branch_value_ty.is_char() {
return None; return None;
}; };
let branch_value_scalar = branch_value.literal.val.try_to_scalar()?; let branch_value_scalar = branch_value.literal.try_to_scalar()?;
Some((branch_value_scalar, branch_value_ty, *to_switch_on)) Some((branch_value_scalar, branch_value_ty, *to_switch_on))
} }
_ => None, _ => None,

View File

@ -1035,7 +1035,7 @@ where
Operand::Constant(box Constant { Operand::Constant(box Constant {
span: self.source_info.span, span: self.source_info.span,
user_ty: None, user_ty: None,
literal: ty::Const::from_usize(self.tcx(), val.into()), literal: ty::Const::from_usize(self.tcx(), val.into()).into(),
}) })
} }

View File

@ -17,8 +17,8 @@ pub fn find_self_call<'tcx>(
&body[block].terminator &body[block].terminator
{ {
debug!("find_self_call: func={:?}", func); debug!("find_self_call: func={:?}", func);
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, substs) = *ty.kind() { if let ty::FnDef(def_id, substs) = *literal.ty().kind() {
if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
tcx.opt_associated_item(def_id) tcx.opt_associated_item(def_id)
{ {

View File

@ -439,7 +439,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location); self.super_constant(constant, location);
let Constant { span, user_ty, literal } = constant; let Constant { span, user_ty, literal } = constant;
match literal.ty.kind() { match literal.ty().kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {}
// Unit type // Unit type
ty::Tuple(tys) if tys.is_empty() => {} ty::Tuple(tys) if tys.is_empty() => {}
@ -449,7 +449,12 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
if let Some(user_ty) = user_ty { if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {:?}", user_ty)); self.push(&format!("+ user_ty: {:?}", user_ty));
} }
self.push(&format!("+ literal: {:?}", literal)); match literal {
ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
ConstantKind::Val(val, ty) => {
self.push(&format!("+ literal: {:?}, {}", val, ty))
}
}
} }
} }
} }

View File

@ -68,7 +68,7 @@ impl<'tcx> CFG<'tcx> {
Rvalue::Use(Operand::Constant(box Constant { Rvalue::Use(Operand::Constant(box Constant {
span: source_info.span, span: source_info.span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit), literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
})), })),
); );
} }

View File

@ -11,7 +11,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> { crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> {
let this = self; let this = self;
let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
match kind { match *kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value), ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
ExprKind::Literal { literal, user_ty, const_id: _ } => { ExprKind::Literal { literal, user_ty, const_id: _ } => {
let user_ty = user_ty.map(|user_ty| { let user_ty = user_ty.map(|user_ty| {
@ -22,11 +22,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}) })
}); });
assert_eq!(literal.ty, ty); assert_eq!(literal.ty, ty);
Constant { span, user_ty, literal } Constant { span, user_ty, literal: literal.into() }
}
ExprKind::StaticRef { literal, .. } => {
Constant { span, user_ty: None, literal: literal.into() }
} }
ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal },
ExprKind::ConstBlock { value } => { ExprKind::ConstBlock { value } => {
Constant { span: span, user_ty: None, literal: value } Constant { span: span, user_ty: None, literal: value.into() }
} }
_ => span_bug!(span, "expression is not a valid constant {:?}", kind), _ => span_bug!(span, "expression is not a valid constant {:?}", kind),
} }

View File

@ -255,7 +255,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::Use(Operand::Constant(box Constant { block.and(Rvalue::Use(Operand::Constant(box Constant {
span: expr_span, span: expr_span,
user_ty: None, user_ty: None,
literal: ty::Const::zero_sized(this.tcx, this.tcx.types.unit), literal: ty::Const::zero_sized(this.tcx, this.tcx.types.unit).into(),
}))) })))
} }
ExprKind::Yield { .. } ExprKind::Yield { .. }

View File

@ -146,7 +146,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant { Constant {
span: expr_span, span: expr_span,
user_ty: None, user_ty: None,
literal: ty::Const::from_bool(this.tcx, true), literal: ty::Const::from_bool(this.tcx, true).into(),
}, },
); );
@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant { Constant {
span: expr_span, span: expr_span,
user_ty: None, user_ty: None,
literal: ty::Const::from_bool(this.tcx, false), literal: ty::Const::from_bool(this.tcx, false).into(),
}, },
); );

View File

@ -438,7 +438,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Need to experiment. // Need to experiment.
user_ty: None, user_ty: None,
literal: method, literal: method.into(),
}), }),
args: vec![val, expect], args: vec![val, expect],
destination: Some((eq_result, eq_block)), destination: Some((eq_result, eq_block)),

View File

@ -30,6 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span: Span, span: Span,
literal: &'tcx ty::Const<'tcx>, literal: &'tcx ty::Const<'tcx>,
) -> Operand<'tcx> { ) -> Operand<'tcx> {
let literal = literal.into();
let constant = box Constant { span, user_ty: None, literal }; let constant = box Constant { span, user_ty: None, literal };
Operand::Constant(constant) Operand::Constant(constant)
} }
@ -57,7 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant { Constant {
span: source_info.span, span: source_info.span,
user_ty: None, user_ty: None,
literal: ty::Const::from_usize(self.tcx, value), literal: ty::Const::from_usize(self.tcx, value).into(),
}, },
); );
temp temp

View File

@ -68,11 +68,11 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
// FIXME(#31407) this is only necessary because float parsing is buggy // FIXME(#31407) this is only necessary because float parsing is buggy
self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)"); self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)");
// create a dummy value and continue compiling // create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty)) self.tcx.const_error(ty)
} }
Err(LitToConstError::Reported) => { Err(LitToConstError::Reported) => {
// create a dummy value and continue compiling // create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty)) self.tcx.const_error(ty)
} }
Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"),
} }

View File

@ -228,6 +228,15 @@ impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
} }
} }
impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
fn query_crate(&self) -> CrateNum {
LOCAL_CRATE
}
fn default_span(&self, _: TyCtxt<'_>) -> Span {
DUMMY_SP
}
}
impl<'tcx> Key for ty::PolyTraitRef<'tcx> { impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
fn query_crate(&self) -> CrateNum { fn query_crate(&self) -> CrateNum {
self.def_id().krate self.def_id().krate

View File

@ -377,7 +377,10 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
let local = self.place_to_local(span, p)?; let local = self.place_to_local(span, p)?;
Ok(self.locals[local]) Ok(self.locals[local])
} }
mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)), mir::Operand::Constant(ct) => match ct.literal {
mir::ConstantKind::Ty(ct) => Ok(self.add_node(Node::Leaf(ct), span)),
mir::ConstantKind::Val(..) => self.error(Some(span), "unsupported constant")?,
},
} }
} }