Rollup merge of #98585 - cuviper:covariant-thinbox, r=thomcc
Make `ThinBox<T>` covariant in `T` Just like `Box<T>`, we want `ThinBox<T>` to be covariant in `T`, but the projection in `WithHeader<<T as Pointee>::Metadata>` was making it invariant. This is now hidden as `WithOpaqueHeader`, which we type-cast whenever the real `WithHeader<H>` type is needed. Fixes the problem noted in <https://github.com/rust-lang/rust/issues/92791#issuecomment-1104636249>.
This commit is contained in:
commit
9dd3288557
@ -31,7 +31,9 @@
|
||||
/// ```
|
||||
#[unstable(feature = "thin_box", issue = "92791")]
|
||||
pub struct ThinBox<T: ?Sized> {
|
||||
ptr: WithHeader<<T as Pointee>::Metadata>,
|
||||
// This is essentially `WithHeader<<T as Pointee>::Metadata>`,
|
||||
// but that would be invariant in `T`, and we want covariance.
|
||||
ptr: WithOpaqueHeader,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
@ -59,7 +61,7 @@ impl<T> ThinBox<T> {
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
pub fn new(value: T) -> Self {
|
||||
let meta = ptr::metadata(&value);
|
||||
let ptr = WithHeader::new(meta, value);
|
||||
let ptr = WithOpaqueHeader::new(meta, value);
|
||||
ThinBox { ptr, _marker: PhantomData }
|
||||
}
|
||||
}
|
||||
@ -83,7 +85,7 @@ pub fn new_unsize<T>(value: T) -> Self
|
||||
T: Unsize<Dyn>,
|
||||
{
|
||||
let meta = ptr::metadata(&value as &Dyn);
|
||||
let ptr = WithHeader::new(meta, value);
|
||||
let ptr = WithOpaqueHeader::new(meta, value);
|
||||
ThinBox { ptr, _marker: PhantomData }
|
||||
}
|
||||
}
|
||||
@ -130,7 +132,7 @@ fn drop(&mut self) {
|
||||
unsafe {
|
||||
let value = self.deref_mut();
|
||||
let value = value as *mut T;
|
||||
self.ptr.drop::<T>(value);
|
||||
self.with_header().drop::<T>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,11 +142,16 @@ impl<T: ?Sized> ThinBox<T> {
|
||||
fn meta(&self) -> <T as Pointee>::Metadata {
|
||||
// Safety:
|
||||
// - NonNull and valid.
|
||||
unsafe { *self.ptr.header() }
|
||||
unsafe { *self.with_header().header() }
|
||||
}
|
||||
|
||||
fn data(&self) -> *mut u8 {
|
||||
self.ptr.value()
|
||||
self.with_header().value()
|
||||
}
|
||||
|
||||
fn with_header(&self) -> &WithHeader<<T as Pointee>::Metadata> {
|
||||
// SAFETY: both types are transparent to `NonNull<u8>`
|
||||
unsafe { &*((&self.ptr) as *const WithOpaqueHeader as *const WithHeader<_>) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,8 +160,22 @@ fn data(&self) -> *mut u8 {
|
||||
/// metadata (`H`) are ZSTs.
|
||||
/// 2. A pointer to a valid `T` that has a header `H` directly before the
|
||||
/// pointed-to location.
|
||||
#[repr(transparent)]
|
||||
struct WithHeader<H>(NonNull<u8>, PhantomData<H>);
|
||||
|
||||
/// An opaque representation of `WithHeader<H>` to avoid the
|
||||
/// projection invariance of `<T as Pointee>::Metadata`.
|
||||
#[repr(transparent)]
|
||||
struct WithOpaqueHeader(NonNull<u8>);
|
||||
|
||||
impl WithOpaqueHeader {
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
fn new<H, T>(header: H, value: T) -> Self {
|
||||
let ptr = WithHeader::new(header, value);
|
||||
Self(ptr.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> WithHeader<H> {
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
fn new<T>(header: H, value: T) -> WithHeader<H> {
|
||||
|
@ -26,6 +26,13 @@ trait Tr {}
|
||||
assert!(is_thin::<i32>());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn assert_covariance() {
|
||||
fn thin_box<'new>(b: ThinBox<[&'static str]>) -> ThinBox<[&'new str]> {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn verify_aligned<T>(ptr: *const T) {
|
||||
// Use `black_box` to attempt to obscure the fact that we're calling this
|
||||
|
Loading…
Reference in New Issue
Block a user