]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/pointer.rs
Make is_mutable use PlaceRef instead of it's fields
[rust.git] / src / librustc / mir / interpret / pointer.rs
1 use std::fmt::{self, Display};
2
3 use crate::mir;
4 use crate::ty::layout::{self, HasDataLayout, Size};
5 use rustc_macros::HashStable;
6
7 use super::{
8     AllocId, InterpResult, PanicMessage
9 };
10
11 /// Used by `check_in_alloc` to indicate context of check
12 #[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
13 pub enum CheckInAllocMsg {
14     MemoryAccessTest,
15     NullPointerTest,
16     PointerArithmeticTest,
17     InboundsTest,
18 }
19
20 impl Display for CheckInAllocMsg {
21     /// When this is printed as an error the context looks like this
22     /// "{test name} failed: pointer must be in-bounds at offset..."
23     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24         write!(f, "{}", match *self {
25             CheckInAllocMsg::MemoryAccessTest => "Memory access",
26             CheckInAllocMsg::NullPointerTest => "Null pointer test",
27             CheckInAllocMsg::PointerArithmeticTest => "Pointer arithmetic",
28             CheckInAllocMsg::InboundsTest => "Inbounds test",
29         })
30     }
31 }
32
33 ////////////////////////////////////////////////////////////////////////////////
34 // Pointer arithmetic
35 ////////////////////////////////////////////////////////////////////////////////
36
37 pub trait PointerArithmetic: layout::HasDataLayout {
38     // These are not supposed to be overridden.
39
40     #[inline(always)]
41     fn pointer_size(&self) -> Size {
42         self.data_layout().pointer_size
43     }
44
45     /// Helper function: truncate given value-"overflowed flag" pair to pointer size and
46     /// update "overflowed flag" if there was an overflow.
47     /// This should be called by all the other methods before returning!
48     #[inline]
49     fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
50         let val = val as u128;
51         let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
52         ((val % max_ptr_plus_1) as u64, over || val >= max_ptr_plus_1)
53     }
54
55     #[inline]
56     fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
57         let res = val.overflowing_add(i);
58         self.truncate_to_ptr(res)
59     }
60
61     // Overflow checking only works properly on the range from -u64 to +u64.
62     #[inline]
63     fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) {
64         // FIXME: is it possible to over/underflow here?
65         if i < 0 {
66             // Trickery to ensure that i64::min_value() works fine: compute n = -i.
67             // This formula only works for true negative values, it overflows for zero!
68             let n = u64::max_value() - (i as u64) + 1;
69             let res = val.overflowing_sub(n);
70             self.truncate_to_ptr(res)
71         } else {
72             self.overflowing_offset(val, i as u64)
73         }
74     }
75
76     #[inline]
77     fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> {
78         let (res, over) = self.overflowing_offset(val, i);
79         if over { err!(Panic(PanicMessage::Overflow(mir::BinOp::Add))) } else { Ok(res) }
80     }
81
82     #[inline]
83     fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> {
84         let (res, over) = self.overflowing_signed_offset(val, i128::from(i));
85         if over { err!(Panic(PanicMessage::Overflow(mir::BinOp::Add))) } else { Ok(res) }
86     }
87 }
88
89 impl<T: layout::HasDataLayout> PointerArithmetic for T {}
90
91
92 /// Pointer is generic over the type that represents a reference to Allocations,
93 /// thus making it possible for the most convenient representation to be used in
94 /// each context.
95 ///
96 /// Defaults to the index based and loosely coupled AllocId.
97 ///
98 /// Pointer is also generic over the `Tag` associated with each pointer,
99 /// which is used to do provenance tracking during execution.
100 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd,
101          RustcEncodable, RustcDecodable, Hash, HashStable)]
102 pub struct Pointer<Tag=(),Id=AllocId> {
103     pub alloc_id: Id,
104     pub offset: Size,
105     pub tag: Tag,
106 }
107
108 static_assert_size!(Pointer, 16);
109
110 impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for Pointer<Tag, Id> {
111     default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112         write!(f, "{:?}.{:#x}[{:?}]", self.alloc_id, self.offset.bytes(), self.tag)
113     }
114 }
115 // Specialization for no tag
116 impl<Id: fmt::Debug> fmt::Debug for Pointer<(), Id> {
117     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118         write!(f, "{:?}.{:#x}", self.alloc_id, self.offset.bytes())
119     }
120 }
121
122 /// Produces a `Pointer` which points to the beginning of the Allocation
123 impl From<AllocId> for Pointer {
124     #[inline(always)]
125     fn from(alloc_id: AllocId) -> Self {
126         Pointer::new(alloc_id, Size::ZERO)
127     }
128 }
129
130 impl Pointer<()> {
131     #[inline(always)]
132     pub fn new(alloc_id: AllocId, offset: Size) -> Self {
133         Pointer { alloc_id, offset, tag: () }
134     }
135
136     #[inline(always)]
137     pub fn with_tag<Tag>(self, tag: Tag) -> Pointer<Tag>
138     {
139         Pointer::new_with_tag(self.alloc_id, self.offset, tag)
140     }
141 }
142
143 impl<'tcx, Tag> Pointer<Tag> {
144     #[inline(always)]
145     pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self {
146         Pointer { alloc_id, offset, tag }
147     }
148
149     #[inline]
150     pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
151         Ok(Pointer::new_with_tag(
152             self.alloc_id,
153             Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
154             self.tag
155         ))
156     }
157
158     #[inline]
159     pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
160         let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
161         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
162     }
163
164     #[inline(always)]
165     pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
166         self.overflowing_offset(i, cx).0
167     }
168
169     #[inline]
170     pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
171         Ok(Pointer::new_with_tag(
172             self.alloc_id,
173             Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
174             self.tag,
175         ))
176     }
177
178     #[inline]
179     pub fn overflowing_signed_offset(self, i: i128, cx: &impl HasDataLayout) -> (Self, bool) {
180         let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
181         (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over)
182     }
183
184     #[inline(always)]
185     pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
186         self.overflowing_signed_offset(i128::from(i), cx).0
187     }
188
189     #[inline(always)]
190     pub fn erase_tag(self) -> Pointer {
191         Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () }
192     }
193
194     #[inline(always)]
195     pub fn check_in_alloc(
196         self,
197         allocation_size: Size,
198         msg: CheckInAllocMsg,
199     ) -> InterpResult<'tcx, ()> {
200         if self.offset > allocation_size {
201             err!(PointerOutOfBounds {
202                 ptr: self.erase_tag(),
203                 msg,
204                 allocation_size,
205             })
206         } else {
207             Ok(())
208         }
209     }
210 }