Change const eval to return ConstValue, instead of Const as the type inside it shouldn't be used.

This commit is contained in:
Ben Lewis 2020-02-15 11:56:23 +13:00
parent e168dcd254
commit c423a8649c
20 changed files with 145 additions and 80 deletions

View File

@ -2,6 +2,7 @@ use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
use crate::hir::map::definitions::DefPathData;
use crate::mir;
use crate::mir::interpret::ConstValue;
use crate::ty::layout::{Align, LayoutError, Size};
use crate::ty::query::TyCtxtAt;
use crate::ty::{self, layout, Ty};
@ -40,7 +41,7 @@ CloneTypeFoldableImpls! {
}
pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
#[derive(Debug)]
pub struct ConstEvalErr<'tcx> {

View File

@ -7,7 +7,7 @@ use std::fmt;
use crate::ty::{
layout::{HasDataLayout, Size},
Ty,
ParamEnv, Ty, TyCtxt,
};
use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
@ -66,6 +66,32 @@ impl<'tcx> ConstValue<'tcx> {
ConstValue::Scalar(val) => Some(val),
}
}
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok()
}
pub fn try_to_bits_for_ty(
&self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
self.try_to_bits(size)
}
pub fn from_bool(b: bool) -> Self {
ConstValue::Scalar(Scalar::from_bool(b))
}
pub fn from_u64(i: u64) -> Self {
ConstValue::Scalar(Scalar::from_u64(i))
}
pub fn from_machine_usize(cx: &impl HasDataLayout, i: u64) -> Self {
ConstValue::Scalar(Scalar::from_machine_usize(cx, i))
}
}
/// A `Scalar` represents an immediate, primitive value existing outside of a
@ -287,6 +313,11 @@ impl<'tcx, Tag> Scalar<Tag> {
Scalar::Raw { data: i as u128, size: 8 }
}
#[inline]
pub fn from_machine_usize(cx: &impl HasDataLayout, i: u64) -> Self {
Self::from_uint(i, cx.data_layout().pointer_size)
}
#[inline]
pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
let i = i.into();

View File

