1 #![allow(unknown_lints)]
3 use ty::layout::{Align, HasDataLayout, Size};
6 use hir::def_id::DefId;
8 use super::{EvalResult, Pointer, PointerArithmetic, Allocation};
10 /// Represents a constant value in Rust. Scalar and ScalarPair are optimizations which
11 /// matches Value's optimizations for easy conversions between these two types
12 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)]
13 pub enum ConstValue<'tcx> {
14 /// Never returned from the `const_eval` query, but the HIR contains these frequently in order
15 /// to allow HIR creation to happen for everything before needing to be able to run constant
17 Unevaluated(DefId, &'tcx Substs<'tcx>),
18 /// Used only for types with layout::abi::Scalar ABI and ZSTs which use Scalar::undef()
20 /// Used only for types with layout::abi::ScalarPair
21 ScalarPair(Scalar, Scalar),
22 /// Used only for the remaining cases. An allocation + offset into the allocation
23 ByRef(&'tcx Allocation, Size),
26 impl<'tcx> ConstValue<'tcx> {
28 pub fn from_byval_value(val: Value) -> Self {
30 Value::ByRef(..) => bug!(),
31 Value::ScalarPair(a, b) => ConstValue::ScalarPair(a, b),
32 Value::Scalar(val) => ConstValue::Scalar(val),
37 pub fn to_byval_value(&self) -> Option<Value> {
39 ConstValue::Unevaluated(..) |
40 ConstValue::ByRef(..) => None,
41 ConstValue::ScalarPair(a, b) => Some(Value::ScalarPair(a, b)),
42 ConstValue::Scalar(val) => Some(Value::Scalar(val)),
47 pub fn from_scalar(val: Scalar) -> Self {
48 ConstValue::Scalar(val)
52 pub fn to_scalar(&self) -> Option<Scalar> {
54 ConstValue::Unevaluated(..) |
55 ConstValue::ByRef(..) |
56 ConstValue::ScalarPair(..) => None,
57 ConstValue::Scalar(val) => Some(val),
62 pub fn to_bits(&self, size: Size) -> Option<u128> {
63 self.to_scalar()?.to_bits(size).ok()
67 pub fn to_ptr(&self) -> Option<Pointer> {
68 self.to_scalar()?.to_ptr().ok()
72 /// A `Value` represents a single self-contained Rust value.
74 /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
75 /// value held directly, outside of any allocation (`Scalar`). For `ByRef`-values, we remember
76 /// whether the pointer is supposed to be aligned or not (also see Place).
78 /// For optimization of a few very common cases, there is also a representation for a pair of
79 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
80 /// operations and fat pointers. This idea was taken from rustc's codegen.
81 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
85 ScalarPair(Scalar, Scalar),
88 impl<'tcx> ty::TypeFoldable<'tcx> for Value {
89 fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
92 fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
98 pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
101 defined: cx.data_layout().pointer_size.bits() as u8,
105 pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
106 let layout = cx.data_layout();
108 Scalar::Bits { bits, defined } => {
109 let pointer_size = layout.pointer_size.bits() as u8;
110 if defined < pointer_size {
114 bits: layout.signed_offset(bits as u64, i)? as u128,
115 defined: pointer_size,
119 Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
123 pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
124 let layout = cx.data_layout();
126 Scalar::Bits { bits, defined } => {
127 let pointer_size = layout.pointer_size.bits() as u8;
128 if defined < pointer_size {
132 bits: layout.offset(bits as u64, i.bytes())? as u128,
133 defined: pointer_size,
137 Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
141 pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
142 let layout = cx.data_layout();
144 Scalar::Bits { bits, defined } => {
145 let pointer_size = layout.pointer_size.bits() as u8;
146 if defined < pointer_size {
150 bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
151 defined: pointer_size,
155 Scalar::Ptr(ptr) => Ok(Scalar::Ptr(ptr.wrapping_signed_offset(i, layout))),
159 pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> EvalResult<'tcx, bool> {
163 } => if defined < cx.data_layout().pointer_size.bits() as u8 {
168 Scalar::Ptr(_) => Ok(false),
172 pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
173 Value::ScalarPair(self, Scalar::Bits {
175 defined: cx.data_layout().pointer_size.bits() as u8,
179 pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
180 Value::ScalarPair(self, Scalar::Ptr(vtable))
183 pub fn to_value(self) -> Value {
188 impl From<Pointer> for Scalar {
189 fn from(ptr: Pointer) -> Self {
194 /// A `Scalar` represents an immediate, primitive value existing outside of a
195 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
196 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
197 /// of a simple value or a pointer into another `Allocation`
198 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
200 /// The raw bytes of a simple value.
202 /// The first `defined` number of bits are valid
207 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
208 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
209 /// relocation and its associated offset together as a `Pointer` here.
214 pub fn undef() -> Self {
215 Scalar::Bits { bits: 0, defined: 0 }
218 pub fn from_bool(b: bool) -> Self {
219 // FIXME: can we make defined `1`?
220 Scalar::Bits { bits: b as u128, defined: 8 }
223 pub fn from_char(c: char) -> Self {
224 Scalar::Bits { bits: c as u128, defined: 32 }
227 pub fn to_bits(self, size: Size) -> EvalResult<'tcx, u128> {
229 Scalar::Bits { .. } if size.bits() == 0 => bug!("to_bits cannot be used with zsts"),
230 Scalar::Bits { bits, defined } if size.bits() <= defined as u64 => Ok(bits),
231 Scalar::Bits { .. } => err!(ReadUndefBytes),
232 Scalar::Ptr(_) => err!(ReadPointerAsBytes),
236 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
238 Scalar::Bits {..} => err!(ReadBytesAsPointer),
239 Scalar::Ptr(p) => Ok(p),
243 pub fn is_bits(self) -> bool {
245 Scalar::Bits { .. } => true,
250 pub fn is_ptr(self) -> bool {
252 Scalar::Ptr(_) => true,
257 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
259 Scalar::Bits { bits: 0, defined: 8 } => Ok(false),
260 Scalar::Bits { bits: 1, defined: 8 } => Ok(true),
261 _ => err!(InvalidBool),