Skip to content

Commit 9a659c5

Browse files
authored
Rollup merge of #73845 - CAD97:weak-as-unsized-ptr, r=RalfJung
Use &raw in A|Rc::as_ptr This PR uses `&raw` for offsetting `*mut [A]RcInner<T> -> *mut T`. Additionally, this updates the implementation of `Weak::as_ptr` to support unsized `T`, though it does not yet relax the bounds of `Weak::as_ptr`/`into_raw`/`from_raw` to accept unsized `T`.
2 parents 50dcefc + 1b5ac57 commit 9a659c5

File tree

3 files changed

+59
-44
lines changed

3 files changed

+59
-44
lines changed

src/liballoc/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
#![feature(fundamental)]
101101
#![feature(internal_uninit_const)]
102102
#![feature(lang_items)]
103+
#![feature(layout_for_ptr)]
103104
#![feature(libc)]
104105
#![feature(negative_impls)]
105106
#![feature(new_uninit)]
@@ -109,6 +110,7 @@
109110
#![feature(pattern)]
110111
#![feature(ptr_internals)]
111112
#![feature(ptr_offset_from)]
113+
#![feature(raw_ref_op)]
112114
#![feature(rustc_attrs)]
113115
#![feature(receiver_trait)]
114116
#![feature(min_specialization)]

src/liballoc/rc.rs

+29-22
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ use core::hash::{Hash, Hasher};
245245
use core::intrinsics::abort;
246246
use core::iter;
247247
use core::marker::{self, PhantomData, Unpin, Unsize};
248-
use core::mem::{self, align_of, align_of_val, forget, size_of_val};
248+
use core::mem::{self, align_of_val_raw, forget, size_of_val};
249249
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
250250
use core::pin::Pin;
251251
use core::ptr::{self, NonNull};
@@ -591,17 +591,11 @@ impl<T: ?Sized> Rc<T> {
591591
#[stable(feature = "weak_into_raw", since = "1.45.0")]
592592
pub fn as_ptr(this: &Self) -> *const T {
593593
let ptr: *mut RcBox<T> = NonNull::as_ptr(this.ptr);
594-
let fake_ptr = ptr as *mut T;
595594

596-
// SAFETY: This cannot go through Deref::deref.
597-
// Instead, we manually offset the pointer rather than manifesting a reference.
598-
// This is so that the returned pointer retains the same provenance as our pointer.
599-
// This is required so that e.g. `get_mut` can write through the pointer
600-
// after the Rc is recovered through `from_raw`.
601-
unsafe {
602-
let offset = data_offset(&(*ptr).value);
603-
set_data_ptr(fake_ptr, (ptr as *mut u8).offset(offset))
604-
}
595+
// SAFETY: This cannot go through Deref::deref or Rc::inner because
596+
// this is required to retain raw/mut provenance such that e.g. `get_mut` can
597+
// write through the pointer after the Rc is recovered through `from_raw`.
598+
unsafe { &raw const (*ptr).value }
605599
}
606600

607601
/// Constructs an `Rc<T>` from a raw pointer.
@@ -1647,6 +1641,7 @@ pub struct Weak<T: ?Sized> {
16471641
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
16481642
// to allocate space on the heap. That's not a value a real pointer
16491643
// will ever have because RcBox has alignment at least 2.
1644+
// This is only possible when `T: Sized`; unsized `T` never dangle.
16501645
ptr: NonNull<RcBox<T>>,
16511646
}
16521647

@@ -1708,9 +1703,18 @@ impl<T> Weak<T> {
17081703
/// [`null`]: ../../std/ptr/fn.null.html
17091704
#[stable(feature = "weak_into_raw", since = "1.45.0")]
17101705
pub fn as_ptr(&self) -> *const T {
1711-
let offset = data_offset_sized::<T>();
1712-
let ptr = self.ptr.cast::<u8>().as_ptr().wrapping_offset(offset);
1713-
ptr as *const T
1706+
let ptr: *mut RcBox<T> = NonNull::as_ptr(self.ptr);
1707+
1708+
// SAFETY: we must offset the pointer manually, and said pointer may be
1709+
// a dangling weak (usize::MAX) if T is sized. data_offset is safe to call,
1710+
// because we know that a pointer to unsized T was derived from a real
1711+
// unsized T, as dangling weaks are only created for sized T. wrapping_offset
1712+
// is used so that we can use the same code path for the non-dangling
1713+
// unsized case and the potentially dangling sized case.
1714+
unsafe {
1715+
let offset = data_offset(ptr as *mut T);
1716+
set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset))
1717+
}
17141718
}
17151719