@ -2380,10 +2380,10 @@ impl<'tcx> AdtDef {
let repr_type = self.repr.discr_type();
match tcx.const_eval_poly(expr_did) {
Ok(val) => {
// FIXME: Find the right type and use it instead of `val.ty` here
if let Some(b) = val.try_eval_bits(tcx, param_env, val.ty) {
let ty = repr_type.to_ty(tcx);
if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) {
trace!("discriminants: {} ({:?})", b, repr_type);
Some(Discr { val: b, ty: val.ty })
Some(Discr { val: b, ty })
} else {
info!("invalid enum discriminant: {:#?}", val);
crate::mir::interpret::struct_error(

View File

@ -2471,7 +2471,9 @@ impl<'tcx> Const<'tcx> {
// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
tcx.const_eval_resolve(param_env, did, substs, promoted, None).ok()
tcx.const_eval_resolve(param_env, did, substs, promoted, None)
.ok()
.map(|val| tcx.mk_const(Const { val: ConstKind::Value(val), ty: self.ty }))
};
match self.val {

View File

@ -78,11 +78,9 @@ pub fn codegen_static_initializer(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> {
let static_ = cx.tcx.const_eval_poly(def_id)?;
let alloc = match static_.val {
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
_ => bug!("static const eval returned {:#?}", static_),
let alloc = match cx.tcx.const_eval_poly(def_id)? {
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
val => bug!("static const eval returned {:#?}", val),
};
Ok((const_alloc_to_llvm(cx, alloc), alloc))
}

View File

@ -193,7 +193,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
.tcx
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
.unwrap();
OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self)
let const_ = ty::Const { val: ty::ConstKind::Value(ty_name), ty: ret_ty };
OperandRef::from_const(self, &const_).immediate_or_packed_pair(self)
}
"init" => {
let ty = substs.type_at(0);

View File

@ -30,7 +30,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
_ => {
let val = self.eval_mir_constant(constant)?;
Ok(OperandRef::from_const(bx, val))
Ok(OperandRef::from_const(bx, &val))
}
}
}
@ -45,6 +45,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.cx
.tcx()
.const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, promoted, None)
.map(|val| {
self.cx.tcx().mk_const(ty::Const {
val: ty::ConstKind::Value(val),
ty: constant.literal.ty,
})
})
.map_err(|err| {
if promoted.is_none() {
self.cx

View File

@ -66,7 +66,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
val: &'tcx ty::Const<'tcx>,
val: &ty::Const<'tcx>,
) -> Self {
let layout = bx.layout_of(val.ty);

View File

@ -40,7 +40,8 @@ pub(crate) fn const_field<'tcx>(
let field = ecx.operand_field(down, field.index() as u64).unwrap();
// and finally move back to the const world, always normalizing because
// this is not called for statics.
op_to_const(&ecx, field)
let val = op_to_const(&ecx, field);
tcx.mk_const(ty::Const { val: ty::ConstKind::Value(val), ty: op.layout.ty })
}
pub(crate) fn const_caller_location<'tcx>(
@ -84,7 +85,8 @@ pub(crate) fn destructure_const<'tcx>(
let down = ecx.operand_downcast(op, variant).unwrap();
let fields_iter = (0..field_count).map(|i| {
let field_op = ecx.operand_field(down, i).unwrap();
op_to_const(&ecx, field_op)
let val = op_to_const(&ecx, field_op);
tcx.mk_const(ty::Const { val: ty::ConstKind::Value(val), ty: field_op.layout.ty })
});
let fields = tcx.arena.alloc_from_iter(fields_iter);

View File

@ -97,7 +97,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
pub(super) fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, 'tcx>,
op: OpTy<'tcx>,
) -> &'tcx ty::Const<'tcx> {
) -> ConstValue<'tcx> {
// We do not have value optimizations for everything.
// Only scalars and slices, since they are very common.
// Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
@ -144,7 +144,7 @@ pub(super) fn op_to_const<'tcx>(
ConstValue::Scalar(Scalar::zst())
}
};
let val = match immediate {
match immediate {
Ok(mplace) => to_const_value(mplace),
// see comment on `let try_as_immediate` above
Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
@ -166,8 +166,7 @@ pub(super) fn op_to_const<'tcx>(
let len: usize = len.try_into().unwrap();
ConstValue::Slice { data, start, end: start + len }
}
};
ecx.tcx.mk_const(ty::Const { val: ty::ConstKind::Value(val), ty: op.layout.ty })
}
}
fn validate_and_turn_into_const<'tcx>(
@ -195,13 +194,10 @@ fn validate_and_turn_into_const<'tcx>(
// whether they become immediates.
if is_static || cid.promoted.is_some() {
let ptr = mplace.ptr.assert_ptr();
Ok(tcx.mk_const(ty::Const {
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
}),
ty: mplace.layout.ty,
}))
Ok(ConstValue::ByRef {
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
offset: ptr.offset,
})
} else {
Ok(op_to_const(&ecx, mplace.into()))
}

View File

