]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/pointer.rs
Rollup merge of #60952 - dtolnay:heap, r=Amanieu
[rust.git] / src / librustc / mir / interpret / pointer.rs
1 use crate::mir;
2 use crate::ty::layout::{self, HasDataLayout, Size};
3 use rustc_macros::HashStable;
4
5 use super::{
6     AllocId, EvalResult, InboundsCheck,
7 };
8
9 ////////////////////////////////////////////////////////////////////////////////
10 // Pointer arithmetic
11 ////////////////////////////////////////////////////////////////////////////////
12
13 pub trait PointerArithmetic: layout::HasDataLayout {
14     // These are not supposed to be overridden.
15
16     #[inline(always)]
17     fn pointer_size(&self) -> Size {
18         self.data_layout().pointer_size
19     }
20
21     //// Trunace the given value to the pointer size; also return whether there was an overflow
22     #[inline]
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)
26     }
27
28     #[inline]
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) }
32     }
33
34     #[inline]
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));
38         (res, over1 || over2)
39     }
40
41     #[inline]
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) }
45     }
46
47     // Overflow checking only works properly on the range from -u64 to +u64.
48     #[inline]
49     fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
50         // FIXME: is it possible to over/underflow here?
51         if i < 0 {
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)
56         } else {
57             self.overflowing_offset(val, i as u64)
58         }
59     }
60 }
61
62 impl<T: layout::HasDataLayout> PointerArithmetic for T {}
63
64
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
67 /// each context.
68 ///
69 /// Defaults to the index based and loosely coupled AllocId.
70 ///
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> {
76     pub alloc_id: Id,
77     pub offset: Size,
78     pub tag: Tag,
79 }
80
81 static_assert!(POINTER_SIZE: ::std::mem::size_of::<Pointer>() == 16);
82
83 /// Produces a `Pointer` which points to the beginning of the Allocation
84 impl From<AllocId> for Pointer {
85     #[inline(always)]
86     fn from(alloc_id: AllocId) -> Self {
87         Pointer::new(alloc_id, Size::ZERO)
88     }
89 }
90
91 impl<'tcx> Pointer<()> {
92     #[inline(always)]
93     pub fn new(alloc_id: AllocId, offset: Size) -> Self {
94         Pointer { alloc_id, offset, tag: () }
95     }
96
97     #[inline(always)]
98     pub fn with_tag<Tag>(self, tag: Tag) -> Pointer<Tag>
99     {
100         Pointer::new_with_tag(self.alloc_id, self.offset, tag)
101     }
102
103     #[inline(always)]
104     pub fn with_default_tag<Tag>(self) -> Pointer<Tag>
105         where Tag: Default
106     {
107         self.with_tag(Tag::default())
108     }
109 }
110
111 impl<'tcx, Tag> Pointer<Tag> {
112     #[inline(always)]
113     pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
114         Pointer { alloc_id, offset, tag }
115     }
116
117     #[inline]
118     pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
119         Ok(Pointer::new_with_tag(
120             self.alloc_id,
121             Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
122             self.tag
123         ))
124     }
125
126     #[inline]
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)
130     }
131
132     #[inline(always)]
133     pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
134         self.overflowing_offset(i, cx).0
135     }
136
137     #[inline]
138     pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
139         Ok(Pointer::new_with_tag(
140             self.alloc_id,
141             Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
142             self.tag,
143         ))
144     }
145
146     #[inline]
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)
150     }
151
152     #[inline(always)]
153     pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
154         self.overflowing_signed_offset(i128::from(i), cx).0
155     }
156
157     #[inline(always)]
158     pub fn erase_tag(self) -> Pointer {
159         Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
160     }
161
162     #[inline(always)]
163     pub fn check_in_alloc(
164         self,
165         allocation_size: Size,
166         check: InboundsCheck,
167     ) -> EvalResult<'tcx, ()> {
168         if self.offset > allocation_size {
169             err!(PointerOutOfBounds {
170                 ptr: self.erase_tag(),
171                 check,
172                 allocation_size,
173             })
174         } else {
175             Ok(())
176         }
177     }
178 }