1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![allow(unknown_lints)]
13 use ty::layout::{HasDataLayout, Size};
14 use ty::subst::Substs;
15 use hir::def_id::DefId;
17 use super::{EvalResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate};
19 /// Represents a constant value in Rust. Scalar and ScalarPair are optimizations which
20 /// matches the LocalValue optimizations for easy conversions between Value and ConstValue.
21 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)]
22 pub enum ConstValue<'tcx> {
23 /// Never returned from the `const_eval` query, but the HIR contains these frequently in order
24 /// to allow HIR creation to happen for everything before needing to be able to run constant
26 Unevaluated(DefId, &'tcx Substs<'tcx>),
27 /// Used only for types with layout::abi::Scalar ABI and ZSTs
29 /// Not using the enum `Value` to encode that this must not be `Undef`
31 /// Used only for types with layout::abi::ScalarPair
33 /// The second field may be undef in case of `Option<usize>::None`
34 ScalarPair(Scalar, ScalarMaybeUndef),
35 /// Used only for the remaining cases. An allocation + offset into the allocation.
36 /// Invariant: The AllocId matches the allocation.
37 ByRef(AllocId, &'tcx Allocation, Size),
40 impl<'tcx> ConstValue<'tcx> {
42 pub fn try_to_scalar(&self) -> Option<Scalar> {
44 ConstValue::Unevaluated(..) |
45 ConstValue::ByRef(..) |
46 ConstValue::ScalarPair(..) => None,
47 ConstValue::Scalar(val) => Some(val),
52 pub fn try_to_bits(&self, size: Size) -> Option<u128> {
53 self.try_to_scalar()?.to_bits(size).ok()
57 pub fn try_to_ptr(&self) -> Option<Pointer> {
58 self.try_to_scalar()?.to_ptr().ok()
65 cx: impl HasDataLayout
67 ConstValue::ScalarPair(val, Scalar::Bits {
69 size: cx.data_layout().pointer_size.bytes() as u8,
74 pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self {
75 ConstValue::ScalarPair(val, Scalar::Ptr(vtable).into())
81 pub fn ptr_null(cx: impl HasDataLayout) -> Self {
84 size: cx.data_layout().pointer_size.bytes() as u8,
89 pub fn zst() -> Self {
90 Scalar::Bits { bits: 0, size: 0 }
94 pub fn ptr_signed_offset(self, i: i64, cx: impl HasDataLayout) -> EvalResult<'tcx, Self> {
95 let layout = cx.data_layout();
97 Scalar::Bits { bits, size } => {
98 assert_eq!(size as u64, layout.pointer_size.bytes());
100 bits: layout.signed_offset(bits as u64, i)? as u128,
104 Scalar::Ptr(ptr) => ptr.signed_offset(i, layout).map(Scalar::Ptr),
109 pub fn ptr_offset(self, i: Size, cx: impl HasDataLayout) -> EvalResult<'tcx, Self> {
110 let layout = cx.data_layout();
112 Scalar::Bits { bits, size } => {
113 assert_eq!(size as u64, layout.pointer_size.bytes());
115 bits: layout.offset(bits as u64, i.bytes())? as u128,
119 Scalar::Ptr(ptr) => ptr.offset(i, layout).map(Scalar::Ptr),
124 pub fn ptr_wrapping_signed_offset(self, i: i64, cx: impl HasDataLayout) -> Self {
125 let layout = cx.data_layout();
127 Scalar::Bits { bits, size } => {
128 assert_eq!(size as u64, layout.pointer_size.bytes());
130 bits: layout.wrapping_signed_offset(bits as u64, i) as u128,
134 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, layout)),
139 pub fn is_null_ptr(self, cx: impl HasDataLayout) -> bool {
141 Scalar::Bits { bits, size } => {
142 assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
145 Scalar::Ptr(_) => false,
150 pub fn is_null(self) -> bool {
152 Scalar::Bits { bits, .. } => bits == 0,
153 Scalar::Ptr(_) => false
158 pub fn from_bool(b: bool) -> Self {
159 Scalar::Bits { bits: b as u128, size: 1 }
163 pub fn from_char(c: char) -> Self {
164 Scalar::Bits { bits: c as u128, size: 4 }
168 pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
170 debug_assert_eq!(truncate(i, size), i,
171 "Unsigned value {} does not fit in {} bits", i, size.bits());
172 Scalar::Bits { bits: i, size: size.bytes() as u8 }
176 pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
178 // `into` performed sign extension, we have to truncate
179 let truncated = truncate(i as u128, size);
180 debug_assert_eq!(sign_extend(truncated, size) as i128, i,
181 "Signed value {} does not fit in {} bits", i, size.bits());
182 Scalar::Bits { bits: truncated, size: size.bytes() as u8 }
186 pub fn from_f32(f: f32) -> Self {
187 Scalar::Bits { bits: f.to_bits() as u128, size: 4 }
191 pub fn from_f64(f: f64) -> Self {
192 Scalar::Bits { bits: f.to_bits() as u128, size: 8 }
196 pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
198 Scalar::Bits { bits, size } => {
199 assert_eq!(target_size.bytes(), size as u64);
200 assert_ne!(size, 0, "to_bits cannot be used with zsts");
203 Scalar::Ptr(_) => err!(ReadPointerAsBytes),
208 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
210 Scalar::Bits { bits: 0, .. } => err!(InvalidNullPointerUsage),
211 Scalar::Bits { .. } => err!(ReadBytesAsPointer),
212 Scalar::Ptr(p) => Ok(p),
217 pub fn is_bits(self) -> bool {
219 Scalar::Bits { .. } => true,
225 pub fn is_ptr(self) -> bool {
227 Scalar::Ptr(_) => true,
232 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
234 Scalar::Bits { bits: 0, size: 1 } => Ok(false),
235 Scalar::Bits { bits: 1, size: 1 } => Ok(true),
236 _ => err!(InvalidBool),
240 pub fn to_char(self) -> EvalResult<'tcx, char> {
241 let val = self.to_u32()?;
242 match ::std::char::from_u32(val) {
244 None => err!(InvalidChar(val as u128)),
248 pub fn to_u8(self) -> EvalResult<'static, u8> {
249 let sz = Size::from_bits(8);
250 let b = self.to_bits(sz)?;
251 assert_eq!(b as u8 as u128, b);
255 pub fn to_u32(self) -> EvalResult<'static, u32> {
256 let sz = Size::from_bits(32);
257 let b = self.to_bits(sz)?;
258 assert_eq!(b as u32 as u128, b);
262 pub fn to_u64(self) -> EvalResult<'static, u64> {
263 let sz = Size::from_bits(64);
264 let b = self.to_bits(sz)?;
265 assert_eq!(b as u64 as u128, b);
269 pub fn to_usize(self, cx: impl HasDataLayout) -> EvalResult<'static, u64> {
270 let b = self.to_bits(cx.data_layout().pointer_size)?;
271 assert_eq!(b as u64 as u128, b);
275 pub fn to_i8(self) -> EvalResult<'static, i8> {
276 let sz = Size::from_bits(8);
277 let b = self.to_bits(sz)?;
278 let b = sign_extend(b, sz) as i128;
279 assert_eq!(b as i8 as i128, b);
283 pub fn to_i32(self) -> EvalResult<'static, i32> {
284 let sz = Size::from_bits(32);
285 let b = self.to_bits(sz)?;
286 let b = sign_extend(b, sz) as i128;
287 assert_eq!(b as i32 as i128, b);
291 pub fn to_i64(self) -> EvalResult<'static, i64> {
292 let sz = Size::from_bits(64);
293 let b = self.to_bits(sz)?;
294 let b = sign_extend(b, sz) as i128;
295 assert_eq!(b as i64 as i128, b);
299 pub fn to_isize(self, cx: impl HasDataLayout) -> EvalResult<'static, i64> {
300 let b = self.to_bits(cx.data_layout().pointer_size)?;
301 let b = sign_extend(b, cx.data_layout().pointer_size) as i128;
302 assert_eq!(b as i64 as i128, b);
307 pub fn to_f32(self) -> EvalResult<'static, f32> {
308 Ok(f32::from_bits(self.to_u32()?))
312 pub fn to_f64(self) -> EvalResult<'static, f64> {
313 Ok(f64::from_bits(self.to_u64()?))
317 impl From<Pointer> for Scalar {
319 fn from(ptr: Pointer) -> Self {
324 /// A `Scalar` represents an immediate, primitive value existing outside of a
325 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
326 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
327 /// of a simple value or a pointer into another `Allocation`
328 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
329 pub enum Scalar<Id=AllocId> {
330 /// The raw bytes of a simple value.
332 /// The first `size` bytes are the value.
333 /// Do not try to read less or more bytes that that. The remaining bytes must be 0.
338 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
339 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
340 /// relocation and its associated offset together as a `Pointer` here.
344 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
345 pub enum ScalarMaybeUndef<Id=AllocId> {
350 impl From<Scalar> for ScalarMaybeUndef {
352 fn from(s: Scalar) -> Self {
353 ScalarMaybeUndef::Scalar(s)
357 impl<'tcx> ScalarMaybeUndef {
359 pub fn not_undef(self) -> EvalResult<'static, Scalar> {
361 ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
362 ScalarMaybeUndef::Undef => err!(ReadUndefBytes),
367 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
368 self.not_undef()?.to_ptr()
372 pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
373 self.not_undef()?.to_bits(target_size)
377 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
378 self.not_undef()?.to_bool()
382 pub fn to_char(self) -> EvalResult<'tcx, char> {
383 self.not_undef()?.to_char()
387 pub fn to_f32(self) -> EvalResult<'tcx, f32> {
388 self.not_undef()?.to_f32()
392 pub fn to_f64(self) -> EvalResult<'tcx, f64> {
393 self.not_undef()?.to_f64()
397 pub fn to_u8(self) -> EvalResult<'tcx, u8> {
398 self.not_undef()?.to_u8()
402 pub fn to_u32(self) -> EvalResult<'tcx, u32> {
403 self.not_undef()?.to_u32()
407 pub fn to_u64(self) -> EvalResult<'tcx, u64> {
408 self.not_undef()?.to_u64()
412 pub fn to_usize(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
413 self.not_undef()?.to_usize(cx)
417 pub fn to_i8(self) -> EvalResult<'tcx, i8> {
418 self.not_undef()?.to_i8()
422 pub fn to_i32(self) -> EvalResult<'tcx, i32> {
423 self.not_undef()?.to_i32()
427 pub fn to_i64(self) -> EvalResult<'tcx, i64> {
428 self.not_undef()?.to_i64()
432 pub fn to_isize(self, cx: impl HasDataLayout) -> EvalResult<'tcx, i64> {
433 self.not_undef()?.to_isize(cx)