/// }
/// }
/// ```
+#[cfg_attr(not(stage0), lang = "ord")]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Ord: Eq + PartialOrd<Self> {
/// This method returns an `Ordering` between `self` and `other`.
/// assert_eq!(x < y, true);
/// assert_eq!(x.lt(&y), true);
/// ```
-#[lang = "ord"]
+#[cfg_attr(stage0, lang = "ord")]
+#[cfg_attr(not(stage0), lang = "partial_ord")]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"]
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX};
use hir::map::DefPathHash;
use hir::{HirId, ItemLocalId};
+use mir;
use ich::Fingerprint;
use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty};
use ty::{self, TyCtxt, layout};
use ty::subst::Substs;
use rustc_const_math::*;
-use mir::interpret::Value;
+use mir::interpret::{Value, PrimVal};
use graphviz::IntoCow;
use errors::DiagnosticBuilder;
Function(DefId, &'tcx Substs<'tcx>),
Aggregate(ConstAggregate<'tcx>),
Unevaluated(DefId, &'tcx Substs<'tcx>),
- /// A miri value, currently only produced if old ctfe fails, but miri succeeds
+ /// A miri value, currently only produced if --miri is enabled
Value(Value),
}
}
impl<'tcx> ConstVal<'tcx> {
- pub fn to_const_int(&self) -> Option<ConstInt> {
+ pub fn to_u128(&self) -> Option<u128> {
match *self {
- ConstVal::Integral(i) => Some(i),
- ConstVal::Bool(b) => Some(ConstInt::U8(b as u8)),
- ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)),
- _ => None
+ ConstVal::Integral(i) => i.to_u128(),
+ ConstVal::Bool(b) => Some(b as u128),
+ ConstVal::Char(ch) => Some(ch as u32 as u128),
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
+ Some(b)
+ },
+ _ => None,
+ }
+ }
+ pub fn unwrap_u64(&self) -> u64 {
+ match self.to_u128() {
+ Some(val) => {
+ assert_eq!(val as u64 as u128, val);
+ val as u64
+ },
+ None => bug!("expected constant u64, got {:#?}", self),
+ }
+ }
+ pub fn unwrap_usize<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> ConstUsize {
+ match *self {
+ ConstVal::Integral(ConstInt::Usize(i)) => i,
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
+ assert_eq!(b as u64 as u128, b);
+ match ConstUsize::new(b as u64, tcx.sess.target.usize_ty) {
+ Ok(val) => val,
+ Err(e) => bug!("{:#?} is not a usize {:?}", self, e),
+ }
+ },
+ _ => bug!("expected constant u64, got {:#?}", self),
}
}
}
GeneratorTraitLangItem, "generator", gen_trait;
EqTraitLangItem, "eq", eq_trait;
+ PartialOrdTraitLangItem, "partial_ord", partial_ord_trait;
OrdTraitLangItem, "ord", ord_trait;
// A number of panic-related lang items. The `panic` item corresponds to
// Always promote `[T; 0]` (even when e.g. borrowed mutably).
let promotable = match expr_ty.sty {
- ty::TyArray(_, len) if
- len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true,
+ ty::TyArray(_, len) if len.val.to_u128() == Some(0) => true,
_ => promotable,
};
#![allow(unknown_lints)]
use ty::layout::{Align, HasDataLayout};
+use ty;
use super::{EvalResult, MemoryPointer, PointerArithmetic};
use syntax::ast::FloatTy;
ByValPair(PrimVal, PrimVal),
}
+impl<'tcx> ty::TypeFoldable<'tcx> for Value {
+ fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
+ *self
+ }
+ fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
+ false
+ }
+}
+
/// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally.
/// This type clears up a few APIs where having a `PrimVal` argument for something that is
/// potentially an integer pointer or a pointer to an allocation was unclear.
use graphviz::IntoCow;
use middle::const_val::ConstVal;
use middle::region;
-use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
+use rustc_const_math::{ConstUsize, ConstMathErr};
use rustc_data_structures::sync::{Lrc};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
use hir::def::CtorKind;
use hir::def_id::DefId;
use mir::visit::MirVisitable;
+use mir::interpret::{Value, PrimVal};
use ty::subst::{Subst, Substs};
use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
+use ty::TypeAndMut;
use util::ppaux;
use std::slice;
use hir::{self, InlineAsm};
/// Possible values. The locations to branch to in each case
/// are found in the corresponding indices from the `targets` vector.
- values: Cow<'tcx, [ConstInt]>,
+ values: Cow<'tcx, [u128]>,
/// Possible branch sites. The last element of this vector is used
/// for the otherwise branch, so targets.len() == values.len() + 1
impl<'tcx> TerminatorKind<'tcx> {
pub fn if_<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, cond: Operand<'tcx>,
t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
- static BOOL_SWITCH_FALSE: &'static [ConstInt] = &[ConstInt::U8(0)];
+ static BOOL_SWITCH_FALSE: &'static [u128] = &[0];
TerminatorKind::SwitchInt {
discr: cond,
switch_ty: tcx.types.bool,
match *self {
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
- SwitchInt { ref values, .. } => {
+ SwitchInt { ref values, switch_ty, .. } => {
values.iter()
- .map(|const_val| {
- let mut buf = String::new();
- fmt_const_val(&mut buf, &ConstVal::Integral(*const_val)).unwrap();
- buf.into()
+ .map(|&u| {
+ let mut s = String::new();
+ print_miri_value(
+ Value::ByVal(PrimVal::Bytes(u)),
+ switch_ty,
+ &mut s,
+ ).unwrap();
+ s.into()
})
.chain(iter::once(String::from("otherwise").into()))
.collect()
ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
- val: ConstVal::Function(def_id, substs),
+ val: if tcx.sess.opts.debugging_opts.miri {
+ // ZST function type
+ ConstVal::Value(Value::ByVal(PrimVal::Undef))
+ } else {
+ ConstVal::Function(def_id, substs)
+ },
ty
})
},
match *self {
Value { value } => {
write!(fmt, "const ")?;
- fmt_const_val(fmt, &value.val)
+ fmt_const_val(fmt, value)
}
Promoted { index } => {
write!(fmt, "{:?}", index)
}
/// Write a `ConstVal` in a way closer to the original source code than the `Debug` output.
-fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
+fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ty::Const) -> fmt::Result {
use middle::const_val::ConstVal::*;
- match *const_val {
+ match const_val.val {
Float(f) => write!(fmt, "{:?}", f),
Integral(n) => write!(fmt, "{}", n),
Str(s) => write!(fmt, "{:?}", s),
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val),
Unevaluated(..) => write!(fmt, "{:?}", const_val),
- Value(val) => write!(fmt, "{:?}", val),
+ Value(val) => print_miri_value(val, const_val.ty, fmt),
+ }
+}
+
+fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Result {
+ use ty::TypeVariants::*;
+ use rustc_const_math::ConstFloat;
+ match (value, &ty.sty) {
+ (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"),
+ (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"),
+ (Value::ByVal(PrimVal::Bytes(bits)), &TyFloat(fty)) =>
+ write!(f, "{}", ConstFloat { bits, ty: fty }),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyUint(ui)) => write!(f, "{:?}{}", n, ui),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyInt(i)) => write!(f, "{:?}{}", n as i128, i),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyChar) =>
+ write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()),
+ (Value::ByVal(PrimVal::Undef), &TyFnDef(did, _)) =>
+ write!(f, "{}", item_path_str(did)),
+ (Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)), &TyRef(_, TypeAndMut {
+ ty: &ty::TyS { sty: TyStr, .. }, ..
+ })) => {
+ ty::tls::with(|tcx| {
+ let alloc = tcx
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .expect("miri alloc not found");
+ assert_eq!(len as usize as u128, len);
+ let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];
+ let s = ::std::str::from_utf8(slice)
+ .expect("non utf8 str from miri");
+ write!(f, "{:?}", s)
+ })
+ },
+ _ => write!(f, "{:?}:{}", value, ty),
}
}
}
}
+impl<'tcx> TypeFoldable<'tcx> for Field {
+ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
+ *self
+ }
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
+ false
+ }
+}
+
impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
Constant {
PlaceTy::Ty {
ty: match ty.sty {
ty::TyArray(inner, size) => {
- let size = size.val.to_const_int().unwrap().to_u64().unwrap();
+ let size = size.val.unwrap_u64();
let len = size - (from as u64) - (to as u64);
tcx.mk_array(inner, len)
}
self.super_generator_interior(interior);
}
- fn visit_const_int(&mut self,
- const_int: &ConstInt,
- _: Location) {
- self.super_const_int(const_int);
- }
-
fn visit_const_usize(&mut self,
const_usize: & $($mutability)* ConstUsize,
_: Location) {
TerminatorKind::SwitchInt { ref $($mutability)* discr,
ref $($mutability)* switch_ty,
- ref values,
+ values: _,
ref targets } => {
self.visit_operand(discr, source_location);
self.visit_ty(switch_ty, TyContext::Location(source_location));
- for value in &values[..] {
- self.visit_const_int(value, source_location);
- }
for &target in targets {
self.visit_branch(block, target);
}
_substs: & $($mutability)* ClosureSubsts<'tcx>) {
}
- fn super_const_int(&mut self, _const_int: &ConstInt) {
- }
-
fn super_const_usize(&mut self, _const_usize: & $($mutability)* ConstUsize) {
}
use middle::lang_items;
use middle::resolve_lifetime::{self, ObjectLifetimeDefault};
use middle::stability;
-use mir::{Mir, interpret};
+use mir::{self, Mir, interpret};
+use mir::interpret::{Value, PrimVal};
use ty::subst::{Kind, Substs};
use ty::ReprOptions;
use ty::Instance;
self.get_lang_items(LOCAL_CRATE)
}
+ pub fn is_binop_lang_item(&self, def_id: DefId) -> Option<(mir::BinOp, bool)> {
+ let items = self.lang_items();
+ let def_id = Some(def_id);
+ if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) }
+ else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) }
+ else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) }
+ else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) }
+ else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) }
+ else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) }
+ else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) }
+ else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) }
+ else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) }
+ else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) }
+ else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) }
+ else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) }
+ else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) }
+ else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) }
+ else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) }
+ else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) }
+ else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) }
+ else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) }
+ else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) }
+ else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) }
+ else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) }
+ else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) }
+ else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) }
+ else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) }
+ else { None }
+ }
+
pub fn stability(self) -> Lrc<stability::Index<'tcx>> {
self.stability_index(LOCAL_CRATE)
}
pub fn mk_array_const_usize(self, ty: Ty<'tcx>, n: ConstUsize) -> Ty<'tcx> {
self.mk_ty(TyArray(ty, self.mk_const(ty::Const {
- val: ConstVal::Integral(ConstInt::Usize(n)),
+ val: if self.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(n.as_u64().into())))
+ } else {
+ ConstVal::Integral(ConstInt::Usize(n))
+ },
ty: self.types.usize
})))
}
use hir::def_id::DefId;
use middle::const_val::ConstVal;
use ty::{self, BoundRegion, Region, Ty, TyCtxt};
+use mir::interpret::{Value, PrimVal};
use std::fmt;
use syntax::abi;
ty::TyAdt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)),
ty::TyForeign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)),
ty::TyArray(_, n) => {
- if let ConstVal::Integral(ConstInt::Usize(n)) = n.val {
- format!("array of {} elements", n)
- } else {
- "array".to_string()
+ match n.val {
+ ConstVal::Integral(ConstInt::Usize(n)) =>
+ format!("array of {} elements", n),
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))) =>
+ format!("array of {} elements", n),
+ _ => "array".to_string(),
}
}
ty::TySlice(_) => "slice".to_string(),
}))
},
TyArray(ty, len) => {
- match len.val.to_const_int().and_then(|i| i.to_u64()) {
+ match len.val.to_u128() {
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
Some(n) if n != 0 => ty.uninhabited_from(visited, tcx),
enum StructKind {
/// A tuple, closure, or univariant which cannot be coerced to unsized.
AlwaysSized,
- /// A univariant, the last field of which may be coerced to unsized.
+ /// A univariant, the last field of which fn compute_uncachedmay be coerced to unsized.
MaybeUnsized,
/// A univariant, but with a prefix of an arbitrary size & alignment (e.g. enum tag).
Prefixed(Size, Align),
}
let element = self.layout_of(element)?;
- let count = count.val.to_const_int().unwrap().to_u64().unwrap();
+ let count = count.val.unwrap_u64();
let size = element.size.checked_mul(count, dl)
.ok_or(LayoutError::SizeOverflow(ty))?;
use ty::{self, Ty, TyCtxt};
use ty::maps::queries;
use ty::subst::Substs;
+use mir;
use std::hash::Hash;
use syntax_pos::symbol::InternedString;
use ty::{self, Ty, TyCtxt};
use ty::subst::Substs;
use ty::fast_reject::SimplifiedType;
+use mir;
use std::fmt::Debug;
use std::hash::Hash;
RESERVED_FOR_INCR_COMP_CACHE, LOCAL_CRATE};
use hir::map::definitions::DefPathHash;
use ich::{CachingCodemapView, Fingerprint};
-use mir;
+use mir::{self, interpret};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> );
+
+impl<'a, 'tcx, 'x> SpecializedDecoder<interpret::AllocId> for CacheDecoder<'a, 'tcx, 'x> {
+ fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> {
+ unimplemented!()
+ }
+}
+
impl<'a, 'tcx, 'x> SpecializedDecoder<Span> for CacheDecoder<'a, 'tcx, 'x> {
fn specialized_decode(&mut self) -> Result<Span, Self::Error> {
let tag: u8 = Decodable::decode(self)?;
use middle::privacy::AccessLevels;
use middle::resolve_lifetime::ObjectLifetimeDefault;
use mir::Mir;
+use mir::interpret::{Value, PrimVal};
use mir::GeneratorLayout;
use session::CrateDisambiguator;
use traits;
Ok(&ty::Const { val: ConstVal::Integral(v), .. }) => {
discr = v;
}
+ Ok(&ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
+ ..
+ }) => {
+ trace!("discriminants: {} ({:?})", b, repr_type);
+ use syntax::attr::IntType;
+ discr = match repr_type {
+ IntType::SignedInt(int_type) => ConstInt::new_signed(
+ b as i128, int_type, tcx.sess.target.isize_ty).unwrap(),
+ IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned(
+ b, uint_type, tcx.sess.target.usize_ty).unwrap(),
+ };
+ }
err => {
if !expr_did.is_local() {
span_bug!(tcx.def_span(expr_did),
explicit_value = v;
break;
}
+ Ok(&ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
+ ..
+ }) => {
+ trace!("discriminants: {} ({:?})", b, repr_type);
+ use syntax::attr::IntType;
+ explicit_value = match repr_type {
+ IntType::SignedInt(int_type) => ConstInt::new_signed(
+ b as i128, int_type, tcx.sess.target.isize_ty).unwrap(),
+ IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned(
+ b, uint_type, tcx.sess.target.usize_ty).unwrap(),
+ };
+ break;
+ }
err => {
if !expr_did.is_local() {
span_bug!(tcx.def_span(expr_did),
use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::fold::{TypeVisitor, TypeFolder};
use ty::error::{ExpectedFound, TypeError};
+use mir::interpret::{Value, PrimVal};
use util::common::ErrorReported;
use std::rc::Rc;
use std::iter;
let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result<u64, ErrorReported> {
match x.val {
ConstVal::Integral(x) => Ok(x.to_u64().unwrap()),
+ ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()),
ConstVal::Unevaluated(def_id, substs) => {
// FIXME(eddyb) get the right param_env.
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => {
return Ok(x.to_u64().unwrap());
}
+ Ok(&ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
+ ..
+ }) => {
+ assert_eq!(b as u64 as u128, b);
+ return Ok(b as u64);
+ }
_ => {}
}
}
}
}
+impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
+ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+ use ty::InstanceDef::*;
+ Self {
+ substs: self.substs.fold_with(folder),
+ def: match self.def {
+ Item(did) => Item(did.fold_with(folder)),
+ Intrinsic(did) => Intrinsic(did.fold_with(folder)),
+ FnPtrShim(did, ty) => FnPtrShim(
+ did.fold_with(folder),
+ ty.fold_with(folder),
+ ),
+ Virtual(did, i) => Virtual(
+ did.fold_with(folder),
+ i,
+ ),
+ ClosureOnceShim { call_once } => ClosureOnceShim {
+ call_once: call_once.fold_with(folder),
+ },
+ DropGlue(did, ty) => DropGlue(
+ did.fold_with(folder),
+ ty.fold_with(folder),
+ ),
+ CloneShim(did, ty) => CloneShim(
+ did.fold_with(folder),
+ ty.fold_with(folder),
+ ),
+ },
+ }
+ }
+
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+ use ty::InstanceDef::*;
+ self.substs.visit_with(visitor) ||
+ match self.def {
+ Item(did) => did.visit_with(visitor),
+ Intrinsic(did) => did.visit_with(visitor),
+ FnPtrShim(did, ty) => {
+ did.visit_with(visitor) ||
+ ty.visit_with(visitor)
+ },
+ Virtual(did, _) => did.visit_with(visitor),
+ ClosureOnceShim { call_once } => call_once.visit_with(visitor),
+ DropGlue(did, ty) => {
+ did.visit_with(visitor) ||
+ ty.visit_with(visitor)
+ },
+ CloneShim(did, ty) => {
+ did.visit_with(visitor) ||
+ ty.visit_with(visitor)
+ },
+ }
+ }
+}
+
impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
let sty = match self.sty {
use ty::TypeVariants::*;
use util::common::ErrorReported;
use middle::lang_items;
+use mir::interpret::{Value, PrimVal};
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
self.hash_discriminant_u8(&n.val);
match n.val {
ConstVal::Integral(x) => self.hash(x.to_u64().unwrap()),
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => self.hash(b),
ConstVal::Unevaluated(def_id, _) => self.def_id(def_id),
_ => bug!("arrays should not have {:?} as length", n)
}
use ty::{TyDynamic, TyInt, TyUint, TyInfer};
use ty::{self, Ty, TyCtxt, TypeFoldable};
use util::nodemap::FxHashSet;
+use mir::interpret::{Value, PrimVal};
use std::cell::Cell;
use std::fmt;
ConstVal::Integral(ConstInt::Usize(sz)) => {
write!(f, "{}", sz)?;
}
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(sz))) => {
+ write!(f, "{}", sz)?;
+ }
ConstVal::Unevaluated(_def_id, substs) => {
write!(f, "<unevaluated{:?}>", &substs[..])?;
}
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::mir::Field;
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::util::common::ErrorReported;
use syntax_pos::{Span, DUMMY_SP};
}
})).collect()
}
+ box PatternKind::Constant {
+ value: &ty::Const { val: ConstVal::Value(b), ty }
+ } => {
+ match b {
+ Value::ByVal(PrimVal::Ptr(ptr)) => {
+ let is_array_ptr = ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|t| t.ty.builtin_index())
+ .map_or(false, |t| t == tcx.types.u8);
+ assert!(is_array_ptr);
+ let alloc = tcx
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .unwrap();
+ assert_eq!(ptr.offset, 0);
+ // FIXME: check length
+ alloc.bytes.iter().map(|b| {
+ &*pattern_arena.alloc(Pattern {
+ ty: tcx.types.u8,
+ span: pat.span,
+ kind: box PatternKind::Constant {
+ value: tcx.mk_const(ty::Const {
+ val: ConstVal::Value(Value::ByVal(
+ PrimVal::Bytes(*b as u128),
+ )),
+ ty: tcx.types.u8
+ })
+ }
+ })
+ }).collect()
+ },
+ _ => bug!("not a byte str: {:?}", b),
+ }
+ }
_ => span_bug!(pat.span, "unexpected byte array pattern {:?}", pat)
}
}).clone()
ty::TyBool => {
[true, false].iter().map(|&b| {
ConstantValue(cx.tcx.mk_const(ty::Const {
- val: ConstVal::Bool(b),
+ val: if cx.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(b as u128)))
+ } else {
+ ConstVal::Bool(b)
+ },
ty: cx.tcx.types.bool
}))
}).collect()
}
- ty::TyArray(ref sub_ty, len) if len.val.to_const_int().is_some() => {
- let len = len.val.to_const_int().unwrap().to_u64().unwrap();
+ ty::TyArray(ref sub_ty, len) if len.val.to_u128().is_some() => {
+ let len = len.val.unwrap_u64();
if len != 0 && cx.is_uninhabited(sub_ty) {
vec![]
} else {
}
fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
- _cx: &mut MatchCheckCtxt<'a, 'tcx>,
+ cx: &mut MatchCheckCtxt<'a, 'tcx>,
patterns: I) -> u64
where I: Iterator<Item=&'p Pattern<'tcx>>
{
PatternKind::Constant { value: &ty::Const { val: ConstVal::ByteStr(b), .. } } => {
max_fixed_len = cmp::max(max_fixed_len, b.data.len() as u64);
}
+ PatternKind::Constant {
+ value: &ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))),
+ ty,
+ }
+ } => {
+ let is_array_ptr = ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|t| t.ty.builtin_index())
+ .map_or(false, |t| t == cx.tcx.types.u8);
+ if is_array_ptr {
+ let alloc = cx.tcx
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .unwrap();
+ max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64);
+ }
+ }
PatternKind::Slice { ref prefix, slice: None, ref suffix } => {
let fixed_len = prefix.len() as u64 + suffix.len() as u64;
max_fixed_len = cmp::max(max_fixed_len, fixed_len);
witness: WitnessPreference)
-> Usefulness<'tcx> {
let &Matrix(ref rows) = matrix;
- debug!("is_useful({:?}, {:?})", matrix, v);
+ debug!("is_useful({:#?}, {:#?})", matrix, v);
// The base case. We are pattern-matching on () and the return value is
// based on whether our matrix has a row or not.
max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0])))
};
- debug!("is_useful_expand_first_col: pcx={:?}, expanding {:?}", pcx, v[0]);
+ debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]);
if let Some(constructors) = pat_constructors(cx, v[0], pcx) {
- debug!("is_useful - expanding constructors: {:?}", constructors);
+ debug!("is_useful - expanding constructors: {:#?}", constructors);
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
).find(|result| result.is_useful()).unwrap_or(NotUseful)
let used_ctors: Vec<Constructor> = rows.iter().flat_map(|row| {
pat_constructors(cx, row[0], pcx).unwrap_or(vec![])
}).collect();
- debug!("used_ctors = {:?}", used_ctors);
+ debug!("used_ctors = {:#?}", used_ctors);
let all_ctors = all_constructors(cx, pcx);
- debug!("all_ctors = {:?}", all_ctors);
+ debug!("all_ctors = {:#?}", all_ctors);
let missing_ctors: Vec<Constructor> = all_ctors.iter().filter(|c| {
!used_ctors.contains(*c)
}).cloned().collect();
all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
let is_declared_nonexhaustive =
cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
- debug!("missing_ctors={:?} is_privately_empty={:?} is_declared_nonexhaustive={:?}",
+ debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
missing_ctors, is_privately_empty, is_declared_nonexhaustive);
// For privately empty and non-exhaustive enums, we work as if there were an "extra"
lty: Ty<'tcx>,
witness: WitnessPreference) -> Usefulness<'tcx>
{
- debug!("is_useful_specialized({:?}, {:?}, {:?})", v, ctor, lty);
+ debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty);
let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| {
Pattern {
Some(vec![ConstantRange(lo, hi, end)]),
PatternKind::Array { .. } => match pcx.ty.sty {
ty::TyArray(_, length) => Some(vec![
- Slice(length.val.to_const_int().unwrap().to_u64().unwrap())
+ Slice(length.val.unwrap_u64())
]),
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty)
},
/// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3.
/// A struct pattern's arity is the number of fields it contains, etc.
fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> u64 {
- debug!("constructor_arity({:?}, {:?})", ctor, ty);
+ debug!("constructor_arity({:#?}, {:?})", ctor, ty);
match ty.sty {
ty::TyTuple(ref fs, _) => fs.len() as u64,
ty::TySlice(..) | ty::TyArray(..) => match *ctor {
ctor: &Constructor,
ty: Ty<'tcx>) -> Vec<Ty<'tcx>>
{
- debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty);
+ debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty);
match ty.sty {
ty::TyTuple(ref fs, _) => fs.into_iter().map(|t| *t).collect(),
ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor {
Slice(length) => (0..length).map(|_| ty).collect(),
ConstantValue(_) => vec![],
+ Single => vec![ty],
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty)
},
ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty],
// Use T as the sub pattern type of Box<T>.
vec![substs.type_at(0)]
} else {
+ if let ConstantValue(_) = *ctor {
+ return vec![];
+ }
adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| {
let is_visible = adt.is_enum()
|| field.vis.is_accessible_from(cx.module, cx.tcx);
}
}
-fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
+fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span,
ctor: &Constructor,
prefix: &[Pattern],
slice: &Option<Pattern>,
suffix: &[Pattern])
-> Result<bool, ErrorReported> {
- let data = match *ctor {
+ let data: &[u8] = match *ctor {
ConstantValue(&ty::Const { val: ConstVal::ByteStr(b), .. }) => b.data,
+ ConstantValue(&ty::Const { val: ConstVal::Value(
+ Value::ByVal(PrimVal::Ptr(ptr))
+ ), ty }) => {
+ let is_array_ptr = ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|t| t.ty.builtin_index())
+ .map_or(false, |t| t == tcx.types.u8);
+ assert!(is_array_ptr);
+ tcx
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .unwrap()
+ .bytes
+ .as_ref()
+ }
_ => bug!()
};
return Ok(false);
}
},
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))) => {
+ assert_eq!(b as u8 as u128, b);
+ if b as u8 != *ch {
+ return Ok(false);
+ }
+ }
_ => span_bug!(pat.span, "bad const u8 {:?}", value)
},
_ => {}
Ok(true)
}
-fn constructor_covered_by_range(tcx: TyCtxt, span: Span,
- ctor: &Constructor,
+fn constructor_covered_by_range(ctor: &Constructor,
from: &ConstVal, to: &ConstVal,
- end: RangeEnd)
+ end: RangeEnd,
+ ty: Ty)
-> Result<bool, ErrorReported> {
- let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less);
- let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to);
+ trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty);
+ let cmp_from = |c_from| compare_const_vals(c_from, from, ty)
+ .map(|res| res != Ordering::Less);
+ let cmp_to = |c_to| compare_const_vals(c_to, to, ty);
+ macro_rules! some_or_ok {
+ ($e:expr) => {
+ match $e {
+ Some(to) => to,
+ None => return Ok(false), // not char or int
+ }
+ };
+ }
match *ctor {
ConstantValue(value) => {
- let to = cmp_to(&value.val)?;
+ let to = some_or_ok!(cmp_to(&value.val));
let end = (to == Ordering::Less) ||
(end == RangeEnd::Included && to == Ordering::Equal);
- Ok(cmp_from(&value.val)? && end)
+ Ok(some_or_ok!(cmp_from(&value.val)) && end)
},
ConstantRange(from, to, RangeEnd::Included) => {
- let to = cmp_to(&to.val)?;
+ let to = some_or_ok!(cmp_to(&to.val));
let end = (to == Ordering::Less) ||
(end == RangeEnd::Included && to == Ordering::Equal);
- Ok(cmp_from(&from.val)? && end)
+ Ok(some_or_ok!(cmp_from(&from.val)) && end)
},
ConstantRange(from, to, RangeEnd::Excluded) => {
- let to = cmp_to(&to.val)?;
+ let to = some_or_ok!(cmp_to(&to.val));
let end = (to == Ordering::Less) ||
(end == RangeEnd::Excluded && to == Ordering::Equal);
- Ok(cmp_from(&from.val)? && end)
+ Ok(some_or_ok!(cmp_from(&from.val)) && end)
}
+ Variant(_) |
Single => Ok(true),
_ => bug!(),
}
result[subpat.field.index()] = &subpat.pattern;
}
- debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result);
+ debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result);
result
}
fn specialize<'p, 'a: 'p, 'tcx: 'a>(
cx: &mut MatchCheckCtxt<'a, 'tcx>,
r: &[&'p Pattern<'tcx>],
- constructor: &Constructor,
+ constructor: &Constructor<'tcx>,
wild_patterns: &[&'p Pattern<'tcx>])
-> Option<Vec<&'p Pattern<'tcx>>>
{
None
}
}
+ ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr))) => {
+ let is_array_ptr = value.ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|t| t.ty.builtin_index())
+ .map_or(false, |t| t == cx.tcx.types.u8);
+ assert!(is_array_ptr);
+ let data_len = cx.tcx
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .unwrap()
+ .bytes
+ .len();
+ if wild_patterns.len() == data_len {
+ Some(cx.lower_byte_str_pattern(pat))
+ } else {
+ None
+ }
+ }
_ => span_bug!(pat.span,
"unexpected const-val {:?} with ctor {:?}", value, constructor)
},
_ => {
match constructor_covered_by_range(
- cx.tcx, pat.span, constructor, &value.val, &value.val, RangeEnd::Included
+ constructor, &value.val, &value.val, RangeEnd::Included,
+ value.ty,
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
PatternKind::Range { lo, hi, ref end } => {
match constructor_covered_by_range(
- cx.tcx, pat.span, constructor, &lo.val, &hi.val, end.clone()
+ constructor, &lo.val, &hi.val, end.clone(), lo.ty,
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
}
}
};
- debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head);
+ debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head);
head.map(|mut head| {
head.extend_from_slice(&r[1 ..]);
use syntax::ast;
use syntax::attr;
use rustc::hir::{self, Expr};
-use syntax_pos::Span;
use std::cmp::Ordering;
hir::ExprUnary(hir::UnNeg, ref inner) => {
// unary neg literals already got their sign during creation
if let hir::ExprLit(ref lit) = inner.node {
- use syntax::ast::*;
- use syntax::ast::LitIntType::*;
- const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128;
- const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128;
- const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128;
- const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128;
- const I128_OVERFLOW: u128 = i128::min_value() as u128;
- let negated = match (&lit.node, &ty.sty) {
- (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) |
- (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => {
- Some(I8(i8::min_value()))
- },
- (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) |
- (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => {
- Some(I16(i16::min_value()))
- },
- (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) |
- (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => {
- Some(I32(i32::min_value()))
- },
- (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) |
- (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => {
- Some(I64(i64::min_value()))
- },
- (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) |
- (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => {
- Some(I128(i128::min_value()))
- },
- (&LitKind::Int(n, _), &ty::TyInt(IntTy::Isize)) |
- (&LitKind::Int(n, Signed(IntTy::Isize)), _) => {
- match tcx.sess.target.isize_ty {
- IntTy::I16 => if n == I16_OVERFLOW {
- Some(Isize(Is16(i16::min_value())))
- } else {
- None
- },
- IntTy::I32 => if n == I32_OVERFLOW {
- Some(Isize(Is32(i32::min_value())))
- } else {
- None
- },
- IntTy::I64 => if n == I64_OVERFLOW {
- Some(Isize(Is64(i64::min_value())))
- } else {
- None
- },
- _ => span_bug!(e.span, "typeck error")
- }
- },
- _ => None
+ return match lit_to_const(&lit.node, tcx, ty, true) {
+ Ok(val) => Ok(mk_const(val)),
+ Err(err) => signal!(e, err),
};
- if let Some(i) = negated {
- return Ok(mk_const(Integral(i)));
- }
}
mk_const(match cx.eval(inner)?.val {
Float(f) => Float(-f),
};
callee_cx.eval(&body.value)?
},
- hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty) {
+ hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ty, false) {
Ok(val) => mk_const(val),
Err(err) => signal!(e, err),
},
}
hir::ExprRepeat(ref elem, _) => {
let n = match ty.sty {
- ty::TyArray(_, n) => n.val.to_const_int().unwrap().to_u64().unwrap(),
+ ty::TyArray(_, n) => n.val.unwrap_u64(),
_ => span_bug!(e.span, "typeck error")
};
mk_const(Aggregate(Repeat(cx.eval(elem)?, n)))
if let Aggregate(Tuple(fields)) = cx.eval(base)?.val {
fields[index.node]
} else {
- signal!(base, ExpectedConstTuple);
+ span_bug!(base.span, "{:#?}", cx.eval(base)?.val);
+ //signal!(base, ExpectedConstTuple);
}
}
hir::ExprField(ref base, field_name) => {
},
ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
ty::TyArray(ty, n) => {
- let n = n.val.to_const_int().unwrap().to_u64().unwrap();
+ let n = n.val.unwrap_u64();
if ty == tcx.types.u8 && n == b.data.len() as u64 {
Ok(val)
} else {
}
}
-fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
+pub fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
- mut ty: Ty<'tcx>)
+ mut ty: Ty<'tcx>,
+ neg: bool)
-> Result<ConstVal<'tcx>, ErrKind<'tcx>> {
use syntax::ast::*;
use syntax::ast::LitIntType::*;
+ if tcx.sess.opts.debugging_opts.miri {
+ use rustc::mir::interpret::*;
+ let lit = match *lit {
+ LitKind::Str(ref s, _) => {
+ let s = s.as_str();
+ let id = tcx.allocate_cached(s.as_bytes());
+ let ptr = MemoryPointer::new(AllocId(id), 0);
+ Value::ByValPair(
+ PrimVal::Ptr(ptr),
+ PrimVal::from_u128(s.len() as u128),
+ )
+ },
+ LitKind::ByteStr(ref data) => {
+ let id = tcx.allocate_cached(data);
+ let ptr = MemoryPointer::new(AllocId(id), 0);
+ Value::ByVal(PrimVal::Ptr(ptr))
+ },
+ LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
+ LitKind::Int(n, _) if neg => {
+ let n = n as i128;
+ let n = n.overflowing_neg().0;
+ Value::ByVal(PrimVal::Bytes(n as u128))
+ },
+ LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n as u128)),
+ LitKind::Float(n, fty) => {
+ let n = n.as_str();
+ let mut f = parse_float(&n, fty)?;
+ if neg {
+ f = -f;
+ }
+ let bits = f.bits;
+ Value::ByVal(PrimVal::Bytes(bits))
+ }
+ LitKind::FloatUnsuffixed(n) => {
+ let fty = match ty.sty {
+ ty::TyFloat(fty) => fty,
+ _ => bug!()
+ };
+ let n = n.as_str();
+ let mut f = parse_float(&n, fty)?;
+ if neg {
+ f = -f;
+ }
+ let bits = f.bits;
+ Value::ByVal(PrimVal::Bytes(bits))
+ }
+ LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
+ LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
+ };
+ return Ok(ConstVal::Value(lit));
+ }
+
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(tcx)
match (&ty.sty, hint) {
(&ty::TyInt(ity), _) |
(_, Signed(ity)) => {
- Ok(Integral(ConstInt::new_signed_truncating(n as i128,
+ let mut n = n as i128;
+ if neg {
+ n = n.overflowing_neg().0;
+ }
+ Ok(Integral(ConstInt::new_signed_truncating(n,
ity, tcx.sess.target.isize_ty)))
}
(&ty::TyUint(uty), _) |
(_, Unsigned(uty)) => {
- Ok(Integral(ConstInt::new_unsigned_truncating(n as u128,
+ Ok(Integral(ConstInt::new_unsigned_truncating(n,
uty, tcx.sess.target.usize_ty)))
}
_ => bug!()
}
}
LitKind::Float(n, fty) => {
- parse_float(&n.as_str(), fty).map(Float)
+ let mut f = parse_float(&n.as_str(), fty)?;
+ if neg {
+ f = -f;
+ }
+ Ok(Float(f))
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::TyFloat(fty) => fty,
_ => bug!()
};
- parse_float(&n.as_str(), fty).map(Float)
+ let mut f = parse_float(&n.as_str(), fty)?;
+ if neg {
+ f = -f;
+ }
+ Ok(Float(f))
}
LitKind::Bool(b) => Ok(Bool(b)),
LitKind::Char(c) => Ok(Char(c)),
})
}
-pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal)
- -> Result<Ordering, ErrorReported>
-{
- let result = match (a, b) {
+pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option<Ordering> {
+ trace!("compare_const_vals: {:?}, {:?}", a, b);
+ use rustc::mir::interpret::{Value, PrimVal};
+ match (a, b) {
(&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
- (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
- (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
- (&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
- (&ByteStr(a), &ByteStr(b)) => Some(a.data.cmp(b.data)),
(&Char(a), &Char(b)) => Some(a.cmp(&b)),
+ (&Value(Value::ByVal(PrimVal::Bytes(a))),
+ &Value(Value::ByVal(PrimVal::Bytes(b)))) => {
+ Some(if ty.is_signed() {
+ (a as i128).cmp(&(b as i128))
+ } else {
+ a.cmp(&b)
+ })
+ },
+ _ if a == b => Some(Ordering::Equal),
_ => None,
- };
-
- match result {
- Some(result) => Ok(result),
- None => {
- // FIXME: can this ever be reached?
- tcx.sess.delay_span_bug(span,
- &format!("type mismatch comparing {:?} and {:?}", a, b));
- Err(ErrorReported)
- }
}
}
impl<'a, 'tcx> ConstContext<'a, 'tcx> {
pub fn compare_lit_exprs(&self,
- span: Span,
a: &'tcx Expr,
- b: &'tcx Expr) -> Result<Ordering, ErrorReported> {
+ b: &'tcx Expr) -> Result<Option<Ordering>, ErrorReported> {
let tcx = self.tcx;
+ let ty = self.tables.expr_ty(a);
let a = match self.eval(a) {
Ok(a) => a,
Err(e) => {
return Err(ErrorReported);
}
};
- compare_const_vals(tcx, span, &a.val, &b.val)
+ Ok(compare_const_vals(&a.val, &b.val, ty))
}
}
use eval;
-use rustc::middle::const_val::{ConstEvalErr, ConstVal};
+use rustc::middle::const_val::{ConstEvalErr, ConstVal, ConstAggregate};
use rustc::mir::{Field, BorrowKind, Mutability};
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
use rustc::ty::subst::{Substs, Kind};
use rustc::hir::{self, PatKind, RangeEnd};
},
}
-fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
- match *value {
+fn print_const_val(value: &ty::Const, f: &mut fmt::Formatter) -> fmt::Result {
+ match value.val {
ConstVal::Float(ref x) => write!(f, "{}", x),
ConstVal::Integral(ref i) => write!(f, "{}", i),
ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]),
ConstVal::ByteStr(b) => write!(f, "{:?}", b.data),
ConstVal::Bool(b) => write!(f, "{:?}", b),
ConstVal::Char(c) => write!(f, "{:?}", c),
+ ConstVal::Value(v) => print_miri_value(v, value.ty, f),
ConstVal::Variant(_) |
ConstVal::Function(..) |
ConstVal::Aggregate(_) |
- ConstVal::Value(_) |
ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value)
}
}
+fn print_miri_value(value: Value, ty: Ty, f: &mut fmt::Formatter) -> fmt::Result {
+ use rustc::ty::TypeVariants::*;
+ match (value, &ty.sty) {
+ (Value::ByVal(PrimVal::Bytes(0)), &TyBool) => write!(f, "false"),
+ (Value::ByVal(PrimVal::Bytes(1)), &TyBool) => write!(f, "true"),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyUint(..)) => write!(f, "{:?}", n),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyInt(..)) => write!(f, "{:?}", n as i128),
+ (Value::ByVal(PrimVal::Bytes(n)), &TyChar) =>
+ write!(f, "{:?}", ::std::char::from_u32(n as u32).unwrap()),
+ _ => bug!("{:?}: {} not printable in a pattern", value, ty),
+ }
+}
+
impl<'tcx> fmt::Display for Pattern<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.kind {
write!(f, "{}", subpattern)
}
PatternKind::Constant { value } => {
- print_const_val(&value.val, f)
+ print_const_val(value, f)
}
PatternKind::Range { lo, hi, end } => {
- print_const_val(&lo.val, f)?;
+ print_const_val(lo, f)?;
match end {
RangeEnd::Included => write!(f, "...")?,
RangeEnd::Excluded => write!(f, "..")?,
}
- print_const_val(&hi.val, f)
+ print_const_val(hi, f)
}
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
}
PatKind::Path(ref qpath) => {
- return self.lower_path(qpath, pat.hir_id, pat.id, pat.span);
+ return self.lower_path(qpath, pat.hir_id, pat.span);
}
PatKind::Ref(ref subpattern, _) |
ty::TyArray(_, len) => {
// fixed-length array
- let len = len.val.to_const_int().unwrap().to_u64().unwrap();
+ let len = len.val.unwrap_u64();
assert!(len >= prefix.len() as u64 + suffix.len() as u64);
PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix }
}
fn lower_path(&mut self,
qpath: &hir::QPath,
id: hir::HirId,
- pat_id: ast::NodeId,
span: Span)
-> Pattern<'tcx> {
let ty = self.tables.node_id_to_type(id);
let kind = match def {
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
let substs = self.tables.node_substs(id);
- match eval::lookup_const_by_id(self.tcx, self.param_env.and((def_id, substs))) {
- Some((def_id, substs)) => {
- // Enter the inlined constant's tables&substs temporarily.
- let old_tables = self.tables;
- let old_substs = self.substs;
- self.tables = self.tcx.typeck_tables_of(def_id);
- self.substs = substs;
- let body = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
- self.tcx.hir.body(self.tcx.hir.body_owned_by(id))
- } else {
- self.tcx.extern_const_body(def_id).body
- };
- let pat = self.lower_const_expr(&body.value, pat_id, span);
- self.tables = old_tables;
- self.substs = old_substs;
- return pat;
- }
- None => {
- self.errors.push(if is_associated_const {
- PatternError::AssociatedConstInPattern(span)
- } else {
- PatternError::StaticInPattern(span)
- });
+ match self.tcx.at(span).const_eval(self.param_env.and((def_id, substs))) {
+ Ok(value) => {
+ if self.tcx.sess.opts.debugging_opts.miri {
+ if let ConstVal::Value(_) = value.val {} else {
+ panic!("const eval produced non-miri value: {:#?}", value);
+ }
+ }
+ let instance = ty::Instance::resolve(
+ self.tcx,
+ self.param_env,
+ def_id,
+ substs,
+ ).unwrap();
+ return self.const_to_pat(instance, value, span)
+ },
+ Err(e) => {
+ self.errors.push(PatternError::ConstEval(e));
PatternKind::Wild
}
}
}
fn lower_lit(&mut self, expr: &'tcx hir::Expr) -> PatternKind<'tcx> {
+ if self.tcx.sess.opts.debugging_opts.miri {
+ return match expr.node {
+ hir::ExprLit(ref lit) => {
+ let ty = self.tables.expr_ty(expr);
+ match ::eval::lit_to_const(&lit.node, self.tcx, ty, false) {
+ Ok(value) => PatternKind::Constant {
+ value: self.tcx.mk_const(ty::Const {
+ ty,
+ val: value,
+ }),
+ },
+ Err(e) => {
+ self.errors.push(PatternError::ConstEval(ConstEvalErr {
+ span: lit.span,
+ kind: e,
+ }));
+ PatternKind::Wild
+ },
+ }
+ },
+ hir::ExprPath(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
+ hir::ExprUnary(hir::UnNeg, ref expr) => {
+ let ty = self.tables.expr_ty(expr);
+ let lit = match expr.node {
+ hir::ExprLit(ref lit) => lit,
+ _ => span_bug!(expr.span, "not a literal: {:?}", expr),
+ };
+ match ::eval::lit_to_const(&lit.node, self.tcx, ty, true) {
+ Ok(value) => PatternKind::Constant {
+ value: self.tcx.mk_const(ty::Const {
+ ty,
+ val: value,
+ }),
+ },
+ Err(e) => {
+ self.errors.push(PatternError::ConstEval(ConstEvalErr {
+ span: lit.span,
+ kind: e,
+ }));
+ PatternKind::Wild
+ },
+ }
+ }
+ _ => span_bug!(expr.span, "not a literal: {:?}", expr),
+ }
+ }
let const_cx = eval::ConstContext::new(self.tcx,
self.param_env.and(self.substs),
self.tables);
}
}
- fn lower_const_expr(&mut self,
- expr: &'tcx hir::Expr,
- pat_id: ast::NodeId,
- span: Span)
- -> Pattern<'tcx> {
- let pat_ty = self.tables.expr_ty(expr);
- debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
- match pat_ty.sty {
+ fn const_to_pat(
+ &self,
+ instance: ty::Instance<'tcx>,
+ cv: &'tcx ty::Const<'tcx>,
+ span: Span,
+ ) -> Pattern<'tcx> {
+ debug!("const_to_pat: cv={:#?}", cv);
+ let kind = match cv.ty.sty {
ty::TyFloat(_) => {
self.tcx.sess.span_err(span, "floating point constants cannot be used in patterns");
+ PatternKind::Wild
}
ty::TyAdt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
+ PatternKind::Wild
}
+ ty::TyAdt(adt_def, _) if !self.tcx.has_attr(adt_def.did, "structural_match") => {
+ let msg = format!("to use a constant of type `{}` in a pattern, \
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+ self.tcx.item_path_str(adt_def.did),
+ self.tcx.item_path_str(adt_def.did));
+ self.tcx.sess.span_err(span, &msg);
+ PatternKind::Wild
+ },
+ ty::TyAdt(adt_def, substs) if adt_def.is_enum() => {
+ match cv.val {
+ ConstVal::Value(val) => {
+ let discr = self.tcx.const_discr(self.param_env.and((
+ instance, val, cv.ty
+ ))).unwrap();
+ let variant_index = adt_def
+ .discriminants(self.tcx)
+ .position(|var| var.to_u128_unchecked() == discr)
+ .unwrap();
+ PatternKind::Variant {
+ adt_def,
+ substs,
+ variant_index,
+ subpatterns: adt_def
+ .variants[variant_index]
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(i, _)| {
+ let field = Field::new(i);
+ let val = match cv.val {
+ ConstVal::Value(miri) => self.tcx.const_val_field(
+ self.param_env.and((instance, field, miri, cv.ty)),
+ ).unwrap(),
+ _ => bug!("{:#?} is not a valid tuple", cv),
+ };
+ FieldPattern {
+ field,
+ pattern: self.const_to_pat(instance, val, span),
+ }
+ }).collect(),
+ }
+ },
+ ConstVal::Variant(var_did) => {
+ let variant_index = adt_def
+ .variants
+ .iter()
+ .position(|var| var.did == var_did)
+ .unwrap();
+ PatternKind::Variant {
+ adt_def,
+ substs,
+ variant_index,
+ subpatterns: Vec::new(),
+ }
+ }
+ _ => return Pattern {
+ span,
+ ty: cv.ty,
+ kind: Box::new(PatternKind::Constant {
+ value: cv,
+ }),
+ }
+ }
+ },
ty::TyAdt(adt_def, _) => {
- if !self.tcx.has_attr(adt_def.did, "structural_match") {
- let msg = format!("to use a constant of type `{}` in a pattern, \
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- self.tcx.item_path_str(adt_def.did),
- self.tcx.item_path_str(adt_def.did));
- self.tcx.sess.span_err(span, &msg);
+ let struct_var = adt_def.struct_variant();
+ PatternKind::Leaf {
+ subpatterns: struct_var.fields.iter().enumerate().map(|(i, f)| {
+ let field = Field::new(i);
+ let val = match cv.val {
+ ConstVal::Aggregate(ConstAggregate::Struct(consts)) => {
+ consts.iter().find(|&&(name, _)| name == f.name).unwrap().1
+ },
+ ConstVal::Value(miri) => self.tcx.const_val_field(
+ self.param_env.and((instance, field, miri, cv.ty)),
+ ).unwrap(),
+ _ => bug!("{:#?} is not a valid tuple", cv),
+ };
+ FieldPattern {
+ field,
+ pattern: self.const_to_pat(instance, val, span),
+ }
+ }).collect()
}
}
- _ => { }
- }
- let kind = match expr.node {
- hir::ExprTup(ref exprs) => {
+ ty::TyTuple(fields, _) => {
PatternKind::Leaf {
- subpatterns: exprs.iter().enumerate().map(|(i, expr)| {
+ subpatterns: (0..fields.len()).map(|i| {
+ let field = Field::new(i);
+ let val = match cv.val {
+ ConstVal::Aggregate(ConstAggregate::Tuple(consts)) => consts[i],
+ ConstVal::Value(miri) => self.tcx.const_val_field(
+ self.param_env.and((instance, field, miri, cv.ty)),
+ ).unwrap(),
+ _ => bug!("{:#?} is not a valid tuple", cv),
+ };
FieldPattern {
- field: Field::new(i),
- pattern: self.lower_const_expr(expr, pat_id, span)
+ field,
+ pattern: self.const_to_pat(instance, val, span),
}
}).collect()
}
}
-
- hir::ExprCall(ref callee, ref args) => {
- let qpath = match callee.node {
- hir::ExprPath(ref qpath) => qpath,
- _ => bug!()
- };
- let ty = self.tables.node_id_to_type(callee.hir_id);
- let def = self.tables.qpath_def(qpath, callee.hir_id);
- match def {
- Def::Fn(..) | Def::Method(..) => self.lower_lit(expr),
- _ => {
- let subpatterns = args.iter().enumerate().map(|(i, expr)| {
- FieldPattern {
- field: Field::new(i),
- pattern: self.lower_const_expr(expr, pat_id, span)
- }
- }).collect();
- self.lower_variant_or_leaf(def, ty, subpatterns)
- }
+ ty::TyArray(_, n) => {
+ PatternKind::Leaf {
+ subpatterns: (0..n.val.unwrap_u64()).map(|i| {
+ let i = i as usize;
+ let field = Field::new(i);
+ let val = match cv.val {
+ ConstVal::Aggregate(ConstAggregate::Array(consts)) => consts[i],
+ ConstVal::Aggregate(ConstAggregate::Repeat(cv, _)) => cv,
+ ConstVal::Value(miri) => self.tcx.const_val_field(
+ self.param_env.and((instance, field, miri, cv.ty)),
+ ).unwrap(),
+ _ => bug!("{:#?} is not a valid tuple", cv),
+ };
+ FieldPattern {
+ field,
+ pattern: self.const_to_pat(instance, val, span),
+ }
+ }).collect()
}
}
-
- hir::ExprStruct(ref qpath, ref fields, None) => {
- let def = self.tables.qpath_def(qpath, expr.hir_id);
- let adt_def = match pat_ty.sty {
- ty::TyAdt(adt_def, _) => adt_def,
- _ => {
- span_bug!(
- expr.span,
- "struct expr without ADT type");
- }
- };
- let variant_def = adt_def.variant_of_def(def);
-
- let subpatterns =
- fields.iter()
- .map(|field| {
- let index = variant_def.index_of_field_named(field.name.node);
- let index = index.unwrap_or_else(|| {
- span_bug!(
- expr.span,
- "no field with name {:?}",
- field.name);
- });
- FieldPattern {
- field: Field::new(index),
- pattern: self.lower_const_expr(&field.expr, pat_id, span),
- }
- })
- .collect();
-
- self.lower_variant_or_leaf(def, pat_ty, subpatterns)
- }
-
- hir::ExprArray(ref exprs) => {
- let pats = exprs.iter()
- .map(|expr| self.lower_const_expr(expr, pat_id, span))
- .collect();
- PatternKind::Array {
- prefix: pats,
- slice: None,
- suffix: vec![]
+ _ => {
+ PatternKind::Constant {
+ value: cv,
}
- }
-
- hir::ExprPath(ref qpath) => {
- return self.lower_path(qpath, expr.hir_id, pat_id, span);
- }
-
- _ => self.lower_lit(expr)
+ },
};
Pattern {
span,
- ty: pat_ty,
+ ty: cv.ty,
kind: Box::new(kind),
}
}
}
}
+impl<CTX> HashStable<CTX> for ::std::cmp::Ordering {
+ fn hash_stable<W: StableHasherResult>(&self,
+ ctx: &mut CTX,
+ hasher: &mut StableHasher<W>) {
+ (*self as i8).hash_stable(ctx, hasher);
+ }
+}
+
impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) {
fn hash_stable<W: StableHasherResult>(&self,
ctx: &mut CTX,
use rustc::ty::layout::{self, LayoutOf};
use middle::const_val::ConstVal;
use rustc_const_eval::ConstContext;
+use rustc::mir::interpret::{Value, PrimVal};
use util::nodemap::FxHashSet;
use lint::{LateContext, LintContext, LintArray};
use lint::{LintPass, LateLintPass};
.map(|i| i >= bits)
.unwrap_or(true)
}
+ Ok(&ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
+ ty,
+ }) => {
+ if ty.is_signed() {
+ (b as i128) < 0
+ } else {
+ b >= bits as u128
+ }
+ }
_ => false,
}
};
impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> {
const MAX1: usize = usize::max_value() - 1;
- let mut interpret_interner = self.tcx.unwrap().interpret_interner.borrow_mut();
+ let tcx = self.tcx;
+ let interpret_interner = || tcx.unwrap().interpret_interner.borrow_mut();
let pos = self.position();
- match self.read_usize()? {
+ match usize::decode(self)? {
::std::usize::MAX => {
+ let id = interpret_interner().reserve();
+ let alloc_id = interpret::AllocId(id);
+ trace!("creating alloc id {:?} at {}", alloc_id, pos);
+ // insert early to allow recursive allocs
+ self.interpret_alloc_cache.insert(pos, alloc_id);
+
let allocation = interpret::Allocation::decode(self)?;
- let id = interpret_interner.reserve();
+ trace!("decoded alloc {:?} {:#?}", alloc_id, allocation);
let allocation = self.tcx.unwrap().intern_const_alloc(allocation);
- interpret_interner.intern_at_reserved(id, allocation);
- let id = interpret::AllocId(id);
- self.interpret_alloc_cache.insert(pos, id);
+ interpret_interner().intern_at_reserved(id, allocation);
let num = usize::decode(self)?;
let ptr = interpret::Pointer {
primval: interpret::PrimVal::Ptr(interpret::MemoryPointer {
- alloc_id: id,
+ alloc_id,
offset: 0,
}),
};
for _ in 0..num {
let glob = interpret::GlobalId::decode(self)?;
- interpret_interner.cache(glob, ptr);
+ interpret_interner().cache(glob, ptr);
}
- Ok(id)
+ Ok(alloc_id)
},
MAX1 => {
+ trace!("creating fn alloc id at {}", pos);
let instance = ty::Instance::decode(self)?;
- let id = interpret::AllocId(interpret_interner.create_fn_alloc(instance));
+ trace!("decoded fn alloc instance: {:?}", instance);
+ let id = interpret::AllocId(interpret_interner().create_fn_alloc(instance));
+ trace!("created fn alloc id: {:?}", id);
self.interpret_alloc_cache.insert(pos, id);
Ok(id)
},
- shorthand => Ok(self.interpret_alloc_cache[&shorthand]),
+ shorthand => {
+ trace!("loading shorthand {}", shorthand);
+ if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) {
+ return Ok(alloc_id);
+ }
+ trace!("shorthand {} not cached, loading entire allocation", shorthand);
+ // need to load allocation
+ self.with_position(shorthand, |this| interpret::AllocId::decode(this))
+ },
}
}
}
impl<'a, 'tcx> SpecializedEncoder<interpret::AllocId> for EncodeContext<'a, 'tcx> {
fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> {
+ trace!("encoding {:?} at {}", alloc_id, self.position());
if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() {
- return self.emit_usize(shorthand);
+ trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand);
+ return shorthand.encode(self);
}
let start = self.position();
+ // cache the allocation shorthand now, because the allocation itself might recursively
+ // point to itself.
+ self.interpret_alloc_shorthands.insert(*alloc_id, start);
let interpret_interner = self.tcx.interpret_interner.borrow();
if let Some(alloc) = interpret_interner.get_alloc(alloc_id.0) {
+ trace!("encoding {:?} with {:#?}", alloc_id, alloc);
usize::max_value().encode(self)?;
alloc.encode(self)?;
let globals = interpret_interner.get_globals(interpret::Pointer {
glob.encode(self)?;
}
} else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) {
+ trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
(usize::max_value() - 1).encode(self)?;
fn_instance.encode(self)?;
} else {
bug!("alloc id without corresponding allocation: {}", alloc_id.0);
}
- let len = self.position() - start * 7;
- // Check that the shorthand is a not longer than the
- // full encoding itself, i.e. it's an obvious win.
- assert!(len >= 64 || (start as u64) < (1 << len));
- self.interpret_alloc_shorthands.insert(*alloc_id, start);
Ok(())
}
}
use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor};
use std::fmt;
// constraints on `'a` and `'b`. These constraints
// would be lost if we just look at the normalized
// value.
- if let ConstVal::Function(def_id, ..) = value.val {
+ let did = match value.val {
+ ConstVal::Function(def_id, ..) => Some(def_id),
+ ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
+ self.tcx()
+ .interpret_interner
+ .borrow()
+ .get_fn(p.alloc_id.0)
+ .map(|instance| instance.def_id())
+ },
+ ConstVal::Value(Value::ByVal(PrimVal::Undef)) => {
+ match value.ty.sty {
+ ty::TyFnDef(ty_def_id, _) => Some(ty_def_id),
+ _ => None,
+ }
+ },
+ _ => None,
+ };
+ if let Some(def_id) = did {
let tcx = self.tcx();
let type_checker = &mut self.cx;
ProjectionElem::Subslice { from, to } => PlaceTy::Ty {
ty: match base_ty.sty {
ty::TyArray(inner, size) => {
- let size = size.val.to_const_int().unwrap().to_u64().unwrap();
+ let size = size.val.unwrap_u64();
let min_size = (from as u64) + (to as u64);
if let Some(rest_size) = size.checked_sub(min_size) {
tcx.mk_array(inner, rest_size)
Literal::Value {
value:
&ty::Const {
- val: ConstVal::Function(def_id, _),
- ..
+ val,
+ ty,
},
..
},
..
- }) => Some(def_id) == self.tcx().lang_items().box_free_fn(),
+ }) => match val {
+ ConstVal::Function(def_id, _) => {
+ Some(def_id) == self.tcx().lang_items().box_free_fn()
+ },
+ ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
+ let inst = self.tcx().interpret_interner.borrow().get_fn(p.alloc_id.0);
+ inst.map_or(false, |inst| {
+ Some(inst.def_id()) == self.tcx().lang_items().box_free_fn()
+ })
+ },
+ ConstVal::Value(Value::ByVal(PrimVal::Undef)) => {
+ match ty.sty {
+ ty::TyFnDef(ty_def_id, _) => {
+ Some(ty_def_id) == self.tcx().lang_items().box_free_fn()
+ }
+ _ => false,
+ }
+ }
+ _ => false,
+ }
_ => false,
}
}
use rustc::middle::region;
use rustc::ty::{self, Ty};
use rustc::mir::*;
+use rustc::mir::interpret::{Value, PrimVal};
use syntax::ast;
use syntax_pos::Span;
ty: this.hir.tcx().types.u32,
literal: Literal::Value {
value: this.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(ConstInt::U32(0)),
+ val: if this.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
+ } else {
+ ConstVal::Integral(ConstInt::U32(0))
+ },
ty: this.hir.tcx().types.u32
}),
},
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(val),
+ val: if self.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.to_u128_unchecked())))
+ } else {
+ ConstVal::Integral(val)
+ },
ty
})
}
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(val),
+ val: if self.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(
+ val.to_u128_unchecked()
+ )))
+ } else {
+ ConstVal::Integral(val)
+ },
ty
})
}
// test the branches of enum
SwitchInt {
switch_ty: Ty<'tcx>,
- options: Vec<&'tcx ty::Const<'tcx>>,
+ options: Vec<u128>,
indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
},
use rustc::ty::{self, Ty};
use rustc::ty::util::IntTypeExt;
use rustc::mir::*;
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::hir::RangeEnd;
use syntax_pos::Span;
use std::cmp::Ordering;
test_place: &Place<'tcx>,
candidate: &Candidate<'pat, 'tcx>,
switch_ty: Ty<'tcx>,
- options: &mut Vec<&'tcx ty::Const<'tcx>>,
+ options: &mut Vec<u128>,
indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>)
-> bool
{
indices.entry(value)
.or_insert_with(|| {
- options.push(value);
+ options.push(value.val.to_u128().expect("switching on int"));
options.len() - 1
});
true
let tcx = self.hir.tcx();
for (idx, discr) in adt_def.discriminants(tcx).enumerate() {
target_blocks.place_back() <- if variants.contains(idx) {
- values.push(discr);
+ values.push(discr.to_u128_unchecked());
*(targets.place_back() <- self.cfg.start_new_block())
} else {
if otherwise_block.is_none() {
assert!(options.len() > 0 && options.len() <= 2);
let (true_bb, false_bb) = (self.cfg.start_new_block(),
self.cfg.start_new_block());
- let ret = match options[0].val {
- ConstVal::Bool(true) => vec![true_bb, false_bb],
- ConstVal::Bool(false) => vec![false_bb, true_bb],
+ let ret = match options[0] {
+ 1 => vec![true_bb, false_bb],
+ 0 => vec![false_bb, true_bb],
v => span_bug!(test.span, "expected boolean value but got {:?}", v)
};
(ret, TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place.clone()),
.map(|_| self.cfg.start_new_block())
.chain(Some(otherwise))
.collect();
- let values: Vec<_> = options.iter().map(|v|
- v.val.to_const_int().expect("switching on integral")
- ).collect();
(targets.clone(), TerminatorKind::SwitchInt {
discr: Operand::Copy(place.clone()),
switch_ty,
- values: From::from(values),
+ values: options.clone().into(),
targets,
})
};
let tcx = self.hir.tcx();
let mut val = Operand::Copy(place.clone());
+ let bytes = match value.val {
+ ConstVal::ByteStr(bytes) => Some(bytes.data),
+ ConstVal::Value(Value::ByVal(PrimVal::Ptr(p))) => {
+ let is_array_ptr = ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|t| t.ty.builtin_index())
+ .map_or(false, |t| t == self.hir.tcx().types.u8);
+ if is_array_ptr {
+ self.hir
+ .tcx()
+ .interpret_interner
+ .borrow()
+ .get_alloc(p.alloc_id.0)
+ .map(|alloc| &alloc.bytes[..])
+ } else {
+ None
+ }
+ },
+ _ => None,
+ };
// If we're using b"..." as a pattern, we need to insert an
// unsizing coercion, as the byte string has the type &[u8; N].
//
// We want to do this even when the scrutinee is a reference to an
// array, so we can call `<[u8]>::eq` rather than having to find an
// `<[u8; N]>::eq`.
- let (expect, val) = if let ConstVal::ByteStr(bytes) = value.val {
- let array_ty = tcx.mk_array(tcx.types.u8, bytes.data.len() as u64);
+ let expect = if let Some(bytes) = bytes {
+ let tcx = self.hir.tcx();
+
+ // Unsize the place to &[u8], too, if necessary.
+ if let ty::TyRef(region, mt) = ty.sty {
+ if let ty::TyArray(_, _) = mt.ty.sty {
+ ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
+ let val_slice = self.temp(ty, test.span);
+ self.cfg.push_assign(block, source_info, &val_slice,
+ Rvalue::Cast(CastKind::Unsize, val, ty));
+ val = Operand::Move(val_slice);
+ }
+ }
+
+ assert!(ty.is_slice());
+
+ let array_ty = tcx.mk_array(tcx.types.u8, bytes.len() as u64);
let array_ref = tcx.mk_imm_ref(tcx.types.re_static, array_ty);
let array = self.literal_operand(test.span, array_ref, Literal::Value {
value
// Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
let fail = self.cfg.start_new_block();
- let ty = expect.ty(&self.local_decls, tcx);
- if let ty::TyRef(_, mt) = ty.sty {
- assert!(ty.is_slice());
+ let str_or_bytestr = ty
+ .builtin_deref(true, ty::NoPreference)
+ .and_then(|tam| match tam.ty.sty {
+ ty::TyStr => Some(tam.ty),
+ ty::TySlice(inner) if inner == self.hir.tcx().types.u8 => Some(tam.ty),
+ _ => None,
+ });
+ if let Some(ty) = str_or_bytestr {
let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap();
- let ty = mt.ty;
let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty]);
let bool_ty = self.hir.bool_ty();
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::mir::*;
use syntax::ast;
ty::TyChar => {
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Char('\0'),
+ val: if self.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
+ } else {
+ ConstVal::Char('\0')
+ },
ty
})
}
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(val),
+ val: if self.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
+ } else {
+ ConstVal::Integral(val)
+ },
ty
})
}
Literal::Value {
value: self.hir.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(val),
+ val: if self.hir.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
+ } else {
+ ConstVal::Integral(val)
+ },
ty
})
}
use hair::*;
use rustc_data_structures::indexed_vec::Idx;
-use rustc_const_math::ConstInt;
use hair::cx::Cx;
use hair::cx::block;
use hair::cx::to_ref::ToRef;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, AdtKind, VariantDef, Ty};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::ty::cast::CastKind as TyCastKind;
use rustc::hir;
use rustc::hir::def_id::LocalDefId;
ExprKind::Deref { arg: expr.to_ref() }
}
Adjust::Deref(Some(deref)) => {
- let call = deref.method_call(cx.tcx, expr.ty);
+ let call = deref.method_call(cx.tcx(), expr.ty);
expr = Expr {
temp_lifetime,
}
}
- hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) },
+ hir::ExprLit(ref lit) => ExprKind::Literal {
+ literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
+ },
hir::ExprBinary(op, ref lhs, ref rhs) => {
if cx.tables().is_method_call(expr) {
if cx.tables().is_method_call(expr) {
overloaded_operator(cx, expr, vec![arg.to_ref()])
} else {
- // FIXME runtime-overflow
- if let hir::ExprLit(_) = arg.node {
- ExprKind::Literal { literal: cx.const_eval_literal(expr) }
+ if let hir::ExprLit(ref lit) = arg.node {
+ ExprKind::Literal {
+ literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
+ }
} else {
ExprKind::Unary {
op: UnOp::Neg,
let def_id = cx.tcx.hir.body_owner_def_id(count);
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id);
let count = match cx.tcx.at(c.span).const_eval(cx.param_env.and((def_id, substs))) {
- Ok(&ty::Const { val: ConstVal::Integral(ConstInt::Usize(u)), .. }) => u,
- Ok(other) => bug!("constant evaluation of repeat count yielded {:?}", other),
+ Ok(cv) => cv.val.unwrap_usize(cx.tcx),
Err(s) => cx.fatal_const_eval_err(&s, c.span, "expression")
};
span: expr.span,
kind: ExprKind::Literal {
literal: Literal::Value {
- value: cx.tcx.mk_const(ty::Const {
- val: ConstVal::Function(def_id, substs),
+ value: cx.tcx().mk_const(ty::Const {
+ val: const_fn(cx.tcx, def_id, substs),
ty
}),
},
}
}
+fn const_fn<'a, 'gcx, 'tcx>(
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ def_id: DefId,
+ substs: &'tcx Substs<'tcx>,
+) -> ConstVal<'tcx> {
+ if tcx.sess.opts.debugging_opts.miri {
+ /*
+ let inst = ty::Instance::new(def_id, substs);
+ let ptr = tcx
+ .interpret_interner
+ .borrow_mut()
+ .create_fn_alloc(inst);
+ let ptr = MemoryPointer::new(AllocId(ptr), 0);
+ ConstVal::Value(Value::ByVal(PrimVal::Ptr(ptr)))
+ */
+ // ZST function type
+ ConstVal::Value(Value::ByVal(PrimVal::Undef))
+ } else {
+ ConstVal::Function(def_id, substs)
+ }
+}
+
fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &'tcx hir::Expr,
def: Def)
Def::VariantCtor(def_id, CtorKind::Fn) => ExprKind::Literal {
literal: Literal::Value {
value: cx.tcx.mk_const(ty::Const {
- val: ConstVal::Function(def_id, substs),
+ val: const_fn(cx.tcx.global_tcx(), def_id, substs),
ty: cx.tables().node_id_to_type(expr.hir_id)
}),
},
use hair::*;
use rustc::middle::const_val::{ConstEvalErr, ConstVal};
-use rustc_const_eval::ConstContext;
use rustc_data_structures::indexed_vec::Idx;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir;
use rustc_const_math::{ConstInt, ConstUsize};
use rustc_data_structures::sync::Lrc;
+use rustc::mir::interpret::{Value, PrimVal};
#[derive(Clone)]
pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
Ok(val) => {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Integral(ConstInt::Usize(val)),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(value as u128)))
+ } else {
+ ConstVal::Integral(ConstInt::Usize(val))
+ },
ty: self.tcx.types.usize
})
}
pub fn true_literal(&mut self) -> Literal<'tcx> {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Bool(true),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(1)))
+ } else {
+ ConstVal::Bool(true)
+ },
ty: self.tcx.types.bool
})
}
pub fn false_literal(&mut self) -> Literal<'tcx> {
Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Bool(false),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(0)))
+ } else {
+ ConstVal::Bool(false)
+ },
ty: self.tcx.types.bool
})
}
}
- pub fn const_eval_literal(&mut self, e: &hir::Expr) -> Literal<'tcx> {
+ pub fn const_eval_literal(
+ &mut self,
+ lit: &'tcx ast::LitKind,
+ ty: Ty<'tcx>,
+ sp: Span,
+ neg: bool,
+ ) -> Literal<'tcx> {
let tcx = self.tcx.global_tcx();
- let const_cx = ConstContext::new(tcx,
- self.param_env.and(self.identity_substs),
- self.tables());
- match const_cx.eval(tcx.hir.expect_expr(e.id)) {
- Ok(value) => Literal::Value { value },
- Err(s) => self.fatal_const_eval_err(&s, e.span, "expression")
+
+ let mut repr_ty = ty;
+ if let ty::TyAdt(adt, _) = ty.sty {
+ if adt.is_enum() {
+ repr_ty = adt.repr.discr_type().to_ty(tcx)
+ }
+ }
+
+ let parse_float = |num: &str, fty| -> ConstFloat {
+ ConstFloat::from_str(num, fty).unwrap_or_else(|_| {
+ // FIXME(#31407) this is only necessary because float parsing is buggy
+ tcx.sess.span_fatal(sp, "could not evaluate float literal (see issue #31407)");
+ })
+ };
+
+ if tcx.sess.opts.debugging_opts.miri {
+ use rustc::mir::interpret::*;
+ let lit = match *lit {
+ LitKind::Str(ref s, _) => {
+ let s = s.as_str();
+ let id = self.tcx.allocate_cached(s.as_bytes());
+ let ptr = MemoryPointer::new(AllocId(id), 0);
+ Value::ByValPair(
+ PrimVal::Ptr(ptr),
+ PrimVal::from_u128(s.len() as u128),
+ )
+ },
+ LitKind::ByteStr(ref data) => {
+ let id = self.tcx.allocate_cached(data);
+ let ptr = MemoryPointer::new(AllocId(id), 0);
+ Value::ByVal(PrimVal::Ptr(ptr))
+ },
+ LitKind::Byte(n) => Value::ByVal(PrimVal::Bytes(n as u128)),
+ LitKind::Int(n, _) if neg => {
+ let n = n as i128;
+ let n = n.overflowing_neg().0;
+ Value::ByVal(PrimVal::Bytes(n as u128))
+ },
+ LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)),
+ LitKind::Float(n, fty) => {
+ let n = n.as_str();
+ let mut f = parse_float(&n, fty);
+ if neg {
+ f = -f;
+ }
+ let bits = f.bits;
+ Value::ByVal(PrimVal::Bytes(bits))
+ }
+ LitKind::FloatUnsuffixed(n) => {
+ let fty = match ty.sty {
+ ty::TyFloat(fty) => fty,
+ _ => bug!()
+ };
+ let n = n.as_str();
+ let mut f = parse_float(&n, fty);
+ if neg {
+ f = -f;
+ }
+ let bits = f.bits;
+ Value::ByVal(PrimVal::Bytes(bits))
+ }
+ LitKind::Bool(b) => Value::ByVal(PrimVal::Bytes(b as u128)),
+ LitKind::Char(c) => Value::ByVal(PrimVal::Bytes(c as u128)),
+ };
+ return Literal::Value {
+ value: self.tcx.mk_const(ty::Const {
+ val: Value(lit),
+ ty,
+ }),
+ };
+ }
+
+ use syntax::ast::*;
+ use syntax::ast::LitIntType::*;
+ use rustc::middle::const_val::ConstVal::*;
+ use rustc_const_math::ConstInt::*;
+ use rustc::ty::util::IntTypeExt;
+ use rustc::middle::const_val::ByteArray;
+ use rustc_const_math::ConstFloat;
+
+ let lit = match *lit {
+ LitKind::Str(ref s, _) => Ok(Str(s.as_str())),
+ LitKind::ByteStr(ref data) => {
+ let data: &'tcx [u8] = data;
+ Ok(ByteStr(ByteArray { data }))
+ },
+ LitKind::Byte(n) => Ok(Integral(U8(n))),
+ LitKind::Int(n, hint) => {
+ match (&repr_ty.sty, hint) {
+ (&ty::TyInt(ity), _) |
+ (_, Signed(ity)) => {
+ let mut n = n as i128;
+ if neg {
+ n = n.overflowing_neg().0;
+ }
+ Ok(Integral(ConstInt::new_signed_truncating(n,
+ ity, tcx.sess.target.isize_ty)))
+ }
+ (&ty::TyUint(uty), _) |
+ (_, Unsigned(uty)) => {
+ Ok(Integral(ConstInt::new_unsigned_truncating(n,
+ uty, tcx.sess.target.usize_ty)))
+ }
+ _ => bug!()
+ }
+ }
+ LitKind::Float(n, fty) => {
+ let mut f = parse_float(&n.as_str(), fty);
+ if neg {
+ f = -f;
+ }
+ Ok(ConstVal::Float(f))
+ }
+ LitKind::FloatUnsuffixed(n) => {
+ let fty = match ty.sty {
+ ty::TyFloat(fty) => fty,
+ _ => bug!()
+ };
+ let mut f = parse_float(&n.as_str(), fty);
+ if neg {
+ f = -f;
+ }
+ Ok(ConstVal::Float(f))
+ }
+ LitKind::Bool(b) => Ok(Bool(b)),
+ LitKind::Char(c) => Ok(Char(c)),
+ };
+
+ match lit {
+ Ok(value) => Literal::Value { value: self.tcx.mk_const(ty::Const {
+ val: value,
+ ty,
+ }) },
+ Err(kind) => self.fatal_const_eval_err(&ConstEvalErr {
+ span: sp,
+ kind,
+ }, sp, "expression")
}
}
return (method_ty,
Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Function(item.def_id, substs),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ // ZST function type
+ ConstVal::Value(Value::ByVal(PrimVal::Undef))
+ } else {
+ ConstVal::Function(item.def_id, substs)
+ },
ty: method_ty
}),
});
use syntax::codemap::Span;
use rustc::mir::interpret::{EvalResult, EvalError, EvalErrorKind, GlobalId, Value, MemoryPointer, Pointer, PrimVal};
-use super::{Place, EvalContext, StackPopCleanup, ValTy};
+use super::{Place, EvalContext, StackPopCleanup, ValTy, HasMemory};
use rustc_const_math::ConstInt;
use std::fmt;
use std::error::Error;
-
pub fn mk_eval_cx<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
instance: Instance<'tcx>,
param_env: ty::ParamEnv<'tcx>,
-) -> EvalResult<'tcx, (Pointer, Ty<'tcx>)> {
+) -> EvalResult<'tcx, (Value, Pointer, Ty<'tcx>)> {
debug!("eval_body: {:?}, {:?}", instance, param_env);
let limits = super::ResourceLimits::default();
let mut ecx = EvalContext::new(tcx, param_env, limits, CompileTimeEvaluator, ());
while ecx.step()? {}
}
let alloc = tcx.interpret_interner.borrow().get_cached(cid).expect("global not cached");
- Ok((MemoryPointer::new(alloc, 0).into(), instance_ty))
+ let align = ecx.layout_of(instance_ty)?.align;
+ let ptr = MemoryPointer::new(alloc, 0).into();
+ let value = match ecx.try_read_value(ptr, align, instance_ty)? {
+ Some(val) => val,
+ _ => Value::ByRef(ptr, align),
+ };
+ Ok((value, ptr, instance_ty))
}
pub fn eval_body_as_integer<'a, 'tcx>(
param_env: ty::ParamEnv<'tcx>,
instance: Instance<'tcx>,
) -> EvalResult<'tcx, ConstInt> {
- let ptr_ty = eval_body(tcx, instance, param_env);
- let (ptr, ty) = ptr_ty?;
- let ecx = mk_eval_cx(tcx, instance, param_env)?;
- let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? {
- Some(Value::ByVal(prim)) => prim.to_bytes()?,
+ let (value, _, ty) = eval_body(tcx, instance, param_env)?;
+ let prim = match value {
+ Value::ByVal(prim) => prim.to_bytes()?,
_ => return err!(TypeNotPrimitive(ty)),
};
use syntax::ast::{IntTy, UintTy};
impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
fn into(self) -> EvalError<'tcx> {
- EvalErrorKind::MachineError(Box::new(self)).into()
+ EvalErrorKind::MachineError(self.to_string()).into()
}
}
let mir = match ecx.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
- // some simple things like `malloc` might get accepted in the future
return Err(
ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
.into(),
}
}
+pub fn const_val_field<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>,
+) -> ::rustc::middle::const_val::EvalResult<'tcx> {
+ trace!("const_val_field: {:#?}", key);
+ match const_val_field_inner(tcx, key) {
+ Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
+ val: ConstVal::Value(field),
+ ty,
+ })),
+ Err(err) => Err(ConstEvalErr {
+ span: tcx.def_span(key.value.0.def_id()),
+ kind: err.into(),
+ }),
+ }
+}
+
+fn const_val_field_inner<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, mir::Field, Value, Ty<'tcx>)>,
+) -> ::rustc::mir::interpret::EvalResult<'tcx, (Value, Ty<'tcx>)> {
+ trace!("const_val_field: {:#?}", key);
+ let (instance, field, value, ty) = key.value;
+ let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
+ let (mut field, ty) = match value {
+ Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, field, ty)?.expect("const_val_field on non-field"),
+ Value::ByRef(ptr, align) => {
+ let place = Place::from_primval_ptr(ptr, align);
+ let layout = ecx.layout_of(ty)?;
+ let (place, layout) = ecx.place_field(place, field, layout)?;
+ let (ptr, align) = place.to_ptr_align();
+ (Value::ByRef(ptr, align), layout.ty)
+ }
+ };
+ if let Value::ByRef(ptr, align) = field {
+ if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
+ field = val;
+ }
+ }
+ Ok((field, ty))
+}
+
+pub fn const_discr<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, Value, Ty<'tcx>)>,
+) -> EvalResult<'tcx, u128> {
+ trace!("const_discr: {:#?}", key);
+ let (instance, value, ty) = key.value;
+ let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
+ let (ptr, align) = match value {
+ Value::ByValPair(..) | Value::ByVal(_) => {
+ let layout = ecx.layout_of(ty)?;
+ use super::MemoryKind;
+ let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?;
+ let ptr: Pointer = ptr.into();
+ ecx.write_value_to_ptr(value, ptr, layout.align, ty)?;
+ (ptr, layout.align)
+ },
+ Value::ByRef(ptr, align) => (ptr, align),
+ };
+ let place = Place::from_primval_ptr(ptr, align);
+ ecx.read_discriminant_value(place, ty)
+}
+
pub fn const_eval_provider<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
key: ty::ParamEnvAnd<'tcx, (DefId, &'tcx Substs<'tcx>)>,
return Err(ConstEvalErr { span: body.value.span, kind: TypeckError })
}
+
+ let instance = ty::Instance::new(def_id, substs);
+ if tcx.sess.opts.debugging_opts.miri {
+ return match ::interpret::eval_body(tcx, instance, key.param_env) {
+ Ok((miri_value, _, miri_ty)) => Ok(tcx.mk_const(ty::Const {
+ val: ConstVal::Value(miri_value),
+ ty: miri_ty,
+ })),
+ Err(err) => {
+ Err(ConstEvalErr { span: body.value.span, kind: err.into() })
+ }
+ };
+ }
+
trace!("running old const eval");
let old_result = ConstContext::new(tcx, key.param_env.and(substs), tables).eval(&body.value);
trace!("old const eval produced {:?}", old_result);
- if tcx.sess.opts.debugging_opts.miri {
- let instance = ty::Instance::new(def_id, substs);
- trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
- let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
- match (miri_result, old_result) {
- (Err(err), Ok(ok)) => {
- trace!("miri failed, ctfe returned {:?}", ok);
- tcx.sess.span_warn(
- tcx.def_span(key.value.0),
- "miri failed to eval, while ctfe succeeded",
- );
- let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
- let () = unwrap_miri(&ecx, Err(err));
- Ok(ok)
- },
- (_, Err(err)) => Err(err),
- (Ok((miri_val, miri_ty)), Ok(ctfe)) => {
- let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
- let layout = ecx.layout_of(miri_ty).unwrap();
- let miri_place = Place::from_primval_ptr(miri_val, layout.align);
- check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
- Ok(ctfe)
- }
+ trace!("const eval instance: {:?}, {:?}", instance, key.param_env);
+ let miri_result = ::interpret::eval_body(tcx, instance, key.param_env);
+ match (miri_result, old_result) {
+ (Err(err), Ok(ok)) => {
+ trace!("miri failed, ctfe returned {:?}", ok);
+ tcx.sess.span_warn(
+ tcx.def_span(key.value.0),
+ "miri failed to eval, while ctfe succeeded",
+ );
+ let ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
+ let () = unwrap_miri(&ecx, Err(err));
+ Ok(ok)
+ },
+ (Ok((value, _, ty)), Err(_)) => Ok(tcx.mk_const(ty::Const {
+ val: ConstVal::Value(value),
+ ty,
+ })),
+ (Err(_), Err(err)) => Err(err),
+ (Ok((_, miri_ptr, miri_ty)), Ok(ctfe)) => {
+ let mut ecx = mk_eval_cx(tcx, instance, key.param_env).unwrap();
+ let layout = ecx.layout_of(miri_ty).unwrap();
+ let miri_place = Place::from_primval_ptr(miri_ptr, layout.align);
+ check_ctfe_against_miri(&mut ecx, miri_place, miri_ty, ctfe.val);
+ Ok(ctfe)
}
- } else {
- old_result
}
}
}
},
TyArray(elem_ty, n) => {
- let n = n.val.to_const_int().unwrap().to_u64().unwrap();
+ let n = n.val.unwrap_u64();
let vec: Vec<(ConstVal, Ty<'tcx>)> = match ctfe {
ConstVal::ByteStr(arr) => arr.data.iter().map(|&b| {
(ConstVal::Integral(ConstInt::U8(b)), ecx.tcx.types.u8)
Repeat(ref operand, _) => {
let (elem_ty, length) = match dest_ty.sty {
- ty::TyArray(elem_ty, n) => (elem_ty, n.val.to_const_int().unwrap().to_u64().unwrap()),
+ ty::TyArray(elem_ty, n) => (elem_ty, n.val.unwrap_u64()),
_ => {
bug!(
"tried to assign array-repeat to non-array type {:?}",
let ptr = self.into_ptr(src)?;
// u64 cast is from usize to u64, which is always good
let valty = ValTy {
- value: ptr.to_value_with_len(length.val.to_const_int().unwrap().to_u64().unwrap() ),
+ value: ptr.to_value_with_len(length.val.unwrap_u64() ),
ty: dest_ty,
};
self.write_value(valty, dest)
pub use self::memory::{Memory, MemoryKind, HasMemory};
-pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider};
+pub use self::const_eval::{eval_body_as_integer, eval_body, CompileTimeEvaluator, const_eval_provider, const_val_field, const_discr};
pub use self::machine::Machine;
(Not, I64) => !(bytes as i64) as u128,
(Not, I128) => !(bytes as i128) as u128,
+ (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath),
(Neg, I8) => -(bytes as i8) as u128,
+ (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath),
(Neg, I16) => -(bytes as i16) as u128,
+ (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath),
(Neg, I32) => -(bytes as i32) as u128,
+ (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath),
(Neg, I64) => -(bytes as i64) as u128,
+ (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath),
(Neg, I128) => -(bytes as i128) as u128,
(Neg, F32) => (-bytes_to_f32(bytes)).bits,
pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
match ty.sty {
- ty::TyArray(elem, n) => (elem, n.val.to_const_int().unwrap().to_u64().unwrap() as u64),
+ ty::TyArray(elem, n) => (elem, n.val.unwrap_u64() as u64),
ty::TySlice(elem) => {
match self {
}
}
+ pub fn read_field(
+ &self,
+ base: Value,
+ field: mir::Field,
+ base_ty: Ty<'tcx>,
+ ) -> EvalResult<'tcx, Option<(Value, Ty<'tcx>)>> {
+ let base_layout = self.layout_of(base_ty)?;
+ let field_index = field.index();
+ let field = base_layout.field(self, field_index)?;
+ let offset = base_layout.fields.offset(field_index);
+ match base {
+ // the field covers the entire type
+ Value::ByValPair(..) |
+ Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some((base, field.ty))),
+ // split fat pointers, 2 element tuples, ...
+ Value::ByValPair(a, b) if base_layout.fields.count() == 2 => {
+ let val = [a, b][field_index];
+ Ok(Some((Value::ByVal(val), field.ty)))
+ },
+ _ => Ok(None),
+ }
+ }
+
fn try_read_place_projection(
&mut self,
proj: &mir::PlaceProjection<'tcx>,
};
let base_ty = self.place_ty(&proj.base);
match proj.elem {
- Field(field, _) => {
- let base_layout = self.layout_of(base_ty)?;
- let field_index = field.index();
- let field = base_layout.field(&self, field_index)?;
- let offset = base_layout.fields.offset(field_index);
- match base {
- // the field covers the entire type
- Value::ByValPair(..) |
- Value::ByVal(_) if offset.bytes() == 0 && field.size == base_layout.size => Ok(Some(base)),
- // split fat pointers, 2 element tuples, ...
- Value::ByValPair(a, b) if base_layout.fields.count() == 2 => {
- let val = [a, b][field_index];
- Ok(Some(Value::ByVal(val)))
- },
- _ => Ok(None),
- }
- },
+ Field(field, _) => Ok(self.read_field(base, field, base_ty)?.map(|(f, _)| f)),
// The NullablePointer cases should work fine, need to take care for normal enums
Downcast(..) |
Subslice { .. } |
// Branch to the `otherwise` case by default, if no match is found.
let mut target_block = targets[targets.len() - 1];
- for (index, const_int) in values.iter().enumerate() {
- let prim = PrimVal::Bytes(const_int.to_u128_unchecked());
+ for (index, &const_int) in values.iter().enumerate() {
+ let prim = PrimVal::Bytes(const_int);
if discr_prim.to_bytes()? == prim.to_bytes()? {
target_block = targets[index];
break;
output.push('[');
self.push_type_name(inner_type, output);
write!(output, "; {}",
- len.val.to_const_int().unwrap().to_u64().unwrap()).unwrap();
+ len.val.unwrap_u64()).unwrap();
output.push(']');
},
ty::TySlice(inner_type) => {
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::ty::maps::Providers;
use rustc_const_math::{ConstInt, ConstUsize};
+use rustc::mir::interpret::{Value, PrimVal};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
match self_ty.sty {
_ if is_copy => builder.copy_shim(),
ty::TyArray(ty, len) => {
- let len = len.val.to_const_int().unwrap().to_u64().unwrap();
+ let len = len.val.unwrap_u64();
builder.array_shim(dest, src, ty, len)
}
ty::TyClosure(def_id, substs) => {
ty: func_ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
- val: ConstVal::Function(self.def_id, substs),
+ val: if tcx.sess.opts.debugging_opts.miri {
+ // ZST function type
+ ConstVal::Value(Value::ByVal(PrimVal::Undef))
+ } else {
+ ConstVal::Function(self.def_id, substs)
+ },
ty: func_ty
}),
},
}
fn make_usize(&self, value: u64) -> Box<Constant<'tcx>> {
- let value = ConstUsize::new(value, self.tcx.sess.target.usize_ty).unwrap();
box Constant {
span: self.span,
ty: self.tcx.types.usize,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Integral(ConstInt::Usize(value)),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(value.into())))
+ } else {
+ let value = ConstUsize::new(
+ value,
+ self.tcx.sess.target.usize_ty,
+ ).unwrap();
+ ConstVal::Integral(ConstInt::Usize(value))
+ },
ty: self.tcx.types.usize,
})
}
ty,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
- val: ConstVal::Function(def_id,
- Substs::identity_for_item(tcx, def_id)),
+ val: if tcx.sess.opts.debugging_opts.miri {
+ // ZST function type
+ ConstVal::Value(Value::ByVal(PrimVal::Undef))
+ } else {
+ ConstVal::Function(def_id, Substs::identity_for_item(tcx, def_id))
+ },
ty
}),
},
use transform::no_landing_pads::no_landing_pads;
use dataflow::{do_dataflow, DebugFormatted, state_for_location};
use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
+use rustc::mir::interpret::{Value, PrimVal};
pub struct StateTransform;
ty: self.tcx.types.u32,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
- val: ConstVal::Integral(ConstInt::U32(state_disc)),
+ val: if self.tcx.sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into())))
+ } else {
+ ConstVal::Integral(ConstInt::U32(state_disc))
+ },
ty: self.tcx.types.u32
}),
},
let switch = TerminatorKind::SwitchInt {
discr: Operand::Copy(transform.make_field(transform.state_field, tcx.types.u32)),
switch_ty: tcx.types.u32,
- values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::<Vec<_>>()),
+ values: Cow::from(cases.iter().map(|&(i, _)| i.into()).collect::<Vec<_>>()),
targets: cases.iter().map(|&(_, d)| d).chain(once(default_block)).collect(),
};
return false;
}
+ // Do not inline {u,i}128 lang items, trans const eval depends
+ // on detecting calls to these lang items and intercepting them
+ if tcx.is_binop_lang_item(callsite.callee).is_some() {
+ debug!(" not inlining 128bit integer lang item");
+ return false;
+ }
+
let trans_fn_attrs = tcx.trans_fn_attrs(callsite.callee);
let hinted = match trans_fn_attrs.inline {
_ => false
}
} else if let ty::TyArray(_, len) = ty.sty {
- len.val.to_const_int().unwrap().to_u64().unwrap() == 0 &&
+ len.val.unwrap_u64() == 0 &&
self.mode == Mode::Fn
} else {
false
TerminatorKind::SwitchInt { discr: Operand::Constant(box Constant {
literal: Literal::Value { ref value }, ..
}), ref values, ref targets, .. } => {
- if let Some(ref constint) = value.val.to_const_int() {
+ if let Some(constint) = value.val.to_u128() {
let (otherwise, targets) = targets.split_last().unwrap();
let mut ret = TerminatorKind::Goto { target: *otherwise };
- for (v, t) in values.iter().zip(targets.iter()) {
+ for (&v, t) in values.iter().zip(targets.iter()) {
if v == constint {
ret = TerminatorKind::Goto { target: *t };
break;
use std::fmt;
use rustc::hir;
use rustc::mir::*;
-use rustc::middle::const_val::{ConstInt, ConstVal};
+use rustc::middle::const_val::ConstVal;
use rustc::middle::lang_items;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::{Kind, Substs};
use rustc::ty::util::IntTypeExt;
use rustc_data_structures::indexed_vec::Idx;
use util::patch::MirPatch;
+use rustc::mir::interpret::{Value, PrimVal};
use std::{iter, u32};
variant_path,
&adt.variants[variant_index],
substs);
- values.push(discr);
+ values.push(discr.to_u128().unwrap());
if let Unwind::To(unwind) = unwind {
// We can't use the half-ladder from the original
// drop ladder, because this breaks the
fn adt_switch_block(&mut self,
adt: &'tcx ty::AdtDef,
blocks: Vec<BasicBlock>,
- values: &[ConstInt],
+ values: &[u128],
succ: BasicBlock,
unwind: Unwind)
-> BasicBlock {
self.complete_drop(Some(DropFlagMode::Deep), succ, unwind)
}
ty::TyArray(ety, size) => self.open_drop_for_array(
- ety, size.val.to_const_int().and_then(|v| v.to_u64())),
+ ety, size.val.to_u128().map(|i| i as u64)),
ty::TySlice(ety) => self.open_drop_for_array(ety, None),
_ => bug!("open drop from non-ADT `{:?}`", ty)
ty: self.tcx().types.usize,
literal: Literal::Value {
value: self.tcx().mk_const(ty::Const {
- val: ConstVal::Integral(self.tcx().const_usize(val)),
+ val: if self.tcx().sess.opts.debugging_opts.miri {
+ ConstVal::Value(Value::ByVal(PrimVal::Bytes(val.into())))
+ } else {
+ ConstVal::Integral(self.tcx().const_usize(val))
+ },
ty: self.tcx().types.usize
})
}
}
fn check_const_eval(&self, expr: &'gcx hir::Expr) {
+ if self.tcx.sess.opts.debugging_opts.miri {
+ return;
+ }
if let Err(err) = self.const_cx().eval(expr) {
match err.kind {
UnimplementedConstVal(_) => {}
self.check_const_eval(lit);
}
PatKind::Range(ref start, ref end, RangeEnd::Excluded) => {
- match self.const_cx().compare_lit_exprs(p.span, start, end) {
- Ok(Ordering::Less) => {}
- Ok(Ordering::Equal) |
- Ok(Ordering::Greater) => {
+ match self.const_cx().compare_lit_exprs(start, end) {
+ Ok(Some(Ordering::Less)) => {}
+ Ok(Some(Ordering::Equal)) |
+ Ok(Some(Ordering::Greater)) => {
span_err!(self.tcx.sess,
start.span,
E0579,
"lower range bound must be less than upper");
}
+ Ok(None) => bug!("ranges must be char or int"),
Err(ErrorReported) => {}
}
}
PatKind::Range(ref start, ref end, RangeEnd::Included) => {
- match self.const_cx().compare_lit_exprs(p.span, start, end) {
- Ok(Ordering::Less) |
- Ok(Ordering::Equal) => {}
- Ok(Ordering::Greater) => {
+ match self.const_cx().compare_lit_exprs(start, end) {
+ Ok(Some(Ordering::Less)) |
+ Ok(Some(Ordering::Equal)) => {}
+ Ok(Some(Ordering::Greater)) => {
let mut err = struct_span_err!(
self.tcx.sess,
start.span,
}
err.emit();
}
+ Ok(None) => bug!("ranges must be char or int"),
Err(ErrorReported) => {}
}
}
self.promotable = false;
}
- if self.in_fn && self.promotable {
+ if self.in_fn && self.promotable && !self.tcx.sess.opts.debugging_opts.miri {
match self.const_cx().eval(ex) {
Ok(_) => {}
Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) |
let (source, target) = cx.tcx.struct_lockstep_tails(source, target);
match (&source.sty, &target.sty) {
(&ty::TyArray(_, len), &ty::TySlice(_)) => {
- C_usize(cx, len.val.to_const_int().unwrap().to_u64().unwrap())
+ C_usize(cx, len.val.unwrap_u64())
}
(&ty::TyDynamic(..), &ty::TyDynamic(..)) => {
// For now, upcasts are limited to changes in marker
let upper_bound = match array_or_slice_type.sty {
ty::TyArray(_, len) => {
- len.val.to_const_int().unwrap().to_u64().unwrap() as c_longlong
+ len.val.unwrap_u64() as c_longlong
}
_ => -1
};
ty::TyArray(inner_type, len) => {
output.push('[');
push_debuginfo_type_name(cx, inner_type, true, output);
- output.push_str(&format!("; {}", len.val.to_const_int().unwrap().to_u64().unwrap()));
+ output.push_str(&format!("; {}", len.val.unwrap_u64()));
output.push(']');
},
ty::TySlice(inner_type) => {
use rustc::mir::{self, Location, TerminatorKind, Literal};
use rustc::mir::visit::{Visitor, PlaceContext};
use rustc::mir::traversal;
+use rustc::mir::interpret::{Value, PrimVal};
use rustc::ty;
use rustc::ty::layout::LayoutOf;
use type_of::LayoutLlvmExt;
block: mir::BasicBlock,
kind: &mir::TerminatorKind<'tcx>,
location: Location) {
- match *kind {
+ let check = match *kind {
mir::TerminatorKind::Call {
func: mir::Operand::Constant(box mir::Constant {
literal: Literal::Value {
- value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, ..
+ value: &ty::Const { val, ty }, ..
}, ..
}),
ref args, ..
- } if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() => {
+ } => match val {
+ ConstVal::Function(def_id, _) => Some((def_id, args)),
+ ConstVal::Value(Value::ByVal(PrimVal::Undef)) => match ty.sty {
+ ty::TyFnDef(did, _) => Some((did, args)),
+ _ => None,
+ },
+ _ => None,
+ }
+ _ => None,
+ };
+ if let Some((def_id, args)) = check {
+ if Some(def_id) == self.cx.ccx.tcx().lang_items().box_free_fn() {
// box_free(x) shares with `drop x` the property that it
// is not guaranteed to be statically dominated by the
// definition of x, so x must always be in an alloca.
self.visit_place(place, PlaceContext::Drop, location);
}
}
- _ => {}
}
self.super_terminator_kind(block, kind, location);
use llvm::{self, ValueRef, BasicBlockRef};
use rustc::middle::lang_items;
-use rustc::middle::const_val::{ConstEvalErr, ConstInt, ErrKind};
+use rustc::middle::const_val::{ConstEvalErr, ErrKind};
use rustc::ty::{self, TypeFoldable};
use rustc::ty::layout::{self, LayoutOf};
use rustc::traits;
if switch_ty == bx.tcx().types.bool {
let lltrue = llblock(self, targets[0]);
let llfalse = llblock(self, targets[1]);
- if let [ConstInt::U8(0)] = values[..] {
+ if let [0] = values[..] {
bx.cond_br(discr.immediate(), llfalse, lltrue);
} else {
+ assert_eq!(&values[..], &[1]);
bx.cond_br(discr.immediate(), lltrue, llfalse);
}
} else {
let (otherwise, targets) = targets.split_last().unwrap();
let switch = bx.switch(discr.immediate(),
llblock(self, *otherwise), values.len());
- for (value, target) in values.iter().zip(targets) {
- let val = Const::from_constint(bx.cx, value);
+ for (&value, target) in values.iter().zip(targets) {
+ let val = Const::from_bytes(bx.cx, value, switch_ty);
let llbb = llblock(self, *target);
bx.add_case(switch, val.llval, llbb)
}
use rustc::infer::TransNormalize;
use rustc::traits;
use rustc::mir;
+use rustc::mir::interpret::{Value as MiriValue, PrimVal};
use rustc::mir::tcx::PlaceTy;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::layout::{self, LayoutOf, Size};
use syntax_pos::Span;
use syntax::ast;
+use syntax::symbol::Symbol;
use std::fmt;
use std::ptr;
Const { llval: llval, ty: ty }
}
+ pub fn from_bytes(ccx: &CrateContext<'a, 'tcx>, b: u128, ty: Ty<'tcx>) -> Const<'tcx> {
+ let llval = match ty.sty {
+ ty::TyInt(ast::IntTy::I128) |
+ ty::TyUint(ast::UintTy::U128) => C_uint_big(Type::i128(ccx), b),
+ ty::TyInt(i) => C_int(Type::int_from_ty(ccx, i), b as i128 as i64),
+ ty::TyUint(u) => C_uint(Type::uint_from_ty(ccx, u), b as u64),
+ ty::TyBool => {
+ assert!(b <= 1);
+ C_bool(ccx, b == 1)
+ },
+ ty::TyChar => {
+ assert_eq!(b as u32 as u128, b);
+ let c = b as u32;
+ assert!(::std::char::from_u32(c).is_some());
+ C_uint(Type::char(ccx), c as u64)
+ },
+ ty::TyFloat(fty) => {
+ let llty = ccx.layout_of(ty).llvm_type(ccx);
+ let bits = match fty {
+ ast::FloatTy::F32 => C_u32(ccx, b as u32),
+ ast::FloatTy::F64 => C_u64(ccx, b as u64),
+ };
+ consts::bitcast(bits, llty)
+ },
+ ty::TyAdt(adt, _) if adt.is_enum() => {
+ use rustc::ty::util::IntTypeExt;
+ Const::from_bytes(ccx, b, adt.repr.discr_type().to_ty(ccx.tcx())).llval
+ },
+ _ => bug!("from_bytes({}, {})", b, ty),
+ };
+ Const { llval, ty }
+ }
+
/// Translate ConstVal into a LLVM constant value.
pub fn from_constval(cx: &CodegenCx<'a, 'tcx>,
cv: &ConstVal,
ty: Ty<'tcx>)
-> Const<'tcx> {
let llty = cx.layout_of(ty).llvm_type(cx);
+ trace!("from_constval: {:#?}: {}", cv, ty);
let val = match *cv {
ConstVal::Float(v) => {
let bits = match v.ty {
ConstVal::Unevaluated(..) => {
bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
}
- ConstVal::Value(_) => unimplemented!(),
+ ConstVal::Value(MiriValue::ByRef(..)) => unimplemented!("{:#?}:{}", cv, ty),
+ ConstVal::Value(MiriValue::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len))) => {
+ match ty.sty {
+ ty::TyRef(_, ref tam) => match tam.ty.sty {
+ ty::TyStr => {},
+ _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty),
+ },
+ _ => unimplemented!("non-str fat pointer: {:?}: {:?}", ptr, ty),
+ }
+ let alloc = ccx
+ .tcx()
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .expect("miri alloc not found");
+ assert_eq!(len as usize as u128, len);
+ let slice = &alloc.bytes[(ptr.offset as usize)..][..(len as usize)];
+ let s = ::std::str::from_utf8(slice)
+ .expect("non utf8 str from miri");
+ C_str_slice(ccx, Symbol::intern(s).as_str())
+ },
+ ConstVal::Value(MiriValue::ByValPair(..)) => unimplemented!(),
+ ConstVal::Value(MiriValue::ByVal(PrimVal::Bytes(b))) =>
+ return Const::from_bytes(ccx, b, ty),
+ ConstVal::Value(MiriValue::ByVal(PrimVal::Undef)) => C_undef(llty),
+ ConstVal::Value(MiriValue::ByVal(PrimVal::Ptr(ptr))) => {
+ let alloc = ccx
+ .tcx()
+ .interpret_interner
+ .borrow()
+ .get_alloc(ptr.alloc_id.0)
+ .expect("miri alloc not found");
+ let data = &alloc.bytes[(ptr.offset as usize)..];
+ consts::addr_of(ccx, C_bytes(ccx, data), ccx.align_of(ty), "byte_str")
+ }
};
assert!(!ty.has_erasable_regions());
pub fn len<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef {
match self.ty.sty {
ty::TyArray(_, n) => {
- C_usize(cx, n.val.to_const_int().unwrap().to_u64().unwrap())
+ C_usize(cx, n.val.unwrap_u64())
}
ty::TySlice(_) | ty::TyStr => {
assert!(self.llextra != ptr::null_mut());
let tcx = self.cx.tcx;
let mut bb = mir::START_BLOCK;
- // Make sure to evaluate all statemenets to
+ // Make sure to evaluate all statements to
// report as many errors as we possibly can.
let mut failure = Ok(());
_ => span_bug!(span, "calling {:?} (of type {}) in constant",
func, fn_ty)
};
+ trace!("trans const fn call {:?}, {:?}, {:#?}", func, fn_ty, args);
let mut arg_vals = IndexVec::with_capacity(args.len());
for arg in args {
}
_ => span_bug!(span, "{:?} in constant", terminator.kind)
}
- } else if let Some((op, is_checked)) = self.is_binop_lang_item(def_id) {
+ } else if let Some((op, is_checked)) = tcx.is_binop_lang_item(def_id) {
(||{
assert_eq!(arg_vals.len(), 2);
let rhs = arg_vals.pop().unwrap()?;
}
}
- fn is_binop_lang_item(&mut self, def_id: DefId) -> Option<(mir::BinOp, bool)> {
- let tcx = self.cx.tcx;
- let items = tcx.lang_items();
- let def_id = Some(def_id);
- if items.i128_add_fn() == def_id { Some((mir::BinOp::Add, false)) }
- else if items.u128_add_fn() == def_id { Some((mir::BinOp::Add, false)) }
- else if items.i128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) }
- else if items.u128_sub_fn() == def_id { Some((mir::BinOp::Sub, false)) }
- else if items.i128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) }
- else if items.u128_mul_fn() == def_id { Some((mir::BinOp::Mul, false)) }
- else if items.i128_div_fn() == def_id { Some((mir::BinOp::Div, false)) }
- else if items.u128_div_fn() == def_id { Some((mir::BinOp::Div, false)) }
- else if items.i128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) }
- else if items.u128_rem_fn() == def_id { Some((mir::BinOp::Rem, false)) }
- else if items.i128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) }
- else if items.u128_shl_fn() == def_id { Some((mir::BinOp::Shl, false)) }
- else if items.i128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) }
- else if items.u128_shr_fn() == def_id { Some((mir::BinOp::Shr, false)) }
- else if items.i128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) }
- else if items.u128_addo_fn() == def_id { Some((mir::BinOp::Add, true)) }
- else if items.i128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) }
- else if items.u128_subo_fn() == def_id { Some((mir::BinOp::Sub, true)) }
- else if items.i128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) }
- else if items.u128_mulo_fn() == def_id { Some((mir::BinOp::Mul, true)) }
- else if items.i128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) }
- else if items.u128_shlo_fn() == def_id { Some((mir::BinOp::Shl, true)) }
- else if items.i128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) }
- else if items.u128_shro_fn() == def_id { Some((mir::BinOp::Shr, true)) }
- else { None }
- }
-
fn store(&mut self,
dest: &mir::Place<'tcx>,
value: Result<Const<'tcx>, ConstEvalErr<'tcx>>,
if let mir::Place::Local(index) = *place {
if let LocalRef::Operand(Some(op)) = self.locals[index] {
if let ty::TyArray(_, n) = op.layout.ty.sty {
- let n = n.val.to_const_int().unwrap().to_u64().unwrap();
+ let n = n.val.unwrap_u64();
return common::C_usize(bx.cx, n);
}
}
let expected_ty = self.structurally_resolved_type(pat.span, expected);
let (inner_ty, slice_ty) = match expected_ty.sty {
ty::TyArray(inner_ty, size) => {
- let size = size.val.to_const_int().unwrap().to_u64().unwrap();
+ let size = size.val.unwrap_u64();
let min_len = before.len() as u64 + after.len() as u64;
if slice.is_none() {
if min_len != size {
};
if let Ok(count) = count {
- let zero_or_one = count.val.to_const_int().and_then(|count| {
- count.to_u64().map(|count| count <= 1)
- }).unwrap_or(false);
+ let zero_or_one = count.val.to_u128().map_or(false, |count| count <= 1);
if !zero_or_one {
// For [foo, ..n] where n > 1, `foo` must have
// Copy type:
hir::BiBitOr => ("bitor", lang.bitor_trait()),
hir::BiShl => ("shl", lang.shl_trait()),
hir::BiShr => ("shr", lang.shr_trait()),
- hir::BiLt => ("lt", lang.ord_trait()),
- hir::BiLe => ("le", lang.ord_trait()),
- hir::BiGe => ("ge", lang.ord_trait()),
- hir::BiGt => ("gt", lang.ord_trait()),
+ hir::BiLt => ("lt", lang.partial_ord_trait()),
+ hir::BiLe => ("le", lang.partial_ord_trait()),
+ hir::BiGe => ("ge", lang.partial_ord_trait()),
+ hir::BiGt => ("gt", lang.partial_ord_trait()),
hir::BiEq => ("eq", lang.eq_trait()),
hir::BiNe => ("ne", lang.eq_trait()),
hir::BiAnd | hir::BiOr => {
use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt};
use rustc::ty::maps::Providers;
use rustc::ty::util::IntTypeExt;
-use rustc::util::nodemap::FxHashSet;
-use util::nodemap::FxHashMap;
+use rustc::util::nodemap::{FxHashSet, FxHashMap};
+use rustc::mir::interpret::{Value, PrimVal};
use rustc_const_math::ConstInt;
match result {
Ok(&ty::Const { val: ConstVal::Integral(x), .. }) => Some(x),
+ Ok(&ty::Const {
+ val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))),
+ ..
+ }) => {
+ use syntax::attr::IntType;
+ Some(match repr_type {
+ IntType::SignedInt(int_type) => ConstInt::new_signed(
+ b as i128, int_type, tcx.sess.target.isize_ty).unwrap(),
+ IntType::UnsignedInt(uint_type) => ConstInt::new_unsigned(
+ b, uint_type, tcx.sess.target.usize_ty).unwrap(),
+ })
+ }
_ => None
}
} else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
#![feature(refcell_replace_swap)]
#![feature(rustc_diagnostic_macros)]
#![feature(slice_patterns)]
+#![feature(i128_type)]
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
// _3 = &'23_1rs _2;
// StorageLive(_5);
// _5 = _2;
-// switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
+// switchInt(move _5) -> [false: bb5, otherwise: bb4];
// }
// bb3: {
// ...
// _3 = &'26_1rs _1;
// StorageLive(_5);
// _5 = _1;
-// switchInt(move _5) -> [0u8: bb5, otherwise: bb4];
+// switchInt(move _5) -> [false: bb5, otherwise: bb4];
// }
// bb3: {
// ...
// bb4: {
// StorageLive(_7);
// _7 = _1;
-// switchInt(move _7) -> [0u8: bb6, otherwise: bb5];
+// switchInt(move _7) -> [false: bb6, otherwise: bb5];
// }
// bb5: {
// _0 = ();
// _11 = const query() -> [return: bb6, unwind: bb3];
// }
// bb6: {
-// switchInt(move _11) -> [0u8: bb8, otherwise: bb7];
+// switchInt(move _11) -> [false: bb8, otherwise: bb7];
// }
// bb7: {
// _0 = ();
// bb3: {
// StorageLive(_4);
// _4 = _1;
-// switchInt(move _4) -> [0u8: bb5, otherwise: bb4];
+// switchInt(move _4) -> [false: bb5, otherwise: bb4];
// }
// bb4: {
// _0 = ();
// _7 = const guard() -> [return: bb10, unwind: bb1];
// }
// bb10: { // end of guard
-// switchInt(move _7) -> [0u8: bb11, otherwise: bb2];
+// switchInt(move _7) -> [false: bb11, otherwise: bb2];
// }
// bb11: { // to pre_binding2
// falseEdges -> [real: bb5, imaginary: bb5];
// _7 = const guard() -> [return: bb10, unwind: bb1];
// }
// bb10: { // end of guard
-// switchInt(move _7) -> [0u8: bb11, otherwise: bb2];
+// switchInt(move _7) -> [false: bb11, otherwise: bb2];
// }
// bb11: { // to pre_binding2
// falseEdges -> [real: bb6, imaginary: bb5];
// _9 = const guard() -> [return: bb10, unwind: bb1];
// }
// bb10: { //end of guard
-// switchInt(move _9) -> [0u8: bb11, otherwise: bb2];
+// switchInt(move _9) -> [false: bb11, otherwise: bb2];
// }
// bb11: { // to pre_binding2
// falseEdges -> [real: bb5, imaginary: bb5];
// _11 = const guard2(move _12) -> [return: bb14, unwind: bb1];
// }
// bb14: { // end of guard2
-// StorageDead(_12);
-// switchInt(move _11) -> [0u8: bb15, otherwise: bb3];
+// StorageDead(_11);
+// switchInt(move _11) -> [false: bb15, otherwise: bb3];
// }
// bb15: { // to pre_binding4
// falseEdges -> [real: bb7, imaginary: bb7];
// | Live variables on entry to bb2[0]: [_1, _3]
// _2 = &'_#2r _1[_3];
// | Live variables on entry to bb2[1]: [_2]
-// switchInt(const true) -> [0u8: bb4, otherwise: bb3];
+// switchInt(const true) -> [false: bb4, otherwise: bb3];
// }
// END rustc.main.nll.0.mir
// START rustc.main.nll.0.mir
// END RUST SOURCE
// START rustc.main.SimplifyBranches-initial.before.mir
// bb0: {
-// switchInt(const false) -> [0u8: bb3, otherwise: bb2];
+// switchInt(const false) -> [false: bb3, otherwise: bb2];
// }
// END rustc.main.SimplifyBranches-initial.before.mir
// START rustc.main.SimplifyBranches-initial.after.mir
const NEG_128: i8 = -128;
const NEG_NEG_128: i8 = -NEG_128;
-//~^ ERROR constant evaluation error
-//~| attempt to negate with overflow
+//~^ ERROR E0080
fn main() {
match -128i8 {
const A_I8_T
: [u32; (i8::MAX as i8 + 1i8) as usize]
- //~^ ERROR constant evaluation error
- //~| WARNING constant evaluation error
+ //~^ ERROR E0080
= [0; (i8::MAX as usize) + 1];
fn main() {
fn main() {
const X: u32 = 0-1; //~ ERROR constant evaluation error
- //~^ WARN constant evaluation error
const Y: u32 = foo(0-1); //~ ERROR constant evaluation error
- //~^ WARN constant evaluation error
println!("{} {}", X, Y);
}
// except according to those terms.
// Encountered while testing #44614.
+// must-compile-successfully
pub fn main() {
// Constant of generic type (int)
- const X: &'static u32 = &22; //~ ERROR constant evaluation error
+ const X: &'static u32 = &22;
assert_eq!(0, match &22 {
X => 0,
_ => 1,
const X : usize = 2;
const fn f(x: usize) -> usize {
- let mut sum = 0; //~ ERROR blocks in constant functions are limited
- for i in 0..x { //~ ERROR calls in constant functions
- //~| ERROR constant function contains unimplemented
+ let mut sum = 0;
+ //~^ ERROR E0016
+ for i in 0..x {
+ //~^ ERROR E0015
+ //~| ERROR E0019
sum += i;
}
- sum //~ ERROR E0080
- //~| non-constant path in constant
+ sum
}
#[allow(unused_variables)]
fn main() {
let a : [i32; f(X)];
- //~^ WARNING constant evaluation error: non-constant path
+ //~^ ERROR E0080
}
+<<<<<<< HEAD
warning: constant evaluation error: non-constant path in constant expression
--> $DIR/const-fn-error.rs:27:19
|
--> $DIR/const-fn-error.rs:16:19
|
LL | let mut sum = 0; //~ ERROR blocks in constant functions are limited
+=======
+error[E0016]: blocks in constant functions are limited to items and tail expressions
+ --> $DIR/const-fn-error.rs:16:19
+ |
+16 | let mut sum = 0;
+>>>>>>> Produce instead of pointers
| ^
error[E0015]: calls in constant functions are limited to constant functions, struct and enum constructors
| ^^^^
error[E0080]: constant evaluation error
+<<<<<<< HEAD
--> $DIR/const-fn-error.rs:21:5
|
LL | sum //~ ERROR E0080
|
LL | let a : [i32; f(X)];
| ^^^^^^^^^^^
+=======
+ --> $DIR/const-fn-error.rs:28:19
+ |
+28 | let a : [i32; f(X)];
+ | ^^^^ miri failed: machine error: Cannot evaluate within constants: "calling non-const fn `<I as std::iter::IntoIterator><std::ops::Range<usize>>::into_iter`"
+>>>>>>> Produce instead of pointers
error: aborting due to 4 previous errors
const ONE: usize = 1;
const TWO: usize = 2;
const LEN: usize = ONE - TWO;
-//~^ ERROR constant evaluation error [E0080]
-//~| WARN attempt to subtract with overflow
fn main() {
let a: [i8; LEN] = unimplemented!();
+//~^ ERROR E0080
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// must-compile-successfully
+
#![feature(const_fn)]
#[derive(PartialEq, Eq)]
struct Pair<A, B>(A, B);
const BOO: Pair<Cake, Cake> = Pair(Marmor, BlackForest);
-//~^ ERROR: constant evaluation error [E0080]
-//~| unimplemented constant expression: tuple struct constructors
const FOO: Cake = BOO.1;
const fn foo() -> Cake {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// must-compile-successfully
fn main() {
const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47];
const IDX: usize = 3;
const VAL: i32 = ARR[IDX];
- const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; //~ ERROR constant evaluation error
+ const BLUB: [i32; (ARR[0] - 41) as usize] = [5];
}
// except according to those terms.
// aux-build:issue_38875_b.rs
+// must-compile-successfully
extern crate issue_38875_b;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// must-compile-successfully
+
union U {
a: usize,
b: usize,
const C: U = U { a: 10 };
fn main() {
- unsafe {
- let a: [u8; C.a]; // OK
- let b: [u8; C.b]; //~ ERROR constant evaluation error
- //~| WARNING constant evaluation error
- }
+ let a: [u8; unsafe { C.a }];
+ let b: [u8; unsafe { C.b }];
}