]> git.proxmox.com Git - rustc.git/blob - src/librustc/mir/interpret/pointer.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / src / librustc / mir / interpret / pointer.rs
1 use crate::mir;
2 use crate::ty::layout::{self, HasDataLayout, Size};
3
4 use super::{
5 AllocId, EvalResult, InboundsCheck,
6 };
7
8 ////////////////////////////////////////////////////////////////////////////////
9 // Pointer arithmetic
10 ////////////////////////////////////////////////////////////////////////////////
11
12 pub trait PointerArithmetic: layout::HasDataLayout {
13 // These are not supposed to be overridden.
14
15 #[inline(always)]
16 fn pointer_size(&self) -> Size {
17 self.data_layout().pointer_size
18 }
19
20 //// Trunace the given value to the pointer size; also return whether there was an overflow
21 #[inline]
22 fn truncate_to_ptr(&self, val: u128) -> (u64, bool) {
23 let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
24 ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
25 }
26
27 #[inline]
28 fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
29 let (res, over) = self.overflowing_offset(val, i);
30 if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
31 }
32
33 #[inline]
34 fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
35 let (res, over1) = val.overflowing_add(i);
36 let (res, over2) = self.truncate_to_ptr(u128::from(res));
37 (res, over1 || over2)
38 }
39
40 #[inline]
41 fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> {
42 let (res, over) = self.overflowing_signed_offset(val, i128::from(i));
43 if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
44 }
45
46 // Overflow checking only works properly on the range from -u64 to +u64.
47 #[inline]
48 fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
49 // FIXME: is it possible to over/underflow here?
50 if i < 0 {
51 // trickery to ensure that i64::min_value() works fine
52 // this formula only works for true negative values, it panics for zero!
53 let n = u64::max_value() - (i as u64) + 1;
54 val.overflowing_sub(n)
55 } else {
56 self.overflowing_offset(val, i as u64)
57 }
58 }
59 }
60
61 impl<T: layout::HasDataLayout> PointerArithmetic for T {}
62
63
64 /// Pointer is generic over the type that represents a reference to Allocations,
65 /// thus making it possible for the most convenient representation to be used in
66 /// each context.
67 ///
68 /// Defaults to the index based and loosely coupled AllocId.
69 ///
70 /// Pointer is also generic over the `Tag` associated with each pointer,
71 /// which is used to do provenance tracking during execution.
72 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
73 pub struct Pointer<Tag=(),Id=AllocId> {
74 pub alloc_id: Id,
75 pub offset: Size,
76 pub tag: Tag,
77 }
78
79 static_assert!(POINTER_SIZE: ::std::mem::size_of::<Pointer>() == 16);
80
81 /// Produces a `Pointer` which points to the beginning of the Allocation
82 impl From<AllocId> for Pointer {
83 #[inline(always)]
84 fn from(alloc_id: AllocId) -> Self {
85 Pointer::new(alloc_id, Size::ZERO)
86 }
87 }
88
89 impl<'tcx> Pointer<()> {
90 #[inline(always)]
91 pub fn new(alloc_id: AllocId, offset: Size) -> Self {
92 Pointer { alloc_id, offset, tag: () }
93 }
94
95 #[inline(always)]
96 pub fn with_default_tag<Tag>(self) -> Pointer<Tag>
97 where Tag: Default
98 {
99 Pointer::new_with_tag(self.alloc_id, self.offset, Default::default())
100 }
101 }
102
103 impl<'tcx, Tag> Pointer<Tag> {
104 #[inline(always)]
105 pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
106 Pointer { alloc_id, offset, tag }
107 }
108
109 #[inline]
110 pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
111 Ok(Pointer::new_with_tag(
112 self.alloc_id,
113 Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
114 self.tag
115 ))
116 }
117
118 #[inline]
119 pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
120 let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
121 (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
122 }
123
124 #[inline(always)]
125 pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
126 self.overflowing_offset(i, cx).0
127 }
128
129 #[inline]
130 pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> EvalResult<'tcx, Self> {
131 Ok(Pointer::new_with_tag(
132 self.alloc_id,
133 Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
134 self.tag,
135 ))
136 }
137
138 #[inline]
139 pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
140 let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
141 (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
142 }
143
144 #[inline(always)]
145 pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
146 self.overflowing_signed_offset(i128::from(i), cx).0
147 }
148
149 #[inline(always)]
150 pub fn erase_tag(self) -> Pointer {
151 Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
152 }
153
154 #[inline(always)]
155 pub fn check_in_alloc(
156 self,
157 allocation_size: Size,
158 check: InboundsCheck,
159 ) -> EvalResult<'tcx, ()> {
160 if self.offset > allocation_size {
161 err!(PointerOutOfBounds {
162 ptr: self.erase_tag(),
163 check,
164 allocation_size,
165 })
166 } else {
167 Ok(())
168 }
169 }
170 }