mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Refactor set_ptr_value as with_metadata_of
By reversing the arguments we achieve several clarifications: - The function closely resembles `cast` but with an argument to initialized the metadata. This is easier to teach and answers an long outstanding question that had restricted cast to `Sized` targets initially. See multiples reviews of <https://github.com/rust-lang/rust/pull/47631> - The 'object identity', in the form or provenance, is now preserved from the call receiver to the result. This helps explain the method as a builder-style, instead of some kind of setter that would modify something in-place. Ensuring that the result has the identity of the `self` argument is also beneficial for an intuition of effects. - An outstanding concern, 'Correct argument type', is avoided by not committing to any specific argument type. This is consistent with cast which does not require its receiver to be a raw address.
This commit is contained in:
parent
c9b45e6010
commit
d489ea777d
@ -895,7 +895,7 @@ impl<T: ?Sized> Rc<T> {
|
||||
|
||||
// Reverse the offset to find the original RcBox.
|
||||
let rc_ptr =
|
||||
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
|
||||
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) };
|
||||
|
||||
unsafe { Self::from_ptr(rc_ptr) }
|
||||
}
|
||||
@ -1338,7 +1338,7 @@ impl<T: ?Sized> Rc<T> {
|
||||
Self::allocate_for_layout(
|
||||
Layout::for_value(&*ptr),
|
||||
|layout| Global.allocate(layout),
|
||||
|mem| (ptr as *mut RcBox<T>).set_ptr_value(mem),
|
||||
|mem| mem.with_metadata_of(ptr as *mut RcBox<T>),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -2263,7 +2263,7 @@ impl<T: ?Sized> Weak<T> {
|
||||
let offset = unsafe { data_offset(ptr) };
|
||||
// Thus, we reverse the offset to get the whole RcBox.
|
||||
// SAFETY: the pointer originated from a Weak, so this offset is safe.
|
||||
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
|
||||
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) }
|
||||
};
|
||||
|
||||
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.
|
||||
|
@ -895,7 +895,8 @@ impl<T: ?Sized> Arc<T> {
|
||||
let offset = data_offset(ptr);
|
||||
|
||||
// Reverse the offset to find the original ArcInner.
|
||||
let arc_ptr = (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset));
|
||||
let arc_ptr =
|
||||
(ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>);
|
||||
|
||||
Self::from_ptr(arc_ptr)
|
||||
}
|
||||
@ -1182,7 +1183,7 @@ impl<T: ?Sized> Arc<T> {
|
||||
Self::allocate_for_layout(
|
||||
Layout::for_value(&*ptr),
|
||||
|layout| Global.allocate(layout),
|
||||
|mem| (ptr as *mut ArcInner<T>).set_ptr_value(mem) as *mut ArcInner<T>,
|
||||
|mem| mem.with_metadata_of(ptr as *mut ArcInner<T>),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1887,7 +1888,7 @@ impl<T: ?Sized> Weak<T> {
|
||||
let offset = unsafe { data_offset(ptr) };
|
||||
// Thus, we reverse the offset to get the whole RcBox.
|
||||
// SAFETY: the pointer originated from a Weak, so this offset is safe.
|
||||
unsafe { (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
|
||||
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>) }
|
||||
};
|
||||
|
||||
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.
|
||||
|
@ -48,6 +48,50 @@ impl<T: ?Sized> *const T {
|
||||
self as _
|
||||
}
|
||||
|
||||
/// Use the pointer value in a new pointer of another type.
|
||||
///
|
||||
/// In case `val` is a (fat) pointer to an unsized type, this operation
|
||||
/// will ignore the pointer part, whereas for (thin) pointers to sized
|
||||
/// types, this has the same effect as a simple cast.
|
||||
///
|
||||
/// The resulting pointer will have provenance of `self`, i.e., for a fat
|
||||
/// pointer, this operation is semantically the same as creating a new
|
||||
/// fat pointer with the data pointer value of `self` but the metadata of
|
||||
/// `val`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This function is primarily useful for allowing byte-wise pointer
|
||||
/// arithmetic on potentially fat pointers:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(set_ptr_value)]
|
||||
/// # use core::fmt::Debug;
|
||||
/// let arr: [i32; 3] = [1, 2, 3];
|
||||
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
|
||||
/// let thin = ptr as *const u8;
|
||||
/// unsafe {
|
||||
/// ptr = thin.add(8).with_metadata_of(ptr);
|
||||
/// # assert_eq!(*(ptr as *const i32), 3);
|
||||
/// println!("{:?}", &*ptr); // will print "3"
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "set_ptr_value", issue = "75091")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[inline]
|
||||
pub fn with_metadata_of<U>(self, mut val: *const U) -> *const U
|
||||
where
|
||||
U: ?Sized,
|
||||
{
|
||||
let target = &mut val as *mut *const U as *mut *const u8;
|
||||
// SAFETY: In case of a thin pointer, this operations is identical
|
||||
// to a simple assignment. In case of a fat pointer, with the current
|
||||
// fat pointer layout implementation, the first field of such a
|
||||
// pointer is always the data pointer, which is likewise assigned.
|
||||
unsafe { *target = self as *const u8 };
|
||||
val
|
||||
}
|
||||
|
||||
/// Changes constness without changing the type.
|
||||
///
|
||||
/// This is a bit safer than `as` because it wouldn't silently change the type if the code is
|
||||
@ -764,47 +808,6 @@ impl<T: ?Sized> *const T {
|
||||
self.wrapping_offset((count as isize).wrapping_neg())
|
||||
}
|
||||
|
||||
/// Sets the pointer value to `ptr`.
|
||||
///
|
||||
/// In case `self` is a (fat) pointer to an unsized type, this operation
|
||||
/// will only affect the pointer part, whereas for (thin) pointers to
|
||||
/// sized types, this has the same effect as a simple assignment.
|
||||
///
|
||||
/// The resulting pointer will have provenance of `val`, i.e., for a fat
|
||||
/// pointer, this operation is semantically the same as creating a new
|
||||
/// fat pointer with the data pointer value of `val` but the metadata of
|
||||
/// `self`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This function is primarily useful for allowing byte-wise pointer
|
||||
/// arithmetic on potentially fat pointers:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(set_ptr_value)]
|
||||
/// # use core::fmt::Debug;
|
||||
/// let arr: [i32; 3] = [1, 2, 3];
|
||||
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
|
||||
/// let thin = ptr as *const u8;
|
||||
/// unsafe {
|
||||
/// ptr = ptr.set_ptr_value(thin.add(8));
|
||||
/// # assert_eq!(*(ptr as *const i32), 3);
|
||||
/// println!("{:?}", &*ptr); // will print "3"
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "set_ptr_value", issue = "75091")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[inline]
|
||||
pub fn set_ptr_value(mut self, val: *const u8) -> Self {
|
||||
let thin = &mut self as *mut *const T as *mut *const u8;
|
||||
// SAFETY: In case of a thin pointer, this operations is identical
|
||||
// to a simple assignment. In case of a fat pointer, with the current
|
||||
// fat pointer layout implementation, the first field of such a
|
||||
// pointer is always the data pointer, which is likewise assigned.
|
||||
unsafe { *thin = val };
|
||||
self
|
||||
}
|
||||
|
||||
/// Reads the value from `self` without moving it. This leaves the
|
||||
/// memory in `self` unchanged.
|
||||
///
|
||||
|
@ -47,6 +47,50 @@ impl<T: ?Sized> *mut T {
|
||||
self as _
|
||||
}
|
||||
|
||||
/// Use the pointer value in a new pointer of another type.
|
||||
///
|
||||
/// In case `val` is a (fat) pointer to an unsized type, this operation
|
||||
/// will ignore the pointer part, whereas for (thin) pointers to sized
|
||||
/// types, this has the same effect as a simple cast.
|
||||
///
|
||||
/// The resulting pointer will have provenance of `self`, i.e., for a fat
|
||||
/// pointer, this operation is semantically the same as creating a new
|
||||
/// fat pointer with the data pointer value of `self` but the metadata of
|
||||
/// `val`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This function is primarily useful for allowing byte-wise pointer
|
||||
/// arithmetic on potentially fat pointers:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(set_ptr_value)]
|
||||
/// # use core::fmt::Debug;
|
||||
/// let mut arr: [i32; 3] = [1, 2, 3];
|
||||
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
|
||||
/// let thin = ptr as *mut u8;
|
||||
/// unsafe {
|
||||
/// ptr = thin.add(8).with_metadata_of(ptr);
|
||||
/// # assert_eq!(*(ptr as *mut i32), 3);
|
||||
/// println!("{:?}", &*ptr); // will print "3"
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "set_ptr_value", issue = "75091")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[inline]
|
||||
pub fn with_metadata_of<U>(self, mut val: *mut U) -> *mut U
|
||||
where
|
||||
U: ?Sized,
|
||||
{
|
||||
let target = &mut val as *mut *mut U as *mut *mut u8;
|
||||
// SAFETY: In case of a thin pointer, this operations is identical
|
||||
// to a simple assignment. In case of a fat pointer, with the current
|
||||
// fat pointer layout implementation, the first field of such a
|
||||
// pointer is always the data pointer, which is likewise assigned.
|
||||
unsafe { *target = self as *mut u8 };
|
||||
val
|
||||
}
|
||||
|
||||
/// Changes constness without changing the type.
|
||||
///
|
||||
/// This is a bit safer than `as` because it wouldn't silently change the type if the code is
|
||||
@ -878,47 +922,6 @@ impl<T: ?Sized> *mut T {
|
||||
self.wrapping_offset((count as isize).wrapping_neg())
|
||||
}
|
||||
|
||||
/// Sets the pointer value to `ptr`.
|
||||
///
|
||||
/// In case `self` is a (fat) pointer to an unsized type, this operation
|
||||
/// will only affect the pointer part, whereas for (thin) pointers to
|
||||
/// sized types, this has the same effect as a simple assignment.
|
||||
///
|
||||
/// The resulting pointer will have provenance of `val`, i.e., for a fat
|
||||
/// pointer, this operation is semantically the same as creating a new
|
||||
/// fat pointer with the data pointer value of `val` but the metadata of
|
||||
/// `self`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This function is primarily useful for allowing byte-wise pointer
|
||||
/// arithmetic on potentially fat pointers:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(set_ptr_value)]
|
||||
/// # use core::fmt::Debug;
|
||||
/// let mut arr: [i32; 3] = [1, 2, 3];
|
||||
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
|
||||
/// let thin = ptr as *mut u8;
|
||||
/// unsafe {
|
||||
/// ptr = ptr.set_ptr_value(thin.add(8));
|
||||
/// # assert_eq!(*(ptr as *mut i32), 3);
|
||||
/// println!("{:?}", &*ptr); // will print "3"
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "set_ptr_value", issue = "75091")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[inline]
|
||||
pub fn set_ptr_value(mut self, val: *mut u8) -> Self {
|
||||
let thin = &mut self as *mut *mut T as *mut *mut u8;
|
||||
// SAFETY: In case of a thin pointer, this operations is identical
|
||||
// to a simple assignment. In case of a fat pointer, with the current
|
||||
// fat pointer layout implementation, the first field of such a
|
||||
// pointer is always the data pointer, which is likewise assigned.
|
||||
unsafe { *thin = val };
|
||||
self
|
||||
}
|
||||
|
||||
/// Reads the value from `self` without moving it. This leaves the
|
||||
/// memory in `self` unchanged.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user