@ -756,6 +756,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn const_eval(
&self,
gid: GlobalId<'tcx>,
ty: Ty<'tcx>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
// and thus don't care about the parameter environment. While we could just use
@ -777,7 +778,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
// return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call
// `ecx.const_eval`.
self.eval_const_to_op(val, None)
let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
self.eval_const_to_op(&const_, None)
}
pub fn const_eval_raw(

View File

@ -48,22 +48,15 @@ crate fn eval_nullary_intrinsic<'tcx>(
param_env: ty::ParamEnv<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> {
) -> InterpResult<'tcx, ConstValue<'tcx>> {
let tp_ty = substs.type_at(0);
let name = tcx.item_name(def_id);
Ok(match name {
sym::type_name => {
let alloc = type_name::alloc_type_name(tcx, tp_ty);
tcx.mk_const(ty::Const {
val: ty::ConstKind::Value(ConstValue::Slice {
data: alloc,
start: 0,
end: alloc.len(),
}),
ty: tcx.mk_static_str(),
})
ConstValue::Slice { data: alloc, start: 0, end: alloc.len() }
}
sym::needs_drop => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)),
sym::needs_drop => ConstValue::from_bool(tp_ty.needs_drop(tcx, param_env)),
sym::size_of | sym::min_align_of | sym::pref_align_of => {
let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
let n = match name {
@ -72,11 +65,9 @@ crate fn eval_nullary_intrinsic<'tcx>(
sym::size_of => layout.size.bytes(),
_ => bug!(),
};
ty::Const::from_usize(tcx, n)
}
sym::type_id => {
ty::Const::from_bits(tcx, tcx.type_id_hash(tp_ty).into(), param_env.and(tcx.types.u64))
ConstValue::from_machine_usize(&tcx, n)
}
sym::type_id => ConstValue::from_u64(tcx.type_id_hash(tp_ty).into()),
other => bug!("`{}` is not a zero arg intrinsic", other),
})
}
@ -119,7 +110,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| sym::type_id
| sym::type_name => {
let gid = GlobalId { instance, promoted: None };
let val = self.const_eval(gid)?;
let ty = instance.ty_env(*self.tcx, self.param_env);
let val = self.const_eval(gid, ty)?;
self.copy_op(val, dest)?;
}

View File

@ -518,7 +518,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// "universe" (param_env).
crate fn eval_const_to_op(
&self,
val: &'tcx ty::Const<'tcx>,
val: &ty::Const<'tcx>,
layout: Option<TyLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
let tag_scalar = |scalar| match scalar {
@ -536,7 +536,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// potentially requiring the current static to be evaluated again. This is not a
// problem here, because we are building an operand which means an actual read is
// happening.
return Ok(OpTy::from(self.const_eval(GlobalId { instance, promoted })?));
return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?);
}
ty::ConstKind::Infer(..)
| ty::ConstKind::Bound(..)

View File

@ -357,7 +357,7 @@ fn collect_items_rec<'tcx>(
recursion_depth_reset = None;
if let Ok(val) = tcx.const_eval_poly(def_id) {
collect_const(tcx, val, InternalSubsts::empty(), &mut neighbors);
collect_const_value(tcx, val, &mut neighbors);
}
}
MonoItem::Fn(instance) => {
@ -971,7 +971,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
let def_id = self.tcx.hir().local_def_id(item.hir_id);
if let Ok(val) = self.tcx.const_eval_poly(def_id) {
collect_const(self.tcx, val, InternalSubsts::empty(), &mut self.output);
collect_const_value(self.tcx, val, &mut self.output);
}
}
hir::ItemKind::Fn(..) => {
@ -1185,18 +1185,10 @@ fn collect_const<'tcx>(
tcx.subst_and_normalize_erasing_regions(param_substs, param_env, &constant);
match substituted_constant.val {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Ptr(ptr))) => {
collect_miri(tcx, ptr.alloc_id, output)
}
ty::ConstKind::Value(ConstValue::Slice { data: alloc, start: _, end: _ })
| ty::ConstKind::Value(ConstValue::ByRef { alloc, .. }) => {
for &((), id) in alloc.relocations().values() {
collect_miri(tcx, id, output);
}
}
ty::ConstKind::Value(val) => collect_const_value(tcx, val, output),
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
match tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) {
Ok(val) => collect_const(tcx, val, param_substs, output),
Ok(val) => collect_const_value(tcx, val, output),
Err(ErrorHandled::Reported) => {}
Err(ErrorHandled::TooGeneric) => {
span_bug!(tcx.def_span(def_id), "collection encountered polymorphic constant",)
@ -1206,3 +1198,19 @@ fn collect_const<'tcx>(
_ => {}
}
}
fn collect_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
value: ConstValue<'tcx>,
output: &mut Vec<MonoItem<'tcx>>,
) {
match value {
ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output),
ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => {
for &((), id) in alloc.relocations().values() {
collect_miri(tcx, id, output);
}
}
_ => {}
}
}

View File

