Skip to content

Commit d489ea7

Browse files
committed
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 <rust-lang#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.
1 parent c9b45e6 commit d489ea7

File tree

4 files changed

+95
-88
lines changed

4 files changed

+95
-88
lines changed

library/alloc/src/rc.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ impl<T: ?Sized> Rc<T> {
895895

896896
// Reverse the offset to find the original RcBox.
897897
let rc_ptr =
898-
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
898+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) };
899899

900900
unsafe { Self::from_ptr(rc_ptr) }
901901
}
@@ -1338,7 +1338,7 @@ impl<T: ?Sized> Rc<T> {
13381338
Self::allocate_for_layout(
13391339
Layout::for_value(&*ptr),
13401340
|layout| Global.allocate(layout),
1341-
|mem| (ptr as *mut RcBox<T>).set_ptr_value(mem),
1341+
|mem| mem.with_metadata_of(ptr as *mut RcBox<T>),
13421342
)
13431343
}
13441344
}
@@ -2263,7 +2263,7 @@ impl<T: ?Sized> Weak<T> {
22632263
let offset = unsafe { data_offset(ptr) };
22642264
// Thus, we reverse the offset to get the whole RcBox.
22652265
// SAFETY: the pointer originated from a Weak, so this offset is safe.
2266-
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
2266+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut RcBox<T>) }
22672267
};
22682268

22692269
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.

library/alloc/src/sync.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,8 @@ impl<T: ?Sized> Arc<T> {
895895
let offset = data_offset(ptr);
896896

897897
// Reverse the offset to find the original ArcInner.
898-
let arc_ptr = (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset));
898+
let arc_ptr =
899+
(ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>);
899900

900901
Self::from_ptr(arc_ptr)
901902
}
@@ -1182,7 +1183,7 @@ impl<T: ?Sized> Arc<T> {
11821183
Self::allocate_for_layout(
11831184
Layout::for_value(&*ptr),
11841185
|layout| Global.allocate(layout),
1185-
|mem| (ptr as *mut ArcInner<T>).set_ptr_value(mem) as *mut ArcInner<T>,
1186+
|mem| mem.with_metadata_of(ptr as *mut ArcInner<T>),
11861187
)
11871188
}
11881189
}
@@ -1887,7 +1888,7 @@ impl<T: ?Sized> Weak<T> {
18871888
let offset = unsafe { data_offset(ptr) };
18881889
// Thus, we reverse the offset to get the whole RcBox.
18891890
// SAFETY: the pointer originated from a Weak, so this offset is safe.
1890-
unsafe { (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) }
1891+
unsafe { (ptr as *mut u8).offset(-offset).with_metadata_of(ptr as *mut ArcInner<T>) }
18911892
};
18921893

18931894
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.

library/core/src/ptr/const_ptr.rs

+44-41
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,50 @@ impl<T: ?Sized> *const T {
4848
self as _
4949
}
5050

51+
/// Use the pointer value in a new pointer of another type.
52+
///
53+
/// In case `val` is a (fat) pointer to an unsized type, this operation
54+
/// will ignore the pointer part, whereas for (thin) pointers to sized
55+
/// types, this has the same effect as a simple cast.
56+
///
57+
/// The resulting pointer will have provenance of `self`, i.e., for a fat
58+
/// pointer, this operation is semantically the same as creating a new
59+
/// fat pointer with the data pointer value of `self` but the metadata of
60+
/// `val`.
61+
///
62+
/// # Examples
63+
///
64+
/// This function is primarily useful for allowing byte-wise pointer
65+
/// arithmetic on potentially fat pointers:
66+
///
67+
/// ```
68+
/// #![feature(set_ptr_value)]
69+
/// # use core::fmt::Debug;
70+
/// let arr: [i32; 3] = [1, 2, 3];
71+
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
72+
/// let thin = ptr as *const u8;
73+
/// unsafe {
74+
/// ptr = thin.add(8).with_metadata_of(ptr);
75+
/// # assert_eq!(*(ptr as *const i32), 3);
76+
/// println!("{:?}", &*ptr); // will print "3"
77+
/// }
78+
/// ```
79+
#[unstable(feature = "set_ptr_value", issue = "75091")]
80+
#[must_use = "returns a new pointer rather than modifying its argument"]
81+
#[inline]
82+
pub fn with_metadata_of<U>(self, mut val: *const U) -> *const U
83+
where
84+
U: ?Sized,
85+
{
86+
let target = &mut val as *mut *const U as *mut *const u8;
87+
// SAFETY: In case of a thin pointer, this operations is identical
88+
// to a simple assignment. In case of a fat pointer, with the current
89+
// fat pointer layout implementation, the first field of such a
90+
// pointer is always the data pointer, which is likewise assigned.
91+
unsafe { *target = self as *const u8 };
92+
val
93+
}
94+
5195
/// Changes constness without changing the type.
5296
///
5397
/// 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 {
764808
self.wrapping_offset((count as isize).wrapping_neg())
765809
}
766810

