2 use crate::ty::layout::{self, HasDataLayout, Size};
3 use rustc_macros::HashStable;
6 AllocId, EvalResult, InboundsCheck,
9 ////////////////////////////////////////////////////////////////////////////////
11 ////////////////////////////////////////////////////////////////////////////////
13 pub trait PointerArithmetic: layout::HasDataLayout {
14 // These are not supposed to be overridden.
17 fn pointer_size(&self) -> Size {
18 self.data_layout().pointer_size
21 //// Trunace the given value to the pointer size; also return whether there was an overflow
23 fn truncate_to_ptr(&self, val: u128) -> (u64, bool) {
24 let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
25 ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
29 fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
30 let (res, over) = self.overflowing_offset(val, i);
31 if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
35 fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
36 let (res, over1) = val.overflowing_add(i);
37 let (res, over2) = self.truncate_to_ptr(u128::from(res));
42 fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
43 let (res, over) = self.overflowing_signed_offset(val, i128::from(i));
44 if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
47 // Overflow checking only works properly on the range from -u64 to +u64.
49 fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
50 // FIXME: is it possible to over/underflow here?
52 // trickery to ensure that i64::min_value() works fine
53 // this formula only works for true negative values, it panics for zero!
54 let n = u64::max_value() - (i as u64) + 1;
55 val.overflowing_sub(n)
57 self.overflowing_offset(val, i as u64)
62 impl<T: layout::HasDataLayout> PointerArithmetic for T {}
65 /// Pointer is generic over the type that represents a reference to Allocations,
66 /// thus making it possible for the most convenient representation to be used in
69 /// Defaults to the index based and loosely coupled AllocId.
71 /// Pointer is also generic over the `Tag` associated with each pointer,
72 /// which is used to do provenance tracking during execution.
73 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd,
74 RustcEncodable, RustcDecodable, Hash, HashStable)]
75 pub struct Pointer<Tag=(),Id=AllocId> {
81 static_assert!(POINTER_SIZE: ::std::mem::size_of::<Pointer>() == 16);
83 /// Produces a `Pointer` which points to the beginning of the Allocation
84 impl From<AllocId> for Pointer {
86 fn from(alloc_id: AllocId) -> Self {
87 Pointer::new(alloc_id, Size::ZERO)
91 impl<'tcx> Pointer<()> {
93 pub fn new(alloc_id: AllocId, offset: Size) -> Self {
94 Pointer { alloc_id, offset, tag: () }
98 pub fn with_tag<Tag>(self, tag: Tag) -> Pointer<Tag>
100 Pointer::new_with_tag(self.alloc_id, self.offset, tag)
104 pub fn with_default_tag<Tag>(self) -> Pointer<Tag>
107 self.with_tag(Tag::default())
111 impl<'tcx, Tag> Pointer<Tag> {
113 pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
114 Pointer { alloc_id, offset, tag }
118 pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
119 Ok(Pointer::new_with_tag(
121 Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
127 pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
128 let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
129 (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
133 pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
134 self.overflowing_offset(i, cx).0
138 pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
139 Ok(Pointer::new_with_tag(
141 Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
147 pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
148 let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
149 (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
153 pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
154 self.overflowing_signed_offset(i128::from(i), cx).0
158 pub fn erase_tag(self) -> Pointer {
159 Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
163 pub fn check_in_alloc(
165 allocation_size: Size,
166 check: InboundsCheck,
167 ) -> EvalResult<'tcx, ()> {
168 if self.offset > allocation_size {
169 err!(PointerOutOfBounds {
170 ptr: self.erase_tag(),