rust/src/librustc/mir/interpret/value.rs

369 lines
11 KiB
Rust

#![allow(unknown_lints)]
use ty::layout::{Align, HasDataLayout};
use ty;
use super::{EvalResult, MemoryPointer, PointerArithmetic, Allocation};
/// Represents a constant value in Rust. ByVal and ByValPair are optimizations which
/// matches Value's optimizations for easy conversions between these two types
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub enum ConstValue<'tcx> {
// Used only for types with layout::abi::Scalar ABI and ZSTs which use PrimVal::Undef
ByVal(PrimVal),
// Used only for types with layout::abi::ScalarPair
ByValPair(PrimVal, PrimVal),
// Used only for the remaining cases
ByRef(&'tcx Allocation),
}
impl<'tcx> ConstValue<'tcx> {
#[inline]
pub fn from_byval_value(val: Value) -> Self {
match val {
Value::ByRef(..) => bug!(),
Value::ByValPair(a, b) => ConstValue::ByValPair(a, b),
Value::ByVal(val) => ConstValue::ByVal(val),
}
}
#[inline]
pub fn to_byval_value(&self) -> Option<Value> {
match *self {
ConstValue::ByRef(..) => None,
ConstValue::ByValPair(a, b) => Some(Value::ByValPair(a, b)),
ConstValue::ByVal(val) => Some(Value::ByVal(val)),
}
}
#[inline]
pub fn from_primval(val: PrimVal) -> Self {
ConstValue::ByVal(val)
}
#[inline]
pub fn to_primval(&self) -> Option<PrimVal> {
match *self {
ConstValue::ByRef(..) => None,
ConstValue::ByValPair(..) => None,
ConstValue::ByVal(val) => Some(val),
}
}
#[inline]
pub fn to_bits(&self) -> Option<u128> {
match self.to_primval() {
Some(PrimVal::Bytes(val)) => Some(val),
_ => None,
}
}
#[inline]
pub fn to_ptr(&self) -> Option<MemoryPointer> {
match self.to_primval() {
Some(PrimVal::Ptr(ptr)) => Some(ptr),
_ => None,
}
}
}
/// A `Value` represents a single self-contained Rust value.
///
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
/// value held directly, outside of any allocation (`ByVal`). For `ByRef`-values, we remember
/// whether the pointer is supposed to be aligned or not (also see Place).
///
/// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary
/// operations and fat pointers. This idea was taken from rustc's codegen.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub enum Value {
ByRef(Pointer, Align),
ByVal(PrimVal),
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.
///
/// I (@oli-obk) believe it is less easy to mix up generic primvals and primvals that are just
/// the representation of pointers. Also all the sites that convert between primvals and pointers
/// are explicit now (and rare!)
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub struct Pointer {
pub primval: PrimVal,
}
impl<'tcx> Pointer {
pub fn null() -> Self {
PrimVal::Bytes(0).into()
}
pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
self.primval.to_ptr()
}
pub fn into_inner_primval(self) -> PrimVal {
self.primval
}
pub fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
let layout = cx.data_layout();
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(
PrimVal::Bytes(layout.signed_offset(b as u64, i)? as u128),
))
}
PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(Pointer::from),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> {
let layout = cx.data_layout();
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(
PrimVal::Bytes(layout.offset(b as u64, i)? as u128),
))
}
PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
let layout = cx.data_layout();
match self.primval {
PrimVal::Bytes(b) => {
assert_eq!(b as u64 as u128, b);
Ok(Pointer::from(PrimVal::Bytes(
layout.wrapping_signed_offset(b as u64, i) as u128,
)))
}
PrimVal::Ptr(ptr) => Ok(Pointer::from(ptr.wrapping_signed_offset(i, layout))),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn is_null(self) -> EvalResult<'tcx, bool> {
match self.primval {
PrimVal::Bytes(b) => Ok(b == 0),
PrimVal::Ptr(_) => Ok(false),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn to_value_with_len(self, len: u64) -> Value {
Value::ByValPair(self.primval, PrimVal::from_u128(len as u128))
}
pub fn to_value_with_vtable(self, vtable: MemoryPointer) -> Value {
Value::ByValPair(self.primval, PrimVal::Ptr(vtable))
}
pub fn to_value(self) -> Value {
Value::ByVal(self.primval)
}
}
impl ::std::convert::From<PrimVal> for Pointer {
fn from(primval: PrimVal) -> Self {
Pointer { primval }
}
}
impl ::std::convert::From<MemoryPointer> for Pointer {
fn from(ptr: MemoryPointer) -> Self {
PrimVal::Ptr(ptr).into()
}
}
/// A `PrimVal` represents an immediate, primitive value existing outside of a
/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
/// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes
/// of a simple value, a pointer into another `Allocation`, or be undefined.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub enum PrimVal {
/// The raw bytes of a simple value.
Bytes(u128),
/// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
/// relocations, but a `PrimVal` is only large enough to contain one, so we just represent the
/// relocation and its associated offset together as a `MemoryPointer` here.
Ptr(MemoryPointer),
/// An undefined `PrimVal`, for representing values that aren't safe to examine, but are safe
/// to copy around, just like undefined bytes in an `Allocation`.
Undef,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimValKind {
I8, I16, I32, I64, I128,
U8, U16, U32, U64, U128,
F32, F64,
Ptr, FnPtr,
Bool,
Char,
}
impl<'tcx> PrimVal {
pub fn from_u128(n: u128) -> Self {
PrimVal::Bytes(n)
}
pub fn from_i128(n: i128) -> Self {
PrimVal::Bytes(n as u128)
}
pub fn from_bool(b: bool) -> Self {
PrimVal::Bytes(b as u128)
}
pub fn from_char(c: char) -> Self {
PrimVal::Bytes(c as u128)
}
pub fn to_bytes(self) -> EvalResult<'tcx, u128> {
match self {
PrimVal::Bytes(b) => Ok(b),
PrimVal::Ptr(_) => err!(ReadPointerAsBytes),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> {
match self {
PrimVal::Bytes(_) => err!(ReadBytesAsPointer),
PrimVal::Ptr(p) => Ok(p),
PrimVal::Undef => err!(ReadUndefBytes),
}
}
pub fn is_bytes(self) -> bool {
match self {
PrimVal::Bytes(_) => true,
_ => false,
}
}
pub fn is_ptr(self) -> bool {
match self {
PrimVal::Ptr(_) => true,
_ => false,
}
}
pub fn is_undef(self) -> bool {
match self {
PrimVal::Undef => true,
_ => false,
}
}
pub fn to_u128(self) -> EvalResult<'tcx, u128> {
self.to_bytes()
}
pub fn to_u64(self) -> EvalResult<'tcx, u64> {
self.to_bytes().map(|b| {
assert_eq!(b as u64 as u128, b);
b as u64
})
}
pub fn to_i32(self) -> EvalResult<'tcx, i32> {
self.to_bytes().map(|b| {
assert_eq!(b as i32 as u128, b);
b as i32
})
}
pub fn to_i128(self) -> EvalResult<'tcx, i128> {
self.to_bytes().map(|b| b as i128)
}
pub fn to_i64(self) -> EvalResult<'tcx, i64> {
self.to_bytes().map(|b| {
assert_eq!(b as i64 as u128, b);
b as i64
})
}
pub fn to_bool(self) -> EvalResult<'tcx, bool> {
match self.to_bytes()? {
0 => Ok(false),
1 => Ok(true),
_ => err!(InvalidBool),
}
}
}
impl PrimValKind {
pub fn is_int(self) -> bool {
use self::PrimValKind::*;
match self {
I8 | I16 | I32 | I64 | I128 | U8 | U16 | U32 | U64 | U128 => true,
_ => false,
}
}
pub fn is_signed_int(self) -> bool {
use self::PrimValKind::*;
match self {
I8 | I16 | I32 | I64 | I128 => true,
_ => false,
}
}
pub fn is_float(self) -> bool {
use self::PrimValKind::*;
match self {
F32 | F64 => true,
_ => false,
}
}
pub fn from_uint_size(size: u64) -> Self {
match size {
1 => PrimValKind::U8,
2 => PrimValKind::U16,
4 => PrimValKind::U32,
8 => PrimValKind::U64,
16 => PrimValKind::U128,
_ => bug!("can't make uint with size {}", size),
}
}
pub fn from_int_size(size: u64) -> Self {
match size {
1 => PrimValKind::I8,
2 => PrimValKind::I16,
4 => PrimValKind::I32,
8 => PrimValKind::I64,
16 => PrimValKind::I128,
_ => bug!("can't make int with size {}", size),
}
}
pub fn is_ptr(self) -> bool {
use self::PrimValKind::*;
match self {
Ptr | FnPtr => true,
_ => false,
}
}
}