1 use super::{AllocId, InterpResult}
;
3 use rustc_macros
::HashStable
;
4 use rustc_target
::abi
::{HasDataLayout, Size}
;
6 use std
::{fmt, num::NonZeroU64}
;
8 ////////////////////////////////////////////////////////////////////////////////
10 ////////////////////////////////////////////////////////////////////////////////
12 pub trait PointerArithmetic
: HasDataLayout
{
13 // These are not supposed to be overridden.
16 fn pointer_size(&self) -> Size
{
17 self.data_layout().pointer_size
21 fn max_size_of_val(&self) -> Size
{
22 Size
::from_bytes(self.target_isize_max())
26 fn target_usize_max(&self) -> u64 {
27 self.pointer_size().unsigned_int_max().try_into().unwrap()
31 fn target_isize_min(&self) -> i64 {
32 self.pointer_size().signed_int_min().try_into().unwrap()
36 fn target_isize_max(&self) -> i64 {
37 self.pointer_size().signed_int_max().try_into().unwrap()
41 fn target_usize_to_isize(&self, val
: u64) -> i64 {
43 // Now wrap-around into the machine_isize range.
44 if val
> self.target_isize_max() {
45 // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
47 debug_assert
!(self.pointer_size().bits() < 64);
48 let max_usize_plus_1
= 1u128 << self.pointer_size().bits();
49 val
- i64::try_from(max_usize_plus_1
).unwrap()
55 /// Helper function: truncate given value-"overflowed flag" pair to pointer size and
56 /// update "overflowed flag" if there was an overflow.
57 /// This should be called by all the other methods before returning!
59 fn truncate_to_ptr(&self, (val
, over
): (u64, bool
)) -> (u64, bool
) {
60 let val
= u128
::from(val
);
61 let max_ptr_plus_1
= 1u128 << self.pointer_size().bits();
62 (u64::try_from(val
% max_ptr_plus_1
).unwrap(), over
|| val
>= max_ptr_plus_1
)
66 fn overflowing_offset(&self, val
: u64, i
: u64) -> (u64, bool
) {
67 // We do not need to check if i fits in a machine usize. If it doesn't,
68 // either the wrapping_add will wrap or res will not fit in a pointer.
69 let res
= val
.overflowing_add(i
);
70 self.truncate_to_ptr(res
)
74 fn overflowing_signed_offset(&self, val
: u64, i
: i64) -> (u64, bool
) {
75 // We need to make sure that i fits in a machine isize.
76 let n
= i
.unsigned_abs();
78 let (val
, over
) = self.overflowing_offset(val
, n
);
79 (val
, over
|| i
> self.target_isize_max())
81 let res
= val
.overflowing_sub(n
);
82 let (val
, over
) = self.truncate_to_ptr(res
);
83 (val
, over
|| i
< self.target_isize_min())
88 fn offset
<'tcx
>(&self, val
: u64, i
: u64) -> InterpResult
<'tcx
, u64> {
89 let (res
, over
) = self.overflowing_offset(val
, i
);
90 if over { throw_ub!(PointerArithOverflow) }
else { Ok(res) }
94 fn signed_offset
<'tcx
>(&self, val
: u64, i
: i64) -> InterpResult
<'tcx
, u64> {
95 let (res
, over
) = self.overflowing_signed_offset(val
, i
);
96 if over { throw_ub!(PointerArithOverflow) }
else { Ok(res) }
100 impl<T
: HasDataLayout
> PointerArithmetic
for T {}
102 /// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is
103 /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to
104 /// some global state.
105 /// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`.
106 pub trait Provenance
: Copy
+ fmt
::Debug
+ '
static {
107 /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
108 /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
109 /// different from what the Abstract Machine prescribes, so the interpreter must prevent any
110 /// operation that would inspect the underlying bytes of a pointer, such as ptr-to-int
111 /// transmutation. A `ReadPointerAsBytes` error will be raised in such situations.
112 /// - If `true`, the interpreter will permit operations to inspect the underlying bytes of a
113 /// pointer, and implement ptr-to-int transmutation by stripping provenance.
114 const OFFSET_IS_ADDR
: bool
;
116 /// Determines how a pointer should be printed.
117 fn fmt(ptr
: &Pointer
<Self>, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
;
119 /// If `OFFSET_IS_ADDR == false`, provenance must always be able to
120 /// identify the allocation this ptr points to (i.e., this must return `Some`).
121 /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
122 /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
123 fn get_alloc_id(self) -> Option
<AllocId
>;
125 /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance.
126 fn join(left
: Option
<Self>, right
: Option
<Self>) -> Option
<Self>;
129 /// The type of provenance in the compile-time interpreter.
130 /// This is a packed representation of an `AllocId` and an `immutable: bool`.
131 #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
132 pub struct CtfeProvenance(NonZeroU64
);
134 impl From
<AllocId
> for CtfeProvenance
{
135 fn from(value
: AllocId
) -> Self {
136 let prov
= CtfeProvenance(value
.0);
137 assert
!(!prov
.immutable(), "`AllocId` with the highest bit set cannot be used in CTFE");
142 impl fmt
::Debug
for CtfeProvenance
{
143 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
144 fmt
::Debug
::fmt(&self.alloc_id(), f
)?
; // propagates `alternate` flag
145 if self.immutable() {
152 const IMMUTABLE_MASK
: u64 = 1 << 63; // the highest bit
154 impl CtfeProvenance
{
155 /// Returns the `AllocId` of this provenance.
157 pub fn alloc_id(self) -> AllocId
{
158 AllocId(NonZeroU64
::new(self.0.get() & !IMMUTABLE_MASK
).unwrap())
161 /// Returns whether this provenance is immutable.
163 pub fn immutable(self) -> bool
{
164 self.0.get() & IMMUTABLE_MASK
!= 0
167 /// Returns an immutable version of this provenance.
169 pub fn as_immutable(self) -> Self {
170 CtfeProvenance(self.0 | IMMUTABLE_MASK
)
174 impl Provenance
for CtfeProvenance
{
175 // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
176 // so ptr-to-int casts are not possible (since we do not know the global physical offset).
177 const OFFSET_IS_ADDR
: bool
= false;
179 fn fmt(ptr
: &Pointer
<Self>, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
181 fmt
::Debug
::fmt(&ptr
.provenance
.alloc_id(), f
)?
; // propagates `alternate` flag
182 // Print offset only if it is non-zero.
183 if ptr
.offset
.bytes() > 0 {
184 write
!(f
, "+{:#x}", ptr
.offset
.bytes())?
;
186 // Print immutable status.
187 if ptr
.provenance
.immutable() {
193 fn get_alloc_id(self) -> Option
<AllocId
> {
194 Some(self.alloc_id())
197 fn join(_left
: Option
<Self>, _right
: Option
<Self>) -> Option
<Self> {
198 panic
!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
202 // We also need this impl so that one can debug-print `Pointer<AllocId>`
203 impl Provenance
for AllocId
{
204 // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
205 // so ptr-to-int casts are not possible (since we do not know the global physical offset).
206 const OFFSET_IS_ADDR
: bool
= false;
208 fn fmt(ptr
: &Pointer
<Self>, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
209 // Forward `alternate` flag to `alloc_id` printing.
211 write
!(f
, "{:#?}", ptr
.provenance
)?
;
213 write
!(f
, "{:?}", ptr
.provenance
)?
;
215 // Print offset only if it is non-zero.
216 if ptr
.offset
.bytes() > 0 {
217 write
!(f
, "+{:#x}", ptr
.offset
.bytes())?
;
222 fn get_alloc_id(self) -> Option
<AllocId
> {
226 fn join(_left
: Option
<Self>, _right
: Option
<Self>) -> Option
<Self> {
227 panic
!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
231 /// Represents a pointer in the Miri engine.
233 /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
234 #[derive(Copy, Clone, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
235 #[derive(HashStable)]
236 pub struct Pointer
<Prov
= CtfeProvenance
> {
237 pub(super) offset
: Size
, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
238 pub provenance
: Prov
,
241 static_assert_size
!(Pointer
, 16);
242 // `Option<Prov>` pointers are also passed around quite a bit
243 // (but not stored in permanent machine state).
244 static_assert_size
!(Pointer
<Option
<CtfeProvenance
>>, 16);
246 // We want the `Debug` output to be readable as it is used by `derive(Debug)` for
247 // all the Miri types.
248 impl<Prov
: Provenance
> fmt
::Debug
for Pointer
<Prov
> {
249 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
250 Provenance
::fmt(self, f
)
254 impl<Prov
: Provenance
> fmt
::Debug
for Pointer
<Option
<Prov
>> {
255 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
256 match self.provenance
{
257 Some(prov
) => Provenance
::fmt(&Pointer
::new(prov
, self.offset
), f
),
258 None
=> write
!(f
, "{:#x}[noalloc]", self.offset
.bytes()),
263 impl<Prov
: Provenance
> fmt
::Display
for Pointer
<Option
<Prov
>> {
264 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
265 if self.provenance
.is_none() && self.offset
.bytes() == 0 {
266 write
!(f
, "null pointer")
268 fmt
::Debug
::fmt(self, f
)
273 /// Produces a `Pointer` that points to the beginning of the `Allocation`.
274 impl From
<AllocId
> for Pointer
{
276 fn from(alloc_id
: AllocId
) -> Self {
277 Pointer
::new(alloc_id
.into(), Size
::ZERO
)
281 impl<Prov
> From
<Pointer
<Prov
>> for Pointer
<Option
<Prov
>> {
283 fn from(ptr
: Pointer
<Prov
>) -> Self {
284 let (prov
, offset
) = ptr
.into_parts();
285 Pointer
::new(Some(prov
), offset
)
289 impl<Prov
> Pointer
<Option
<Prov
>> {
290 /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a
291 /// provenance, or an absolute address.
293 /// This is rarely what you want; call `ptr_try_get_alloc_id` instead.
294 pub fn into_pointer_or_addr(self) -> Result
<Pointer
<Prov
>, Size
> {
295 match self.provenance
{
296 Some(prov
) => Ok(Pointer
::new(prov
, self.offset
)),
297 None
=> Err(self.offset
),
301 /// Returns the absolute address the pointer points to.
302 /// Only works if Prov::OFFSET_IS_ADDR is true!
303 pub fn addr(self) -> Size
307 assert
!(Prov
::OFFSET_IS_ADDR
);
312 impl<Prov
> Pointer
<Option
<Prov
>> {
313 /// Creates a pointer to the given address, with invalid provenance (i.e., cannot be used for
314 /// any memory access).
316 pub fn from_addr_invalid(addr
: u64) -> Self {
317 Pointer { provenance: None, offset: Size::from_bytes(addr) }
321 pub fn null() -> Self {
322 Pointer
::from_addr_invalid(0)
326 impl<'tcx
, Prov
> Pointer
<Prov
> {
328 pub fn new(provenance
: Prov
, offset
: Size
) -> Self {
329 Pointer { provenance, offset }
332 /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`!
333 /// This function must only be used in the implementation of `Machine::ptr_get_alloc`,
334 /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`.
336 pub fn into_parts(self) -> (Prov
, Size
) {
337 (self.provenance
, self.offset
)
340 pub fn map_provenance(self, f
: impl FnOnce(Prov
) -> Prov
) -> Self {
341 Pointer { provenance: f(self.provenance), ..self }
345 pub fn offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
347 offset
: Size
::from_bytes(cx
.data_layout().offset(self.offset
.bytes(), i
.bytes())?
),
353 pub fn overflowing_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> (Self, bool
) {
354 let (res
, over
) = cx
.data_layout().overflowing_offset(self.offset
.bytes(), i
.bytes());
355 let ptr
= Pointer { offset: Size::from_bytes(res), ..self }
;
360 pub fn wrapping_offset(self, i
: Size
, cx
: &impl HasDataLayout
) -> Self {
361 self.overflowing_offset(i
, cx
).0
365 pub fn signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> InterpResult
<'tcx
, Self> {
367 offset
: Size
::from_bytes(cx
.data_layout().signed_offset(self.offset
.bytes(), i
)?
),
373 pub fn overflowing_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> (Self, bool
) {
374 let (res
, over
) = cx
.data_layout().overflowing_signed_offset(self.offset
.bytes(), i
);
375 let ptr
= Pointer { offset: Size::from_bytes(res), ..self }
;
380 pub fn wrapping_signed_offset(self, i
: i64, cx
: &impl HasDataLayout
) -> Self {
381 self.overflowing_signed_offset(i
, cx
).0