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.
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>),
28 /// Used only for types with layout::abi::Scalar ABI and ZSTs
30 /// Not using the enum `Value` to encode that this must not be `Undef`
33 /// Used only for *fat pointers* with layout::abi::ScalarPair
35 /// Needed for pattern matching code related to slices and strings.
36 ScalarPair(Scalar, Scalar),
38 /// An allocation + offset into the allocation.
39 /// Invariant: The AllocId matches the allocation.
40 ByRef(AllocId, &'tcx Allocation, Size),
43 impl<'tcx> ConstValue<'tcx> {
45 pub fn try_to_scalar(&self) -> Option<Scalar> {
47 ConstValue::Unevaluated(..) |
48 ConstValue::ByRef(..) |
49 ConstValue::ScalarPair(..) => None,
50 ConstValue::Scalar(val) => Some(val),
55 pub fn try_to_bits(&self, size: Size) -> Option<u128> {
56 self.try_to_scalar()?.to_bits(size).ok()
60 pub fn try_to_ptr(&self) -> Option<Pointer> {
61 self.try_to_scalar()?.to_ptr().ok()
68 cx: &impl HasDataLayout
70 ConstValue::ScalarPair(val, Scalar::Bits {
72 size: cx.data_layout().pointer_size.bytes() as u8,
77 pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self {
78 ConstValue::ScalarPair(val, Scalar::Ptr(vtable))
82 /// A `Scalar` represents an immediate, primitive value existing outside of a
83 /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
84 /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
85 /// of a simple value or a pointer into another `Allocation`
86 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
87 pub enum Scalar<Tag=(), Id=AllocId> {
88 /// The raw bytes of a simple value.
90 /// The first `size` bytes are the value.
91 /// Do not try to read less or more bytes that that. The remaining bytes must be 0.
96 /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
97 /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
98 /// relocation and its associated offset together as a `Pointer` here.
99 Ptr(Pointer<Tag, Id>),
102 impl<Tag> fmt::Display for Scalar<Tag> {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 Scalar::Ptr(_) => write!(f, "a pointer"),
106 Scalar::Bits { bits, .. } => write!(f, "{}", bits),
111 impl<'tcx> Scalar<()> {
113 pub fn with_default_tag<Tag>(self) -> Scalar<Tag>
117 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_default_tag()),
118 Scalar::Bits { bits, size } => Scalar::Bits { bits, size },
123 impl<'tcx, Tag> Scalar<Tag> {
125 pub fn erase_tag(self) -> Scalar {
127 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()),
128 Scalar::Bits { bits, size } => Scalar::Bits { bits, size },
133 pub fn ptr_null(cx: &impl HasDataLayout) -> Self {
136 size: cx.data_layout().pointer_size.bytes() as u8,
141 pub fn zst() -> Self {
142 Scalar::Bits { bits: 0, size: 0 }
146 pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
147 let dl = cx.data_layout();
149 Scalar::Bits { bits, size } => {
150 assert_eq!(size as u64, dl.pointer_size.bytes());
152 bits: dl.offset(bits as u64, i.bytes())? as u128,
156 Scalar::Ptr(ptr) => ptr.offset(i, dl).map(Scalar::Ptr),
161 pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
162 let dl = cx.data_layout();
164 Scalar::Bits { bits, size } => {
165 assert_eq!(size as u64, dl.pointer_size.bytes());
167 bits: dl.overflowing_offset(bits as u64, i.bytes()).0 as u128,
171 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_offset(i, dl)),
176 pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
177 let dl = cx.data_layout();
179 Scalar::Bits { bits, size } => {
180 assert_eq!(size as u64, dl.pointer_size().bytes());
182 bits: dl.signed_offset(bits as u64, i)? as u128,
186 Scalar::Ptr(ptr) => ptr.signed_offset(i, dl).map(Scalar::Ptr),
191 pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
192 let dl = cx.data_layout();
194 Scalar::Bits { bits, size } => {
195 assert_eq!(size as u64, dl.pointer_size.bytes());
197 bits: dl.overflowing_signed_offset(bits as u64, i128::from(i)).0 as u128,
201 Scalar::Ptr(ptr) => Scalar::Ptr(ptr.wrapping_signed_offset(i, dl)),
205 /// Returns this pointers offset from the allocation base, or from NULL (for
206 /// integer pointers).
208 pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
210 Scalar::Bits { bits, size } => {
211 assert_eq!(size as u64, cx.pointer_size().bytes());
212 Size::from_bytes(bits as u64)
214 Scalar::Ptr(ptr) => ptr.offset,
219 pub fn is_null_ptr(self, cx: &impl HasDataLayout) -> bool {
221 Scalar::Bits { bits, size } => {
222 assert_eq!(size as u64, cx.data_layout().pointer_size.bytes());
225 Scalar::Ptr(_) => false,
230 pub fn from_bool(b: bool) -> Self {
231 Scalar::Bits { bits: b as u128, size: 1 }
235 pub fn from_char(c: char) -> Self {
236 Scalar::Bits { bits: c as u128, size: 4 }
240 pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
242 debug_assert_eq!(truncate(i, size), i,
243 "Unsigned value {} does not fit in {} bits", i, size.bits());
244 Scalar::Bits { bits: i, size: size.bytes() as u8 }
248 pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
250 // `into` performed sign extension, we have to truncate
251 let truncated = truncate(i as u128, size);
252 debug_assert_eq!(sign_extend(truncated, size) as i128, i,
253 "Signed value {} does not fit in {} bits", i, size.bits());
254 Scalar::Bits { bits: truncated, size: size.bytes() as u8 }
258 pub fn from_f32(f: f32) -> Self {
259 Scalar::Bits { bits: f.to_bits() as u128, size: 4 }
263 pub fn from_f64(f: f64) -> Self {
264 Scalar::Bits { bits: f.to_bits() as u128, size: 8 }
268 pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
270 Scalar::Bits { bits, size } => {
271 assert_eq!(target_size.bytes(), size as u64);
272 assert_ne!(size, 0, "to_bits cannot be used with zsts");
275 Scalar::Ptr(_) => err!(ReadPointerAsBytes),
280 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
282 Scalar::Bits { bits: 0, .. } => err!(InvalidNullPointerUsage),
283 Scalar::Bits { .. } => err!(ReadBytesAsPointer),
284 Scalar::Ptr(p) => Ok(p),
289 pub fn is_bits(self) -> bool {
291 Scalar::Bits { .. } => true,
297 pub fn is_ptr(self) -> bool {
299 Scalar::Ptr(_) => true,
304 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
306 Scalar::Bits { bits: 0, size: 1 } => Ok(false),
307 Scalar::Bits { bits: 1, size: 1 } => Ok(true),
308 _ => err!(InvalidBool),
312 pub fn to_char(self) -> EvalResult<'tcx, char> {
313 let val = self.to_u32()?;
314 match ::std::char::from_u32(val) {
316 None => err!(InvalidChar(val as u128)),
320 pub fn to_u8(self) -> EvalResult<'static, u8> {
321 let sz = Size::from_bits(8);
322 let b = self.to_bits(sz)?;
323 assert_eq!(b as u8 as u128, b);
327 pub fn to_u32(self) -> EvalResult<'static, u32> {
328 let sz = Size::from_bits(32);
329 let b = self.to_bits(sz)?;
330 assert_eq!(b as u32 as u128, b);
334 pub fn to_u64(self) -> EvalResult<'static, u64> {
335 let sz = Size::from_bits(64);
336 let b = self.to_bits(sz)?;
337 assert_eq!(b as u64 as u128, b);
341 pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'static, u64> {
342 let b = self.to_bits(cx.data_layout().pointer_size)?;
343 assert_eq!(b as u64 as u128, b);
347 pub fn to_i8(self) -> EvalResult<'static, i8> {
348 let sz = Size::from_bits(8);
349 let b = self.to_bits(sz)?;
350 let b = sign_extend(b, sz) as i128;
351 assert_eq!(b as i8 as i128, b);
355 pub fn to_i32(self) -> EvalResult<'static, i32> {
356 let sz = Size::from_bits(32);
357 let b = self.to_bits(sz)?;
358 let b = sign_extend(b, sz) as i128;
359 assert_eq!(b as i32 as i128, b);
363 pub fn to_i64(self) -> EvalResult<'static, i64> {
364 let sz = Size::from_bits(64);
365 let b = self.to_bits(sz)?;
366 let b = sign_extend(b, sz) as i128;
367 assert_eq!(b as i64 as i128, b);
371 pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'static, i64> {
372 let b = self.to_bits(cx.data_layout().pointer_size)?;
373 let b = sign_extend(b, cx.data_layout().pointer_size) as i128;
374 assert_eq!(b as i64 as i128, b);
379 pub fn to_f32(self) -> EvalResult<'static, f32> {
380 Ok(f32::from_bits(self.to_u32()?))
384 pub fn to_f64(self) -> EvalResult<'static, f64> {
385 Ok(f64::from_bits(self.to_u64()?))
389 impl<Tag> From<Pointer<Tag>> for Scalar<Tag> {
391 fn from(ptr: Pointer<Tag>) -> Self {
396 #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
397 pub enum ScalarMaybeUndef<Tag=(), Id=AllocId> {
398 Scalar(Scalar<Tag, Id>),
402 impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
404 fn from(s: Scalar<Tag>) -> Self {
405 ScalarMaybeUndef::Scalar(s)
409 impl<Tag> fmt::Display for ScalarMaybeUndef<Tag> {
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 ScalarMaybeUndef::Undef => write!(f, "uninitialized bytes"),
413 ScalarMaybeUndef::Scalar(s) => write!(f, "{}", s),
418 impl<'tcx> ScalarMaybeUndef<()> {
420 pub fn with_default_tag<Tag>(self) -> ScalarMaybeUndef<Tag>
424 ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.with_default_tag()),
425 ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef,
430 impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
432 pub fn erase_tag(self) -> ScalarMaybeUndef
435 ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.erase_tag()),
436 ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef,
441 pub fn not_undef(self) -> EvalResult<'static, Scalar<Tag>> {
443 ScalarMaybeUndef::Scalar(scalar) => Ok(scalar),
444 ScalarMaybeUndef::Undef => err!(ReadUndefBytes(Size::from_bytes(0))),
449 pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
450 self.not_undef()?.to_ptr()
454 pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
455 self.not_undef()?.to_bits(target_size)
459 pub fn to_bool(self) -> EvalResult<'tcx, bool> {
460 self.not_undef()?.to_bool()
464 pub fn to_char(self) -> EvalResult<'tcx, char> {
465 self.not_undef()?.to_char()
469 pub fn to_f32(self) -> EvalResult<'tcx, f32> {
470 self.not_undef()?.to_f32()
474 pub fn to_f64(self) -> EvalResult<'tcx, f64> {
475 self.not_undef()?.to_f64()
479 pub fn to_u8(self) -> EvalResult<'tcx, u8> {
480 self.not_undef()?.to_u8()
484 pub fn to_u32(self) -> EvalResult<'tcx, u32> {
485 self.not_undef()?.to_u32()
489 pub fn to_u64(self) -> EvalResult<'tcx, u64> {
490 self.not_undef()?.to_u64()
494 pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, u64> {
495 self.not_undef()?.to_usize(cx)
499 pub fn to_i8(self) -> EvalResult<'tcx, i8> {
500 self.not_undef()?.to_i8()
504 pub fn to_i32(self) -> EvalResult<'tcx, i32> {
505 self.not_undef()?.to_i32()
509 pub fn to_i64(self) -> EvalResult<'tcx, i64> {
510 self.not_undef()?.to_i64()
514 pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, i64> {
515 self.not_undef()?.to_isize(cx)
519 impl_stable_hash_for!(enum ::mir::interpret::ScalarMaybeUndef {