17161720
/// Consumes the `Weak<T>` and turns it into a raw pointer.
@@ -2113,19 +2117,22 @@ impl<T: ?Sized> AsRef<T> for Rc<T> {
21132117
#[stable(feature = "pin", since = "1.33.0")]
21142118
impl<T: ?Sized> Unpin for Rc<T> {}
21152119

2120+
/// Get the offset within an `ArcInner` for
2121+
/// a payload of type described by a pointer.
2122+
///
2123+
/// # Safety
2124+
///
2125+
/// This has the same safety requirements as `align_of_val_raw`. In effect:
2126+
///
2127+
/// - This function is safe for any argument if `T` is sized, and
2128+
/// - if `T` is unsized, the pointer must have appropriate pointer metadata
2129+
/// aquired from the real instance that you are getting this offset for.
21162130
unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize {
21172131
// Align the unsized value to the end of the `RcBox`.
21182132
// Because it is ?Sized, it will always be the last field in memory.
21192133
// Note: This is a detail of the current implementation of the compiler,
21202134
// and is not a guaranteed language detail. Do not rely on it outside of std.
2121-
unsafe { data_offset_align(align_of_val(&*ptr)) }
2122-
}
2123-
2124-
/// Computes the offset of the data field within `RcBox`.
2125-
///
2126-
/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`.
2127-
fn data_offset_sized<T>() -> isize {
2128-
data_offset_align(align_of::<T>())
2135+
unsafe { data_offset_align(align_of_val_raw(ptr)) }
21292136
}
21302137

21312138
#[inline]

src/liballoc/sync.rs

+28-22
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use core::hash::{Hash, Hasher};
1616
use core::intrinsics::abort;
1717
use core::iter;
1818
use core::marker::{PhantomData, Unpin, Unsize};
19-
use core::mem::{self, align_of, align_of_val, size_of_val};
19+
use core::mem::{self, align_of_val, size_of_val};
2020
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
2121
use core::pin::Pin;
2222
use core::ptr::{self, NonNull};
@@ -267,6 +267,7 @@ pub struct Weak<T: ?Sized> {
267267
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
268268
// to allocate space on the heap. That's not a value a real pointer
269269
// will ever have because RcBox has alignment at least 2.
270+
// This is only possible when `T: Sized`; unsized `T` never dangle.
270271
ptr: NonNull<ArcInner<T>>,
271272
}
272273

@@ -590,17 +591,11 @@ impl<T: ?Sized> Arc<T> {
590591
#[stable(feature = "weak_into_raw", since = "1.45.0")]
591592
pub fn as_ptr(this: &Self) -> *const T {
592593
let ptr: *mut ArcInner<T> = NonNull::as_ptr(this.ptr);
593-
let fake_ptr = ptr as *mut T;
594594

595-
// SAFETY: This cannot go through Deref::deref.
596-
// Instead, we manually offset the pointer rather than manifesting a reference.
597-
// This is so that the returned pointer retains the same provenance as our pointer.
598-
// This is required so that e.g. `get_mut` can write through the pointer
599-
// after the Arc is recovered through `from_raw`.
600-
unsafe {
601-
let offset = data_offset(&(*ptr).data);
602-
set_data_ptr(fake_ptr, (ptr as *mut u8).offset(offset))
603-
}
595+
// SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because
596+
// this is required to retain raw/mut provenance such that e.g. `get_mut` can
597+
// write through the pointer after the Rc is recovered through `from_raw`.
598+
unsafe { &raw const (*ptr).data }
604599
}
605600

606601
/// Constructs an `Arc<T>` from a raw pointer.
@@ -1476,9 +1471,18 @@ impl<T> Weak<T> {
14761471
/// [`null`]: ../../std/ptr/fn.null.html
14771472
#[stable(feature = "weak_into_raw", since = "1.45.0")]
14781473
pub fn as_ptr(&self) -> *const T {
1479-
let offset = data_offset_sized::<T>();
1480-
let ptr = self.ptr.cast::<u8>().as_ptr().wrapping_offset(offset);
1481-
ptr as *const T
1474+
let ptr: *mut ArcInner<T> = NonNull::as_ptr(self.ptr);
1475+
1476+
// SAFETY: we must offset the pointer manually, and said pointer may be
1477+
// a dangling weak (usize::MAX) if T is sized. data_offset is safe to call,
1478+
// because we know that a pointer to unsized T was derived from a real
1479+
// unsized T, as dangling weaks are only created for sized T. wrapping_offset
1480+
// is used so that we can use the same code path for the non-dangling
1481+
// unsized case and the potentially dangling sized case.
1482+
unsafe {
1483+
let offset = data_offset(ptr as *mut T);
1484+
set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset))
1485+
}
14821486
}
14831487

14841488
/// Consumes the `Weak<T>` and turns it into a raw pointer.
@@ -2270,7 +2274,16 @@ impl<T: ?Sized> AsRef<T> for Arc<T> {
22702274
#[stable(feature = "pin", since = "1.33.0")]
22712275
impl<T: ?Sized> Unpin for Arc<T> {}
22722276

2273-
/// Computes the offset of the data field within `ArcInner`.
2277+
/// Get the offset within an `ArcInner` for
2278+
/// a payload of type described by a pointer.
2279+
///
2280+
/// # Safety
2281+
///
2282+
/// This has the same safety requirements as `align_of_val_raw`. In effect:
2283+
///
2284+
/// - This function is safe for any argument if `T` is sized, and
2285+
/// - if `T` is unsized, the pointer must have appropriate pointer metadata
2286+
/// aquired from the real instance that you are getting this offset for.
22742287
unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize {
22752288
// Align the unsized value to the end of the `ArcInner`.
22762289
// Because it is `?Sized`, it will always be the last field in memory.
@@ -2279,13 +2292,6 @@ unsafe fn data_offset<T: ?Sized>(ptr: *const T) -> isize {
22792292
unsafe { data_offset_align(align_of_val(&*ptr)) }
22802293
}
22812294

2282-
/// Computes the offset of the data field within `ArcInner`.
2283-
///
2284-
/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`.
2285-
fn data_offset_sized<T>() -> isize {
2286-
data_offset_align(align_of::<T>())
2287-
}
2288-
22892295
#[inline]
22902296
fn data_offset_align(align: usize) -> isize {
22912297
let layout = Layout::new::<ArcInner<()>>();

0 commit comments

Comments
 (0)