Skip to content

Commit 738d4a7

Browse files
committed
Auto merge of #74160 - CAD97:weak-as-unsized-ptr, r=RalfJung
Allow Weak::as_ptr and friends for unsized T Relaxes `impl<T> Weak<T>` to `impl<T: ?Sized> Weak<T>` for the methods `rc::Weak::as_ptr`, `into_raw`, and `from_raw`. Follow-up to #73845, which did most of the impl work to make these functions work for `T: ?Sized`. We still have to adjust the implementation of `Weak::from_raw` here, however, because I missed a use of `ptr.is_null()` previously. This check was necessary when `into`/`from_raw` were first implemented, as `into_raw` returned `ptr::null()` for dangling weak. However, we now just (wrapping) offset dangling weaks' pointers the same as nondangling weak, so the null check is no longer necessary (or even hit). (I can submit just 17a928f as a separate PR if desired.) As a nice side effect, moves the `fn is_dangling` definition closer to `Weak::new`, which creates the dangling weak. This technically stabilizes that "something like `align_of_val_raw`" is possible to do. However, I believe the part of the functionality required by these methods here -- specifically, getting the alignment of a pointee from a pointer where it may be dangling iff the pointee is `Sized` -- is uncontroversial enough to stabilize these methods without a way to implement them on stable Rust. r? `@RalfJung,` who reviewed #73845. ATTN: This changes (relaxes) the (input) generic bounds on stable fn!
2 parents 6f56fbd + e27ef13 commit 738d4a7

File tree

4 files changed

+130
-44
lines changed

4 files changed

+130
-44
lines changed

library/alloc/src/rc.rs

+25-24
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,21 @@ impl<T> Weak<T> {
17211721
pub fn new() -> Weak<T> {
17221722
Weak { ptr: NonNull::new(usize::MAX as *mut RcBox<T>).expect("MAX is not 0") }
17231723
}
1724+
}
17241725

1726+
pub(crate) fn is_dangling<T: ?Sized>(ptr: NonNull<T>) -> bool {
1727+
let address = ptr.as_ptr() as *mut () as usize;
1728+
address == usize::MAX
1729+
}
1730+
1731+
/// Helper type to allow accessing the reference counts without
1732+
/// making any assertions about the data field.
1733+
struct WeakInner<'a> {
1734+
weak: &'a Cell<usize>,
1735+
strong: &'a Cell<usize>,
1736+
}
1737+
1738+
impl<T: ?Sized> Weak<T> {
17251739
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
17261740
///
17271741
/// The pointer is valid only if there are some strong references. The pointer may be dangling,
@@ -1841,33 +1855,20 @@ impl<T> Weak<T> {
18411855
/// [`new`]: Weak::new
18421856
#[stable(feature = "weak_into_raw", since = "1.45.0")]
18431857
pub unsafe fn from_raw(ptr: *const T) -> Self {
1844-
if ptr.is_null() {
1845-
Self::new()
1846-
} else {
1847-
// See Rc::from_raw for details
1848-
unsafe {
1849-
let offset = data_offset(ptr);
1850-
let fake_ptr = ptr as *mut RcBox<T>;
1851-
let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
1852-
Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") }
1853-
}
1854-
}
1855-
}
1856-
}
1858+
// SAFETY: data_offset is safe to call, because this pointer originates from a Weak.
1859+
// See Weak::as_ptr for context on how the input pointer is derived.
1860+
let offset = unsafe { data_offset(ptr) };
18571861

1858-
pub(crate) fn is_dangling<T: ?Sized>(ptr: NonNull<T>) -> bool {
1859-
let address = ptr.as_ptr() as *mut () as usize;
1860-
address == usize::MAX
1861-
}
1862+
// Reverse the offset to find the original RcBox.
1863+
// SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized).
1864+
let ptr = unsafe {
1865+
set_data_ptr(ptr as *mut RcBox<T>, (ptr as *mut u8).wrapping_offset(-offset))
1866+
};
18621867

1863-
/// Helper type to allow accessing the reference counts without
1864-
/// making any assertions about the data field.
1865-
struct WeakInner<'a> {
1866-
weak: &'a Cell<usize>,
1867-
strong: &'a Cell<usize>,
1868-
}
1868+
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.
1869+
Weak { ptr: unsafe { NonNull::new_unchecked(ptr) } }
1870+
}
18691871

