]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/value.rs
Address behaviour changing review comments
[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
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) -> EvalResult<'static, Self> {
29         Ok(match val {
30             Value::ByRef(..) => bug!(),
31             Value::ScalarPair(a, b) => ConstValue::ScalarPair(
32                 a.unwrap_or_err()?,
33                 b.unwrap_or_err()?,
34             ),
35             Value::Scalar(val) => ConstValue::Scalar(val.unwrap_or_err()?),
36         })
37     }
38
39     #[inline]
40     pub fn to_byval_value(&self) -> Option<Value> {
41         match *self {
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())),
46         }
47     }
48
49     #[inline]
50     pub fn try_to_scalar(&self) -> Option<Scalar> {
51         match *self {
52             ConstValue::Unevaluated(..) |
53             ConstValue::ByRef(..) |
54             ConstValue::ScalarPair(..) => None,
55             ConstValue::Scalar(val) => Some(val),
56         }
57     }
58
59     #[inline]
60     pub fn to_bits(&self, size: Size) -> Option<u128> {
61         self.try_to_scalar()?.to_bits(size).ok()
62     }
63
64     #[inline]
65     pub fn to_ptr(&self) -> Option<Pointer> {
66         self.try_to_scalar()?.to_ptr().ok()
67     }
68 }
69
70 /// A `Value` represents a single self-contained Rust value.
71 ///
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).
75 ///
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)]
80 pub enum Value {
81     ByRef(Scalar, Align),
82     Scalar(ScalarMaybeUndef),
83     ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
84 }
85
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 {
88         *self
89     }
90     fn super_visit_with<V: ty::fold::TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
91         false
92     }
93 }
94
95 impl<'tcx> Scalar {
96     pub fn ptr_null<C: HasDataLayout>(cx: C) -> Self {
97         Scalar::Bits {
98             bits: 0,
99             size: cx.data_layout().pointer_size.bytes() as u8,
100         }
101     }
102
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)
105     }
106
107     pub fn ptr_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> {
108         let layout = cx.data_layout();
109         match self {
110             Scalar::Bits { bits, size } => {
111                 assert_eq!(size as u64, layout.pointer_size.bytes());
112                 Ok(Scalar::Bits {
113                     bits: layout.signed_offset(bits as u64, i)? as u128,
114                     size,
115                 })
116             }
117             Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
118         }
119     }
120
121     pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
122         let layout = cx.data_layout();
123         match self {
124             Scalar::Bits { bits, size } => {
125                 assert_eq!(size as u64, layout.pointer_size.bytes());
126                 Ok(Scalar::Bits {
127                     bits: layout.offset(bits as u64, i.bytes())? as u128,
128                     size,
129                 })
130             }
131             Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
132         }
133     }
134
135     pub fn ptr_wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self {
136         let layout = cx.data_layout();
137         match self {
138             Scalar::Bits { bits, size } => {
139                 assert_eq!(size as u64, layout.pointer_size.bytes());
140                 Scalar::Bits {
141                     bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
142                     size,
143                 }
144             }
145             Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, layout)),
146         }
147     }
148
149     pub fn is_null_ptr<C: HasDataLayout>(self, cx: C) -> bool {
150         match self {
151             Scalar::Bits { bits, size } =>  {
152                 assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
153                 bits == 0
154             },
155             Scalar::Ptr(_) => false,
156         }
157     }
158
159     pub fn to_value(self) -> Value {
160         Value::Scalar(ScalarMaybeUndef::Scalar(self))
161     }
162 }
163
164 impl From<Pointer> for Scalar {
165     fn from(ptr: Pointer) -> Self {
166         Scalar::Ptr(ptr)
167     }
168 }
169
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)]
175 pub enum Scalar {
176     /// The raw bytes of a simple value.
177     Bits {
178         /// The first `size` bytes are the value.
179         /// Do not try to read less or more bytes that that
180         size: u8,
181         bits: u128,
182     },
183
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.
187     Ptr(Pointer),
188 }
189
190 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
191 pub enum ScalarMaybeUndef {
192     Scalar(Scalar),
193     Undef,
194 }
195
196 impl From<Scalar> for ScalarMaybeUndef {
197     fn from(s: Scalar) -> Self {
198         ScalarMaybeUndef::Scalar(s)
199     }
200 }
201
202 impl ScalarMaybeUndef {
203     pub fn unwrap_or_err(self) -> EvalResult<'static, Scalar> {
204         match self {
205             ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
206             ScalarMaybeUndef::Undef => err!(ReadUndefBytes),
207         }
208     }
209
210     pub fn to_value_with_len<C: HasDataLayout>(self, len: u64, cx: C) -> Value {
211         Value::ScalarPair(self.into(), Scalar::Bits {
212             bits: len as u128,
213             size: cx.data_layout().pointer_size.bytes() as u8,
214         }.into())
215     }
216
217     pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
218         Value::ScalarPair(self.into(), Scalar::Ptr(vtable).into())
219     }
220
221     pub fn ptr_offset<C: HasDataLayout>(self, i: Size, cx: C) -> EvalResult<'tcx, Self> {
222         match self {
223             ScalarMaybeUndef::Scalar(scalar) => {
224                 scalar.ptr_offset(i, cx).map(ScalarMaybeUndef::Scalar)
225             },
226             ScalarMaybeUndef::Undef => Ok(ScalarMaybeUndef::Undef)
227         }
228     }
229 }
230
231 impl<'tcx> Scalar {
232     pub fn from_bool(b: bool) -> Self {
233         Scalar::Bits { bits: b as u128, size: 1 }
234     }
235
236     pub fn from_char(c: char) -> Self {
237         Scalar::Bits { bits: c as u128, size: 4 }
238     }
239
240     pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
241         match self {
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");
245                 Ok(bits)
246             }
247             Scalar::Ptr(_) => err!(ReadPointerAsBytes),
248         }
249     }
250
251     pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
252         match self {
253             Scalar::Bits {..} => err!(ReadBytesAsPointer),
254             Scalar::Ptr(p) => Ok(p),
255         }
256     }
257
258     pub fn is_bits(self) -> bool {
259         match self {
260             Scalar::Bits { .. } => true,
261             _ => false,
262         }
263     }
264
265     pub fn is_ptr(self) -> bool {
266         match self {
267             Scalar::Ptr(_) => true,
268             _ => false,
269         }
270     }
271
272     pub fn to_bool(self) -> EvalResult<'tcx, bool> {
273         match self {
274             Scalar::Bits { bits: 0, size: 1 } => Ok(false),
275             Scalar::Bits { bits: 1, size: 1 } => Ok(true),
276             _ => err!(InvalidBool),
277         }
278     }
279 }