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
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) -> EvalResult<'static, Self> {
30 Value::ByRef(..) => bug!(),
31 Value::ScalarPair(a, b) => ConstValue::ScalarPair(
35 Value::Scalar(val) => ConstValue::Scalar(val.unwrap_or_err()?),
40 pub fn to_byval_value(&self) -> Option<Value> {
42 ConstValue::Unevaluated(..) |
43 ConstValue::ByRef(..) => None,
44 ConstValue::ScalarPair(a, b) => Some(Value::ScalarPair(a.into(), b.into())),
45 ConstValue::Scalar(val) => Some(Value::Scalar(val.into())),
50 pub fn try_to_scalar(&self) -> Option<Scalar> {
52 ConstValue::Unevaluated(..) |
53 ConstValue::ByRef(..) |
54 ConstValue::ScalarPair(..) => None,
55 ConstValue::Scalar(val) => Some(val),
60 pub fn to_bits(&self, size: Size) -> Option<u128> {
61 self.try_to_scalar()?.to_bits(size).ok()
65 pub fn to_ptr(&self) -> Option<Pointer> {
66 self.try_to_scalar()?.to_ptr().ok()
70 /// A `Value` represents a single self-contained Rust value.
72 /// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
73 /// value held directly, outside of any allocation (`Scalar`). For `ByRef`-values, we remember
74 /// whether the pointer is supposed to be aligned or not (also see Place).
76 /// For optimization of a few very common cases, there is also a representation for a pair of
77 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
78 /// operations and fat pointers. This idea was taken from rustc's codegen.
79 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
82 Scalar(ScalarMaybeUndef),
83 ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
86 impl<'tcx> ty::TypeFoldable<'tcx> for Value {
87 fn super_fold_with<'gcx: 'tcx, F: ty::fold::TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> Self {
90 fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
96 pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
99 size: cx.data_layout().pointer_size.bytes() as u8,
103 pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
104 ScalarMaybeUndef::Scalar(self).to_value_with_len(len, cx)
107 pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
108 let layout = cx.data_layout();
110 Scalar::Bits { bits, size } => {
111 assert_eq!(size as u64, layout.pointer_size.bytes());
113 bits: layout.signed_offset(bits as u64, i)? as u128,
117 Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
121 pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
122 let layout = cx.data_layout();
124 Scalar::Bits { bits, size } => {
125 assert_eq!(size as u64, layout.pointer_size.bytes());
127 bits: layout.offset(bits as u64, i.bytes())? as u128,
131 Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
135 pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
136 let layout = cx.data_layout();
138 Scalar::Bits { bits, size } => {
139 assert_eq!(size as u64, layout.pointer_size.bytes());
141 bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
145 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, layout)),
149 pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> bool {
151 Scalar::Bits { bits, size } => {
152 assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
155 Scalar::Ptr(_) => false,
159 pub fn to_value(self) -> Value {
160 Value::Scalar(ScalarMaybeUndef::Scalar(self))
164 impl From<Pointer> for Scalar {
165 fn from(ptr: Pointer) -> Self {
170 /// A `Scalar` represents an immediate, primitive value existing outside of a
171 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
172 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
173 /// of a simple value or a pointer into another `Allocation`
174 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
176 /// The raw bytes of a simple value.
178 /// The first `size` bytes are the value.
179 /// Do not try to read less or more bytes that that
184 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
185 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
186 /// relocation and its associated offset together as a `Pointer` here.
190 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
191 pub enum ScalarMaybeUndef {
196 impl From<Scalar> for ScalarMaybeUndef {
197 fn from(s: Scalar) -> Self {
198 ScalarMaybeUndef::Scalar(s)
202 impl ScalarMaybeUndef {
203 pub fn unwrap_or_err(self) -> EvalResult<'static, Scalar> {
205 ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
206 ScalarMaybeUndef::Undef => err!(ReadUndefBytes),
210 pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
211 Value::ScalarPair(self.into(), Scalar::Bits {
213 size: cx.data_layout().pointer_size.bytes() as u8,
217 pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
218 Value::ScalarPair(self.into(), Scalar::Ptr(vtable).into())
221 pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
223 ScalarMaybeUndef::Scalar(scalar) => {
224 scalar.ptr_offset(i, cx).map(ScalarMaybeUndef::Scalar)
226 ScalarMaybeUndef::Undef => Ok(ScalarMaybeUndef::Undef)
232 pub fn from_bool(b: bool) -> Self {
233 Scalar::Bits { bits: b as u128, size: 1 }
236 pub fn from_char(c: char) -> Self {
237 Scalar::Bits { bits: c as u128, size: 4 }
240 pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
242 Scalar::Bits { bits, size } => {
243 assert_eq!(target_size.bytes(), size as u64);
244 assert_ne!(size, 0, "to_bits cannot be used with zsts");
247 Scalar::Ptr(_) => err!(ReadPointerAsBytes),
251 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
253 Scalar::Bits {..} => err!(ReadBytesAsPointer),
254 Scalar::Ptr(p) => Ok(p),
258 pub fn is_bits(self) -> bool {
260 Scalar::Bits { .. } => true,
265 pub fn is_ptr(self) -> bool {
267 Scalar::Ptr(_) => true,
272 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
274 Scalar::Bits { bits: 0, size: 1 } => Ok(false),
275 Scalar::Bits { bits: 1, size: 1 } => Ok(true),
276 _ => err!(InvalidBool),