1870-
impl<T: ?Sized> Weak<T> {
18711872
/// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying
18721873
/// dropping of the inner value if successful.
18731874
///

library/alloc/src/rc/tests.rs

+42
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,48 @@ fn test_into_from_raw_unsized() {
190190
assert_eq!(rc2.to_string(), "123");
191191
}
192192

193+
#[test]
194+
fn into_from_weak_raw() {
195+
let x = Rc::new(box "hello");
196+
let y = Rc::downgrade(&x);
197+
198+
let y_ptr = Weak::into_raw(y);
199+
unsafe {
200+
assert_eq!(**y_ptr, "hello");
201+
202+
let y = Weak::from_raw(y_ptr);
203+
let y_up = Weak::upgrade(&y).unwrap();
204+
assert_eq!(**y_up, "hello");
205+
drop(y_up);
206+
207+
assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello"));
208+
}
209+
}
210+
211+
#[test]
212+
fn test_into_from_weak_raw_unsized() {
213+
use std::fmt::Display;
214+
use std::string::ToString;
215+
216+
let arc: Rc<str> = Rc::from("foo");
217+
let weak: Weak<str> = Rc::downgrade(&arc);
218+
219+
let ptr = Weak::into_raw(weak.clone());
220+
let weak2 = unsafe { Weak::from_raw(ptr) };
221+
222+
assert_eq!(unsafe { &*ptr }, "foo");
223+
assert!(weak.ptr_eq(&weak2));
224+
225+
let arc: Rc<dyn Display> = Rc::new(123);
226+
let weak: Weak<dyn Display> = Rc::downgrade(&arc);
227+
228+
let ptr = Weak::into_raw(weak.clone());
229+
let weak2 = unsafe { Weak::from_raw(ptr) };
230+
231+
assert_eq!(unsafe { &*ptr }.to_string(), "123");
232+
assert!(weak.ptr_eq(&weak2));
233+
}
234+
193235
#[test]
194236
fn get_mut() {
195237
let mut x = Rc::new(3);

library/alloc/src/sync.rs

+21-20
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,16 @@ impl<T> Weak<T> {
15091509
pub fn new() -> Weak<T> {
15101510
Weak { ptr: NonNull::new(usize::MAX as *mut ArcInner<T>).expect("MAX is not 0") }
15111511
}
1512+
}
1513+
1514+
/// Helper type to allow accessing the reference counts without
1515+
/// making any assertions about the data field.
1516+
struct WeakInner<'a> {
1517+
weak: &'a atomic::AtomicUsize,
1518+
strong: &'a atomic::AtomicUsize,
1519+
}
15121520

1521+
impl<T: ?Sized> Weak<T> {
15131522
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
15141523
///
15151524
/// The pointer is valid only if there are some strong references. The pointer may be dangling,
@@ -1629,28 +1638,20 @@ impl<T> Weak<T> {
16291638
/// [`forget`]: std::mem::forget
16301639
#[stable(feature = "weak_into_raw", since = "1.45.0")]
16311640
pub unsafe fn from_raw(ptr: *const T) -> Self {
1632-
if ptr.is_null() {
1633-
Self::new()
1634-
} else {
1635-
// See Arc::from_raw for details
1636-
unsafe {
1637-
let offset = data_offset(ptr);
1638-
let fake_ptr = ptr as *mut ArcInner<T>;
1639-
let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset));
1640-
Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") }
1641-
}
1642-
}
1643-
}
1644-
}
1641+
// SAFETY: data_offset is safe to call, because this pointer originates from a Weak.
1642+
// See Weak::as_ptr for context on how the input pointer is derived.
1643+
let offset = unsafe { data_offset(ptr) };
1644+
1645+
// Reverse the offset to find the original ArcInner.
1646+
// SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized)
1647+
let ptr = unsafe {
1648+
set_data_ptr(ptr as *mut ArcInner<T>, (ptr as *mut u8).wrapping_offset(-offset))
1649+
};
16451650

1646-
/// Helper type to allow accessing the reference counts without
1647-
/// making any assertions about the data field.
1648-
struct WeakInner<'a> {
1649-
weak: &'a atomic::AtomicUsize,
1650-
strong: &'a atomic::AtomicUsize,
1651-
}
1651+
// SAFETY: we now have recovered the original Weak pointer, so can create the Weak.
1652+
unsafe { Weak { ptr: NonNull::new_unchecked(ptr) } }
1653+
}
16521654

1653-
impl<T: ?Sized> Weak<T> {
16541655
/// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying
16551656
/// dropping of the inner value if successful.
16561657
///

library/alloc/src/sync/tests.rs

+42
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,48 @@ fn test_into_from_raw_unsized() {
140140
assert_eq!(arc2.to_string(), "123");
141141
}
142142

143+
#[test]
144+
fn into_from_weak_raw() {
145+
let x = Arc::new(box "hello");
146+
let y = Arc::downgrade(&x);
147+
148+
let y_ptr = Weak::into_raw(y);
149+
unsafe {
150+
assert_eq!(**y_ptr, "hello");
151+
152+
let y = Weak::from_raw(y_ptr);
153+
let y_up = Weak::upgrade(&y).unwrap();
154+
assert_eq!(**y_up, "hello");
155+
drop(y_up);
156+
157+
assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello"));
158+
}
159+
}
160+
161+
#[test]
162+
fn test_into_from_weak_raw_unsized() {
163+
use std::fmt::Display;
164+
use std::string::ToString;
165+
166+
let arc: Arc<str> = Arc::from("foo");
167+
let weak: Weak<str> = Arc::downgrade(&arc);
168+
169+
let ptr = Weak::into_raw(weak.clone());
170+
let weak2 = unsafe { Weak::from_raw(ptr) };
171+
172+
assert_eq!(unsafe { &*ptr }, "foo");
173+
assert!(weak.ptr_eq(&weak2));
174+
175+
let arc: Arc<dyn Display> = Arc::new(123);
176+
let weak: Weak<dyn Display> = Arc::downgrade(&arc);
177+
178+
let ptr = Weak::into_raw(weak.clone());
179+
let weak2 = unsafe { Weak::from_raw(ptr) };
180+
181+
assert_eq!(unsafe { &*ptr }.to_string(), "123");
182+
assert!(weak.ptr_eq(&weak2));
183+
}
184+
143185
#[test]
144186
fn test_cowarc_clone_make_mut() {
145187
let mut cow0 = Arc::new(75);

0 commit comments

Comments
 (0)