Rollup merge of #127275 - RalfJung:offset-from-isize-min, r=Amanieu
offset_from, offset: clearly separate safety requirements the user needs to prove from corollaries that automatically follow By landing https://github.com/rust-lang/rust/pull/116675 we decided that objects larger than `isize::MAX` cannot exist in the address space of a Rust program, which lets us simplify these rules. For `offset_from`, we can even state that the *absolute* distance fits into an `isize`, and therefore exclude `isize::MIN`. This PR also changes Miri to treat an `isize::MIN` difference like the other isize-overflowing cases.
This commit is contained in:
commit
2137d19ef6
6 changed files with 139 additions and 272 deletions
|
@ -301,9 +301,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
}
|
}
|
||||||
// The signed form of the intrinsic allows this. If we interpret the
|
// The signed form of the intrinsic allows this. If we interpret the
|
||||||
// difference as isize, we'll get the proper signed difference. If that
|
// difference as isize, we'll get the proper signed difference. If that
|
||||||
// seems *positive*, they were more than isize::MAX apart.
|
// seems *positive* or equal to isize::MIN, they were more than isize::MAX apart.
|
||||||
let dist = val.to_target_isize(self)?;
|
let dist = val.to_target_isize(self)?;
|
||||||
if dist >= 0 {
|
if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() {
|
||||||
throw_ub_custom!(
|
throw_ub_custom!(
|
||||||
fluent::const_eval_offset_from_underflow,
|
fluent::const_eval_offset_from_underflow,
|
||||||
name = intrinsic_name,
|
name = intrinsic_name,
|
||||||
|
|
|
@ -390,37 +390,26 @@ impl<T: ?Sized> *const T {
|
||||||
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
|
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer.
|
/// Adds an offset to a pointer.
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum, **in bytes** must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_offset`] instead if these constraints are
|
/// Consider using [`wrapping_offset`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
@ -611,8 +600,7 @@ impl<T: ?Sized> *const T {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * `self` and `origin` must either
|
/// * `self` and `origin` must either
|
||||||
///
|
///
|
||||||
|
@ -623,26 +611,10 @@ impl<T: ?Sized> *const T {
|
||||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||||
/// of the size of `T`.
|
/// of the size of `T`.
|
||||||
///
|
///
|
||||||
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
|
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||||
///
|
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||||
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
|
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||||
///
|
/// than `isize::MAX` bytes.
|
||||||
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
|
|
||||||
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
|
|
||||||
/// the last two conditions. The standard library also generally ensures that allocations
|
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
|
|
||||||
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
|
|
||||||
/// always satisfies the last two conditions.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such a large allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
|
|
||||||
/// such large allocations either.)
|
|
||||||
///
|
///
|
||||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||||
|
@ -879,37 +851,26 @@ impl<T: ?Sized> *const T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
/// Adds an offset to a pointer (convenience for `.offset(count as isize)`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a `usize`.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_add`] instead if these constraints are
|
/// Consider using [`wrapping_add`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
@ -963,7 +924,7 @@ impl<T: ?Sized> *const T {
|
||||||
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
|
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for
|
/// Subtracts an offset from a pointer (convenience for
|
||||||
/// `.offset((count as isize).wrapping_neg())`).
|
/// `.offset((count as isize).wrapping_neg())`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
|
@ -971,30 +932,19 @@ impl<T: ?Sized> *const T {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_sub`] instead if these constraints are
|
/// Consider using [`wrapping_sub`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
|
|
@ -404,37 +404,26 @@ impl<T: ?Sized> *mut T {
|
||||||
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
|
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer.
|
/// Adds an offset to a pointer.
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum, **in bytes** must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_offset`] instead if these constraints are
|
/// Consider using [`wrapping_offset`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
@ -836,8 +825,7 @@ impl<T: ?Sized> *mut T {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * `self` and `origin` must either
|
/// * `self` and `origin` must either
|
||||||
///
|
///
|
||||||
|
@ -848,26 +836,10 @@ impl<T: ?Sized> *mut T {
|
||||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||||
/// of the size of `T`.
|
/// of the size of `T`.
|
||||||
///
|
///
|
||||||
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
|
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||||
///
|
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||||
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
|
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||||
///
|
/// than `isize::MAX` bytes.
|
||||||
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
|
|
||||||
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
|
|
||||||
/// the last two conditions. The standard library also generally ensures that allocations
|
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
|
|
||||||
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
|
|
||||||
/// always satisfies the last two conditions.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such a large allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
|
|
||||||
/// such large allocations either.)
|
|
||||||
///
|
///
|
||||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||||
|
@ -1020,37 +992,26 @@ impl<T: ?Sized> *mut T {
|
||||||
unsafe { (self as *const T).sub_ptr(origin) }
|
unsafe { (self as *const T).sub_ptr(origin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
/// Adds an offset to a pointer (convenience for `.offset(count as isize)`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a `usize`.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_add`] instead if these constraints are
|
/// Consider using [`wrapping_add`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
@ -1104,7 +1065,7 @@ impl<T: ?Sized> *mut T {
|
||||||
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
|
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for
|
/// Subtracts an offset from a pointer (convenience for
|
||||||
/// `.offset((count as isize).wrapping_neg())`).
|
/// `.offset((count as isize).wrapping_neg())`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
|
@ -1112,30 +1073,19 @@ impl<T: ?Sized> *mut T {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// pointer must be either in bounds or at the end of the same [allocated object].
|
|
||||||
/// (If it is zero, then the function is always well-defined.)
|
|
||||||
///
|
///
|
||||||
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// Consider using [`wrapping_sub`] instead if these constraints are
|
/// Consider using [`wrapping_sub`] instead if these constraints are
|
||||||
/// difficult to satisfy. The only advantage of this method is that it
|
/// difficult to satisfy. The only advantage of this method is that it
|
||||||
|
|
|
@ -476,36 +476,26 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
unsafe { NonNull { pointer: self.as_ptr() as *mut U } }
|
unsafe { NonNull { pointer: self.as_ptr() as *mut U } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer.
|
/// Adds an offset to a pointer.
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * Both the starting and resulting pointer must be either in bounds or one
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// byte past the end of the same [allocated object].
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum, **in bytes** must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// [allocated object]: crate::ptr#allocated-object
|
/// [allocated object]: crate::ptr#allocated-object
|
||||||
///
|
///
|
||||||
|
@ -562,36 +552,26 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
unsafe { NonNull { pointer: self.pointer.byte_offset(count) } }
|
unsafe { NonNull { pointer: self.pointer.byte_offset(count) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
/// Adds an offset to a pointer (convenience for `.offset(count as isize)`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
/// offset of `3 * size_of::<T>()` bytes.
|
/// offset of `3 * size_of::<T>()` bytes.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * Both the starting and resulting pointer must be either in bounds or one
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// byte past the end of the same [allocated object].
|
|
||||||
///
|
///
|
||||||
/// * The computed offset, **in bytes**, cannot overflow an `isize`.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a `usize`.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// [allocated object]: crate::ptr#allocated-object
|
/// [allocated object]: crate::ptr#allocated-object
|
||||||
///
|
///
|
||||||
|
@ -649,7 +629,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
unsafe { NonNull { pointer: self.pointer.byte_add(count) } }
|
unsafe { NonNull { pointer: self.pointer.byte_add(count) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for
|
/// Subtracts an offset from a pointer (convenience for
|
||||||
/// `.offset((count as isize).wrapping_neg())`).
|
/// `.offset((count as isize).wrapping_neg())`).
|
||||||
///
|
///
|
||||||
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
/// `count` is in units of T; e.g., a `count` of 3 represents a pointer
|
||||||
|
@ -657,29 +637,19 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * Both the starting and resulting pointer must be either in bounds or one
|
/// * The computed offset, `count * size_of::<T>()` bytes, must not overflow `isize`.
|
||||||
/// byte past the end of the same [allocated object].
|
|
||||||
///
|
///
|
||||||
/// * The computed offset cannot exceed `isize::MAX` **bytes**.
|
/// * If the computed offset is non-zero, then `self` must be derived from a pointer to some
|
||||||
|
/// [allocated object], and the entire memory range between `self` and the result must be in
|
||||||
|
/// bounds of that allocated object. In particular, this range must not "wrap around" the edge
|
||||||
|
/// of the address space.
|
||||||
///
|
///
|
||||||
/// * The offset being in bounds cannot rely on "wrapping around" the address
|
/// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset
|
||||||
/// space. That is, the infinite-precision sum must fit in a usize.
|
/// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement.
|
||||||
///
|
/// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec<T>`) is always
|
||||||
/// The compiler and standard library generally tries to ensure allocations
|
/// safe.
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec`
|
|
||||||
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
|
|
||||||
/// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such an allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
///
|
///
|
||||||
/// [allocated object]: crate::ptr#allocated-object
|
/// [allocated object]: crate::ptr#allocated-object
|
||||||
///
|
///
|
||||||
|
@ -761,38 +731,21 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If any of the following conditions are violated, the result is Undefined
|
/// If any of the following conditions are violated, the result is Undefined Behavior:
|
||||||
/// Behavior:
|
|
||||||
///
|
///
|
||||||
/// * Both `self` and `origin` must be either in bounds or one
|
/// * `self` and `origin` must either
|
||||||
/// byte past the end of the same [allocated object].
|
|
||||||
///
|
///
|
||||||
/// * Both pointers must be *derived from* a pointer to the same object.
|
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
|
||||||
/// (See below for an example.)
|
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
|
||||||
|
/// * or both be derived from an integer literal/constant, and point to the same address.
|
||||||
///
|
///
|
||||||
/// * The distance between the pointers, in bytes, must be an exact multiple
|
/// * The distance between the pointers, in bytes, must be an exact multiple
|
||||||
/// of the size of `T`.
|
/// of the size of `T`.
|
||||||
///
|
///
|
||||||
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
|
/// As a consequence, the absolute distance between the pointers, in bytes, computed on
|
||||||
///
|
/// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is
|
||||||
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
|
/// implied by the in-bounds requirement, and the fact that no allocated object can be larger
|
||||||
///
|
/// than `isize::MAX` bytes.
|
||||||
/// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the
|
|
||||||
/// address space, so two pointers within some value of any Rust type `T` will always satisfy
|
|
||||||
/// the last two conditions. The standard library also generally ensures that allocations
|
|
||||||
/// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they
|
|
||||||
/// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
|
|
||||||
/// always satisfies the last two conditions.
|
|
||||||
///
|
|
||||||
/// Most platforms fundamentally can't even construct such a large allocation.
|
|
||||||
/// For instance, no known 64-bit platform can ever serve a request
|
|
||||||
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
|
|
||||||
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
|
|
||||||
/// more than `isize::MAX` bytes with things like Physical Address
|
|
||||||
/// Extension. As such, memory acquired directly from allocators or memory
|
|
||||||
/// mapped files *may* be too large to handle with this function.
|
|
||||||
/// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on
|
|
||||||
/// such large allocations either.)
|
|
||||||
///
|
///
|
||||||
/// The requirement for pointers to be derived from the same allocated object is primarily
|
/// The requirement for pointers to be derived from the same allocated object is primarily
|
||||||
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
/// needed for `const`-compatibility: the distance between pointers into *different* allocated
|
||||||
|
|
|
@ -92,6 +92,14 @@ pub const TOO_FAR_APART2: isize = {
|
||||||
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
|
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
|
||||||
//~| too far before
|
//~| too far before
|
||||||
};
|
};
|
||||||
|
pub const TOO_FAR_APART3: isize = {
|
||||||
|
let ptr1 = &0u8 as *const u8;
|
||||||
|
let ptr2 = ptr1.wrapping_offset(isize::MIN);
|
||||||
|
// The result of this would be `isize::MIN`, which *does* fit in an `isize`, but its
|
||||||
|
// absolute value does not. (Also anyway there cannot be an allocation of that size.)
|
||||||
|
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
|
||||||
|
//~| too far before
|
||||||
|
};
|
||||||
|
|
||||||
const WRONG_ORDER_UNSIGNED: usize = {
|
const WRONG_ORDER_UNSIGNED: usize = {
|
||||||
let a = ['a', 'b', 'c'];
|
let a = ['a', 'b', 'c'];
|
||||||
|
|
|
@ -60,13 +60,19 @@ LL | unsafe { ptr_offset_from(ptr1, ptr2) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
|
||||||
|
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/offset_from_ub.rs:99:14
|
--> $DIR/offset_from_ub.rs:100:14
|
||||||
|
|
|
||||||
|
LL | unsafe { ptr_offset_from(ptr1, ptr2) }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
|
||||||
|
|
||||||
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> $DIR/offset_from_ub.rs:107:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
|
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8
|
||||||
|
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/offset_from_ub.rs:106:14
|
--> $DIR/offset_from_ub.rs:114:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
|
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
|
||||||
|
@ -79,7 +85,7 @@ error[E0080]: evaluation of constant value failed
|
||||||
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
|
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
|
||||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
note: inside `OFFSET_VERY_FAR1`
|
note: inside `OFFSET_VERY_FAR1`
|
||||||
--> $DIR/offset_from_ub.rs:115:14
|
--> $DIR/offset_from_ub.rs:123:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr2.offset_from(ptr1) }
|
LL | unsafe { ptr2.offset_from(ptr1) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -92,11 +98,11 @@ error[E0080]: evaluation of constant value failed
|
||||||
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
|
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
|
||||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
note: inside `OFFSET_VERY_FAR2`
|
note: inside `OFFSET_VERY_FAR2`
|
||||||
--> $DIR/offset_from_ub.rs:121:14
|
--> $DIR/offset_from_ub.rs:129:14
|
||||||
|
|
|
|
||||||
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
|
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 13 previous errors
|
error: aborting due to 14 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0080`.
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue