-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Introduce unsafe offset_from on pointers #49297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
68e0ea9
d6926ca
02b5851
4a097ea
6264952
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -700,6 +700,114 @@ impl<T: ?Sized> *const T { | |
} | ||
} | ||
|
||
/// Calculates the distance between two pointers. The returned value is in | ||
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. | ||
/// | ||
/// This function is the inverse of [`offset`]. | ||
/// | ||
/// [`offset`]: #method.offset | ||
/// [`wrapping_offset_from`]: #method.wrapping_offset_from | ||
/// | ||
/// # Safety | ||
/// | ||
/// If any of the following conditions are violated, the result is Undefined | ||
/// Behavior: | ||
/// | ||
/// * Both the starting and other pointer must be either in bounds or one | ||
/// byte past the end of the same allocated object. | ||
/// | ||
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. | ||
/// | ||
/// * The distance between the pointers, in bytes, must be an exact multiple | ||
/// of the size of `T` and `T` must not be a Zero-Sized Type ("ZST"). | ||
/// | ||
/// * The distance being in bounds cannot rely on "wrapping around" the address space. | ||
/// | ||
/// The compiler and standard library generally try to ensure 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())` 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_from`] instead if these constraints are | ||
/// difficult to satisfy. The only advantage of this method is that it | ||
/// enables more aggressive compiler optimizations. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// #![feature(ptr_offset_from)] | ||
/// | ||
/// let a = [0; 5]; | ||
/// let ptr1: *const i32 = &a[1]; | ||
/// let ptr2: *const i32 = &a[3]; | ||
/// unsafe { | ||
/// assert_eq!(ptr2.offset_from(ptr1), 2); | ||
/// assert_eq!(ptr1.offset_from(ptr2), -2); | ||
/// assert_eq!(ptr1.offset(2), ptr2); | ||
/// assert_eq!(ptr2.offset(-2), ptr1); | ||
/// } | ||
/// ``` | ||
#[unstable(feature = "ptr_offset_from", issue = "41079")] | ||
#[inline] | ||
pub unsafe fn offset_from(self, other: *const T) -> isize where T: Sized { | ||
let pointee_size = mem::size_of::<T>(); | ||
assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); | ||
|
||
// FIXME: can this be nuw/nsw? | ||
let d = isize::wrapping_sub(self as _, other as _); | ||
intrinsics::exact_div(d, pointee_size as _) | ||
} | ||
|
||
/// Calculates the distance between two pointers. The returned value is in | ||
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. | ||
/// | ||
/// If the address different between the two pointers is not a multiple of | ||
/// `mem::size_of::<T>()` then the result of the division is rounded towards | ||
/// zero. | ||
/// | ||
/// # Panics | ||
/// | ||
/// This function panics if `T` is a zero-sized typed. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// #![feature(ptr_wrapping_offset_from)] | ||
/// | ||
/// let a = [0; 5]; | ||
/// let ptr1: *const i32 = &a[1]; | ||
/// let ptr2: *const i32 = &a[3]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will panic at runtime. You do this a few other places as well. You should probably use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh? (But if it was, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I, uh, may be too used to OCaml, sorry -.- There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I'm just being stupid, array indexing is zero-based lol. Sorry, I should not comment when I'm tired :P |
||
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); | ||
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2); | ||
/// assert_eq!(ptr1.wrapping_offset(2), ptr2); | ||
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1); | ||
/// | ||
/// let ptr1: *const i32 = 3 as _; | ||
/// let ptr2: *const i32 = 13 as _; | ||
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); | ||
/// ``` | ||
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] | ||
#[inline] | ||
pub fn wrapping_offset_from(self, other: *const T) -> isize where T: Sized { | ||
let pointee_size = mem::size_of::<T>(); | ||
assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); | ||
|
||
let d = isize::wrapping_sub(self as _, other as _); | ||
d.wrapping_div(pointee_size as _) | ||
} | ||
|
||
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). | ||
/// | ||
/// `count` is in units of T; e.g. a `count` of 3 represents a pointer | ||
|
@@ -1347,6 +1455,105 @@ impl<T: ?Sized> *mut T { | |
} | ||
} | ||
|
||
/// Calculates the distance between two pointers. The returned value is in | ||
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. | ||
/// | ||
/// This function is the inverse of [`offset`]. | ||
/// | ||
/// [`offset`]: #method.offset-1 | ||
/// [`wrapping_offset_from`]: #method.wrapping_offset_from-1 | ||
/// | ||
/// # Safety | ||
/// | ||
/// If any of the following conditions are violated, the result is Undefined | ||
/// Behavior: | ||
/// | ||
/// * Both the starting and other pointer must be either in bounds or one | ||
/// byte past the end of the same allocated object. | ||
/// | ||
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. | ||
/// | ||
/// * The distance between the pointers, in bytes, must be an exact multiple | ||
/// of the size of `T` and `T` must not be a Zero-Sized Type ("ZST"). | ||
/// | ||
/// * The distance being in bounds cannot rely on "wrapping around" the address space. | ||
/// | ||
/// The compiler and standard library generally try to ensure 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())` 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_from`] instead if these constraints are | ||
/// difficult to satisfy. The only advantage of this method is that it | ||
/// enables more aggressive compiler optimizations. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// #![feature(ptr_offset_from)] | ||
/// | ||
/// let a = [0; 5]; | ||
/// let ptr1: *mut i32 = &mut a[1]; | ||
/// let ptr2: *mut i32 = &mut a[3]; | ||
/// unsafe { | ||
/// assert_eq!(ptr2.offset_from(ptr1), 2); | ||
/// assert_eq!(ptr1.offset_from(ptr2), -2); | ||
/// assert_eq!(ptr1.offset(2), ptr2); | ||
/// assert_eq!(ptr2.offset(-2), ptr1); | ||
/// } | ||
/// ``` | ||
#[unstable(feature = "ptr_offset_from", issue = "41079")] | ||
#[inline] | ||
pub unsafe fn offset_from(self, other: *const T) -> isize where T: Sized { | ||
(self as *const T).offset_from(other) | ||
} | ||
|
||
/// Calculates the distance between two pointers. The returned value is in | ||
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. | ||
/// | ||
/// If the address different between the two pointers is not a multiple of | ||
/// `mem::size_of::<T>()` then the result of the division is rounded towards | ||
/// zero. | ||
/// | ||
/// # Panics | ||
/// | ||
/// This function panics if `T` is a zero-sized typed. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Basic usage: | ||
/// | ||
/// ``` | ||
/// #![feature(ptr_wrapping_offset_from)] | ||
/// | ||
/// let a = [0; 5]; | ||
/// let ptr1: *mut i32 = &mut a[1]; | ||
/// let ptr2: *mut i32 = &mut a[3]; | ||
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); | ||
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2); | ||
/// assert_eq!(ptr1.wrapping_offset(2), ptr2); | ||
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1); | ||
/// | ||
/// let ptr1: *mut i32 = 3 as _; | ||
/// let ptr2: *mut i32 = 13 as _; | ||
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); | ||
/// ``` | ||
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] | ||
#[inline] | ||
pub fn wrapping_offset_from(self, other: *const T) -> isize where T: Sized { | ||
(self as *const T).wrapping_offset_from(other) | ||
} | ||
|
||
/// Computes the byte offset that needs to be applied in order to | ||
/// make the pointer aligned to `align`. | ||
/// If it is not possible to align the pointer, the implementation returns | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -289,7 +289,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, | |
"ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | | ||
"bitreverse" | "add_with_overflow" | "sub_with_overflow" | | ||
"mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | | ||
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" => { | ||
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { | ||
let ty = arg_tys[0]; | ||
match int_type_width_signed(ty, cx) { | ||
Some((width, signed)) => | ||
|
@@ -343,6 +343,12 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, | |
"overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()), | ||
"overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()), | ||
"overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()), | ||
"exact_div" => | ||
if signed { | ||
bx.exactsdiv(args[0].immediate(), args[1].immediate()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It turned out to already exist: https://github.com/rust-lang/rust/pull/49297/files#diff-89b470c8d50892235012a9fffdb50d7cR361 |
||
} else { | ||
bx.exactudiv(args[0].immediate(), args[1].immediate()) | ||
}, | ||
"unchecked_div" => | ||
if signed { | ||
bx.sdiv(args[0].immediate(), args[1].immediate()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can't be
nuw
because the result might be a negative value.It can't be
nsw
either because an allocation may straddle the boundary betweenISIZE_MAX
(0x7fffffff) andISIZE_MIN
(0x80000000).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree.
nuw
andnsw
force both the result and the operands to have the same interpretation as each other. What you'd really want here is a way to say that the operands have an unsigned interpretation, while the result has a signed interpretation, however there's currently no way to express that in LLVM.