]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/value.rs
ffd138c9c4815542c897c6880c8fffcdea194923
[rust.git] / src / librustc / mir / interpret / value.rs
1 #![allow(unknown_lints)]
2
3 use ty::layout::{Align, HasDataLayout, Size};
4 use ty;
5 use ty::subst::Substs;
6 use hir::def_id::DefId;
7
8 use super::{EvalResult, Pointer, PointerArithmetic, Allocation};
9
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
16     /// evaluation
17     Unevaluated(DefId, &'tcx Substs<'tcx>),
18     /// Used only for types with layout::abi::Scalar ABI and ZSTs which use Scalar::undef()
19     Scalar(Scalar),
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),
24 }
25
26 impl<'tcx> ConstValue<'tcx> {
27     #[inline]
28     pub fn from_byval_value(val: Value) -> Self {
29         match val {
30             Value::ByRef(..) => bug!(),
31             Value::ScalarPair(a, b) => ConstValue::ScalarPair(a, b),
32             Value::Scalar(val) => ConstValue::Scalar(val),
33         }
34     }
35
36     #[inline]
37     pub fn to_byval_value(&self) -> Option<Value> {
38         match *self {
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)),
43         }
44     }
45
46     #[inline]
47     pub fn from_scalar(val: Scalar) -> Self {
48         ConstValue::Scalar(val)
49     }
50
51     #[inline]
52     pub fn to_scalar(&self) -> Option<Scalar> {
53         match *self {
54             ConstValue::Unevaluated(..) |
55             ConstValue::ByRef(..) |
56             ConstValue::ScalarPair(..) => None,
57             ConstValue::Scalar(val) => Some(val),
58         }
59     }
60
61     #[inline]
62     pub fn to_bits(&self, size: Size) -> Option<u128> {
63         self.to_scalar()?.to_bits(size).ok()
64     }
65
66     #[inline]
67     pub fn to_ptr(&self) -> Option<Pointer> {
68         self.to_scalar()?.to_ptr().ok()
69     }
70 }
71
72 /// A `Value` represents a single self-contained Rust value.
73 ///
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).
77 ///
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)]
82 pub enum Value {
83     ByRef(Scalar, Align),
84     Scalar(Scalar),
85     ScalarPair(Scalar, Scalar),
86 }
87
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 {
90         *self
91     }
92     fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
93         false
94     }
95 }
96
97 impl<'tcx> Scalar {
98     pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
99         Scalar::Bits {
100             bits: 0,
101             defined: cx.data_layout().pointer_size.bits() as u8,
102         }
103     }
104
105     pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
106         let layout = cx.data_layout();
107         match self {
108             Scalar::Bits { bits, defined } => {
109                 let pointer_size = layout.pointer_size.bits() as u8;
110                 if defined < pointer_size {
111                     err!(ReadUndefBytes)
112                 } else {
113                     Ok(Scalar::Bits {
114                         bits: layout.signed_offset(bits as u64, i)? as u128,
115                         defined: pointer_size,
116                     })
117             }
118             }
119             Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
120         }
121     }
122
123     pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
124         let layout = cx.data_layout();
125         match self {
126             Scalar::Bits { bits, defined } => {
127                 let pointer_size = layout.pointer_size.bits() as u8;
128                 if defined < pointer_size {
129                     err!(ReadUndefBytes)
130                 } else {
131                     Ok(Scalar::Bits {
132                         bits: layout.offset(bits as u64, i.bytes())? as u128,
133                         defined: pointer_size,
134                     })
135             }
136             }
137             Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
138         }
139     }
140
141     pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
142         let layout = cx.data_layout();
143         match self {
144             Scalar::Bits { bits, defined } => {
145                 let pointer_size = layout.pointer_size.bits() as u8;
146                 if defined < pointer_size {
147                     err!(ReadUndefBytes)
148                 } else {
149                     Ok(Scalar::Bits {
150                         bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
151                         defined: pointer_size,
152                     })
153             }
154             }
155             Scalar::Ptr(ptr) => Ok(Scalar::Ptr(ptr.wrapping_signed_offset(i, layout))),
156         }
157     }
158
159     pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> EvalResult<'tcx, bool> {
160         match self {
161             Scalar::Bits {
162                 bits, defined,
163             } => if defined < cx.data_layout().pointer_size.bits() as u8 {
164                 err!(ReadUndefBytes)
165             } else {
166                 Ok(bits == 0)
167             },
168             Scalar::Ptr(_) => Ok(false),
169         }
170     }
171
172     pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
173         Value::ScalarPair(self, Scalar::Bits {
174             bits: len as u128,
175             defined: cx.data_layout().pointer_size.bits() as u8,
176         })
177     }
178
179     pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
180         Value::ScalarPair(self, Scalar::Ptr(vtable))
181     }
182
183     pub fn to_value(self) -> Value {
184         Value::Scalar(self)
185     }
186 }
187
188 impl From<Pointer> for Scalar {
189     fn from(ptr: Pointer) -> Self {
190         Scalar::Ptr(ptr)
191     }
192 }
193
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)]
199 pub enum Scalar {
200     /// The raw bytes of a simple value.
201     Bits {
202         /// The first `defined` number of bits are valid
203         defined: u8,
204         bits: u128,
205     },
206
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.
210     Ptr(Pointer),
211 }
212
213 impl<'tcx> Scalar {
214     pub fn undef() -> Self {
215         Scalar::Bits { bits: 0, defined: 0 }
216     }
217
218     pub fn from_bool(b: bool) -> Self {
219         // FIXME: can we make defined `1`?
220         Scalar::Bits { bits: b as u128, defined: 8 }
221     }
222
223     pub fn from_char(c: char) -> Self {
224         Scalar::Bits { bits: c as u128, defined: 32 }
225     }
226
227     pub fn to_bits(self, size: Size) -> EvalResult<'tcx, u128> {
228         match self {
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),
233         }
234     }
235
236     pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
237         match self {
238             Scalar::Bits {..} => err!(ReadBytesAsPointer),
239             Scalar::Ptr(p) => Ok(p),
240         }
241     }
242
243     pub fn is_bits(self) -> bool {
244         match self {
245             Scalar::Bits { .. } => true,
246             _ => false,
247         }
248     }
249
250     pub fn is_ptr(self) -> bool {
251         match self {
252             Scalar::Ptr(_) => true,
253             _ => false,
254         }
255     }
256
257     pub fn to_bool(self) -> EvalResult<'tcx, bool> {
258         match self {
259             Scalar::Bits { bits: 0, defined: 8 } => Ok(false),
260             Scalar::Bits { bits: 1, defined: 8 } => Ok(true),
261             _ => err!(InvalidBool),
262         }
263     }
264 }