767-
/// Sets the pointer value to `ptr`.
768-
///
769-
/// In case `self` is a (fat) pointer to an unsized type, this operation
770-
/// will only affect the pointer part, whereas for (thin) pointers to
771-
/// sized types, this has the same effect as a simple assignment.
772-
///
773-
/// The resulting pointer will have provenance of `val`, i.e., for a fat
774-
/// pointer, this operation is semantically the same as creating a new
775-
/// fat pointer with the data pointer value of `val` but the metadata of
776-
/// `self`.
777-
///
778-
/// # Examples
779-
///
780-
/// This function is primarily useful for allowing byte-wise pointer
781-
/// arithmetic on potentially fat pointers:
782-
///
783-
/// ```
784-
/// #![feature(set_ptr_value)]
785-
/// # use core::fmt::Debug;
786-
/// let arr: [i32; 3] = [1, 2, 3];
787-
/// let mut ptr = arr.as_ptr() as *const dyn Debug;
788-
/// let thin = ptr as *const u8;
789-
/// unsafe {
790-
/// ptr = ptr.set_ptr_value(thin.add(8));
791-
/// # assert_eq!(*(ptr as *const i32), 3);
792-
/// println!("{:?}", &*ptr); // will print "3"
793-
/// }
794-
/// ```
795-
#[unstable(feature = "set_ptr_value", issue = "75091")]
796-
#[must_use = "returns a new pointer rather than modifying its argument"]
797-
#[inline]
798-
pub fn set_ptr_value(mut self, val: *const u8) -> Self {
799-
let thin = &mut self as *mut *const T as *mut *const u8;
800-
// SAFETY: In case of a thin pointer, this operations is identical
801-
// to a simple assignment. In case of a fat pointer, with the current
802-
// fat pointer layout implementation, the first field of such a
803-
// pointer is always the data pointer, which is likewise assigned.
804-
unsafe { *thin = val };
805-
self
806-
}
807-
808811
/// Reads the value from `self` without moving it. This leaves the
809812
/// memory in `self` unchanged.
810813
///

library/core/src/ptr/mut_ptr.rs

+44-41
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,50 @@ impl<T: ?Sized> *mut T {
4747
self as _
4848
}
4949

50+
/// Use the pointer value in a new pointer of another type.
51+
///
52+
/// In case `val` is a (fat) pointer to an unsized type, this operation
53+
/// will ignore the pointer part, whereas for (thin) pointers to sized
54+
/// types, this has the same effect as a simple cast.
55+
///
56+
/// The resulting pointer will have provenance of `self`, i.e., for a fat
57+
/// pointer, this operation is semantically the same as creating a new
58+
/// fat pointer with the data pointer value of `self` but the metadata of
59+
/// `val`.
60+
///
61+
/// # Examples
62+
///
63+
/// This function is primarily useful for allowing byte-wise pointer
64+
/// arithmetic on potentially fat pointers:
65+
///
66+
/// ```
67+
/// #![feature(set_ptr_value)]
68+
/// # use core::fmt::Debug;
69+
/// let mut arr: [i32; 3] = [1, 2, 3];
70+
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
71+
/// let thin = ptr as *mut u8;
72+
/// unsafe {
73+
/// ptr = thin.add(8).with_metadata_of(ptr);
74+
/// # assert_eq!(*(ptr as *mut i32), 3);
75+
/// println!("{:?}", &*ptr); // will print "3"
76+
/// }
77+
/// ```
78+
#[unstable(feature = "set_ptr_value", issue = "75091")]
79+
#[must_use = "returns a new pointer rather than modifying its argument"]
80+
#[inline]
81+
pub fn with_metadata_of<U>(self, mut val: *mut U) -> *mut U
82+
where
83+
U: ?Sized,
84+
{
85+
let target = &mut val as *mut *mut U as *mut *mut u8;
86+
// SAFETY: In case of a thin pointer, this operations is identical
87+
// to a simple assignment. In case of a fat pointer, with the current
88+
// fat pointer layout implementation, the first field of such a
89+
// pointer is always the data pointer, which is likewise assigned.
90+
unsafe { *target = self as *mut u8 };
91+
val
92+
}
93+
5094
/// Changes constness without changing the type.
5195
///
5296
/// 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 {
878922
self.wrapping_offset((count as isize).wrapping_neg())
879923
}
880924

881-
/// Sets the pointer value to `ptr`.
882-
///
883-
/// In case `self` is a (fat) pointer to an unsized type, this operation
884-
/// will only affect the pointer part, whereas for (thin) pointers to
885-
/// sized types, this has the same effect as a simple assignment.
886-
///
887-
/// The resulting pointer will have provenance of `val`, i.e., for a fat
888-
/// pointer, this operation is semantically the same as creating a new
889-
/// fat pointer with the data pointer value of `val` but the metadata of
890-
/// `self`.
891-
///
892-
/// # Examples
893-
///
894-
/// This function is primarily useful for allowing byte-wise pointer
895-
/// arithmetic on potentially fat pointers:
896-
///
897-
/// ```
898-
/// #![feature(set_ptr_value)]
899-
/// # use core::fmt::Debug;
900-
/// let mut arr: [i32; 3] = [1, 2, 3];
901-
/// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug;
902-
/// let thin = ptr as *mut u8;
903-
/// unsafe {
904-
/// ptr = ptr.set_ptr_value(thin.add(8));
905-
/// # assert_eq!(*(ptr as *mut i32), 3);
906-
/// println!("{:?}", &*ptr); // will print "3"
907-
/// }
908-
/// ```
909-
#[unstable(feature = "set_ptr_value", issue = "75091")]
910-
#[must_use = "returns a new pointer rather than modifying its argument"]
911-
#[inline]
912-
pub fn set_ptr_value(mut self, val: *mut u8) -> Self {
913-
let thin = &mut self as *mut *mut T as *mut *mut u8;
914-
// SAFETY: In case of a thin pointer, this operations is identical
915-
// to a simple assignment. In case of a fat pointer, with the current
916-
// fat pointer layout implementation, the first field of such a
917-
// pointer is always the data pointer, which is likewise assigned.
918-
unsafe { *thin = val };
919-
self
920-
}
921-
922925
/// Reads the value from `self` without moving it. This leaves the
923926
/// memory in `self` unchanged.
924927
///

0 commit comments

Comments
 (0)