]> git.lizzy.rs Git - rust.git/blob - src/librustc_middle/mir/interpret/pointer.rs
Rework `rustc_serialize`
[rust.git] / src / librustc_middle / mir / interpret / pointer.rs
1 use super::{uabs, AllocId, InterpResult};
2
3 use rustc_macros::HashStable;
4 use rustc_target::abi::{HasDataLayout, Size};
5
6 use std::convert::TryFrom;
7 use std::fmt;
8
9 ////////////////////////////////////////////////////////////////////////////////
10 // Pointer arithmetic
11 ////////////////////////////////////////////////////////////////////////////////
12
13 pub trait PointerArithmetic: 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     #[inline]
22     fn machine_usize_max(&self) -> u64 {
23         let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
24         u64::try_from(max_usize_plus_1 - 1).unwrap()
25     }
26
27     #[inline]
28     fn machine_isize_min(&self) -> i64 {
29         let max_isize_plus_1 = 1i128 << (self.pointer_size().bits() - 1);
30         i64::try_from(-max_isize_plus_1).unwrap()
31     }
32
33     #[inline]
34     fn machine_isize_max(&self) -> i64 {
35         let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1);
36         i64::try_from(max_isize_plus_1 - 1).unwrap()
37     }
38
39     /// Helper function: truncate given value-"overflowed flag" pair to pointer size and
40     /// update "overflowed flag" if there was an overflow.
41     /// This should be called by all the other methods before returning!
42     #[inline]
43     fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
44         let val = u128::from(val);
45         let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
46         (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
47     }
48
49     #[inline]
50     fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
51         // We do not need to check if i fits in a machine usize. If it doesn't,
52         // either the wrapping_add will wrap or res will not fit in a pointer.
53         let res = val.overflowing_add(i);
54         self.truncate_to_ptr(res)
55     }
56
57     #[inline]
58     fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
59         // We need to make sure that i fits in a machine isize.
60         let n = uabs(i);
61         if i >= 0 {
62             let (val, over) = self.overflowing_offset(val, n);
63             (val, over || i > self.machine_isize_max())
64         } else {
65             let res = val.overflowing_sub(n);
66             let (val, over) = self.truncate_to_ptr(res);
67             (val, over || i < self.machine_isize_min())
68         }
69     }
70
71     #[inline]
72     fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> {
73         let (res, over) = self.overflowing_offset(val, i);
74         if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
75     }
76
77     #[inline]
78     fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> {
79         let (res, over) = self.overflowing_signed_offset(val, i);
80         if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
81     }
82 }
83
84 impl<T: HasDataLayout> PointerArithmetic for T {}
85
86 /// Represents a pointer in the Miri engine.
87 ///
88 /// `Pointer` is generic over the `Tag` associated with each pointer,
89 /// which is used to do provenance tracking during execution.
90 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
91 #[derive(HashStable)]
92 pub struct Pointer<Tag = ()> {
93     pub alloc_id: AllocId,
94     pub offset: Size,
95     pub tag: Tag,
96 }
97
98 static_assert_size!(Pointer, 16);
99
100 /// Print the address of a pointer (without the tag)
101 fn print_ptr_addr<Tag>(ptr: &Pointer<Tag>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102     // Forward `alternate` flag to `alloc_id` printing.
103     if f.alternate() {
104         write!(f, "{:#?}", ptr.alloc_id)?;
105     } else {
106         write!(f, "{:?}", ptr.alloc_id)?;
107     }
108     // Print offset only if it is non-zero.
109     if ptr.offset.bytes() > 0 {
110         write!(f, "+0x{:x}", ptr.offset.bytes())?;
111     }
112     Ok(())
113 }
114
115 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
116 // all the Miri types.
117 // We have to use `Debug` output for the tag, because `()` does not implement
118 // `Display` so we cannot specialize that.
119 impl<Tag: fmt::Debug> fmt::Debug for Pointer<Tag> {
120     default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121         print_ptr_addr(self, f)?;
122         write!(f, "[{:?}]", self.tag)
123     }
124 }
125 // Specialization for no tag
126 impl fmt::Debug for Pointer<()> {
127     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128         print_ptr_addr(self, f)
129     }
130 }
131
132 impl<Tag: fmt::Debug> fmt::Display for Pointer<Tag> {
133     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134         fmt::Debug::fmt(self, f)
135     }
136 }
137
138 /// Produces a `Pointer` that points to the beginning of the `Allocation`.
139 impl From<AllocId> for Pointer {
140     #[inline(always)]
141     fn from(alloc_id: AllocId) -> Self {
142         Pointer::new(alloc_id, Size::ZERO)
143     }
144 }
145
146 impl Pointer<()> {
147     #[inline(always)]
148     pub fn new(alloc_id: AllocId, offset: Size) -> Self {
149         Pointer { alloc_id, offset, tag: () }
150     }
151
152     #[inline(always)]
153     pub fn with_tag<Tag>(self, tag: Tag) -> Pointer<Tag> {
154         Pointer::new_with_tag(self.alloc_id, self.offset, tag)
155     }
156 }
157
158 impl<'tcx, Tag> Pointer<Tag> {
159     #[inline(always)]
160     pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
161         Pointer { alloc_id, offset, tag }
162     }
163
164     #[inline]
165     pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
166         Ok(Pointer::new_with_tag(
167             self.alloc_id,
168             Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
169             self.tag,
170         ))
171     }
172
173     #[inline]
174     pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
175         let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
176         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
177     }
178
179     #[inline(always)]
180     pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
181         self.overflowing_offset(i, cx).0
182     }
183
184     #[inline]
185     pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
186         Ok(Pointer::new_with_tag(
187             self.alloc_id,
188             Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
189             self.tag,
190         ))
191     }
192
193     #[inline]
194     pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) {
195         let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
196         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
197     }
198
199     #[inline(always)]
200     pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
201         self.overflowing_signed_offset(i, cx).0
202     }
203
204     #[inline(always)]
205     pub fn erase_tag(self) -> Pointer {
206         Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
207     }
208 }