@ -418,7 +418,17 @@ fn make_mirror_unadjusted<'a, 'tcx>(
None,
Some(span),
) {
Ok(cv) => cv.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()),
Ok(cv) => {
if let Some(count) = cv.try_to_bits_for_ty(
cx.tcx,
ty::ParamEnv::reveal_all(),
cx.tcx.types.usize,
) {
count as u64
} else {
bug!("repeat count constant value can't be converted to usize");
}
}
Err(ErrorHandled::Reported) => 0,
Err(ErrorHandled::TooGeneric) => {
let span = cx.tcx.def_span(def_id);

View File

@ -769,7 +769,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
Some(span),
) {
Ok(value) => {
let pattern = self.const_to_pat(value, id, span);
let const_ = self.tcx.mk_const(ty::Const {
val: ty::ConstKind::Value(value),
ty: self.tables.node_type(id),
});
let pattern = self.const_to_pat(&const_, id, span);
if !is_associated_const {
return pattern;
}
@ -789,7 +794,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
user_ty_span: span,
},
}),
ty: value.ty,
ty: const_.ty,
}
} else {
pattern

View File

@ -25,7 +25,7 @@ use rustc::ty;
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::Ty;
use rustc::ty::TypeFoldable;
use rustc::ty::{AdtKind, Visibility};
use rustc::ty::{AdtKind, ConstKind, Visibility};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir as hir;
@ -1011,7 +1011,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let count = if self.const_param_def_id(count).is_some() {
Ok(self.to_const(count, tcx.type_of(count_def_id)))
} else {
tcx.const_eval_poly(count_def_id)
tcx.const_eval_poly(count_def_id).map(|val| {
tcx.mk_const(ty::Const {
val: ConstKind::Value(val),
ty: tcx.type_of(count_def_id),
})
})
};
let uty = match expected {

View File

@ -1832,18 +1832,17 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span)
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
// the consumer's responsibility to ensure all bytes that have been read
// have defined values.
if let Ok(static_) = tcx.const_eval_poly(id) {
let alloc = if let ty::ConstKind::Value(ConstValue::ByRef { alloc, .. }) = static_.val {
alloc
} else {
bug!("Matching on non-ByRef static")
};
if alloc.relocations().len() != 0 {
let msg = "statics with a custom `#[link_section]` must be a \
match tcx.const_eval_poly(id) {
Ok(ConstValue::ByRef { alloc, .. }) => {
if alloc.relocations().len() != 0 {
let msg = "statics with a custom `#[link_section]` must be a \
simple list of bytes on the wasm target with no \
extra levels of indirection such as references";
tcx.sess.span_err(span, msg);
tcx.sess.span_err(span, msg);
}
}
Ok(_) => bug!("Matching on non-ByRef static"),
Err(_) => {}
}
}

View File

@ -1332,7 +1332,11 @@ impl Clean<Type> for hir::Ty<'_> {
TyKind::Array(ref ty, ref length) => {
let def_id = cx.tcx.hir().local_def_id(length.hir_id);
let length = match cx.tcx.const_eval_poly(def_id) {
Ok(length) => print_const(cx, length),
Ok(length) => {
let const_ =
ty::Const { val: ty::ConstKind::Value(length), ty: cx.tcx.types.usize };
print_const(cx, &const_)
}
Err(_) => cx
.sess()
.source_map()

View File

@ -487,15 +487,18 @@ pub fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String {
}
pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option<String> {
let value =
cx.tcx.const_eval_poly(def_id).ok().and_then(|value| match (value.val, &value.ty.kind) {
(_, ty::Ref(..)) => None,
(ty::ConstKind::Value(ConstValue::Scalar(_)), ty::Adt(_, _)) => None,
(ty::ConstKind::Value(ConstValue::Scalar(_)), _) => {
Some(print_const_with_custom_print_scalar(cx, value))
let value = cx.tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = cx.tcx.type_of(def_id);
match (val, &ty.kind) {
(_, &ty::Ref(..)) => None,
(ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(ConstValue::Scalar(_), _) => {
let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
Some(print_const_with_custom_print_scalar(cx, &const_))
}
_ => None,
});
}
});
value
}
@ -510,7 +513,7 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
.collect()
}
fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> String {
fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &ty::Const<'tcx>) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct.val, &ct.ty.kind) {