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:
Dylan DPC 2022-07-01 20:19:17 +05:30 committed by GitHub
commit 9dd3288557
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 6 deletions

View File

@ -31,7 +31,9 @@ use core::ptr::{self, NonNull};
/// ``` /// ```
#[unstable(feature = "thin_box", issue = "92791")] #[unstable(feature = "thin_box", issue = "92791")]
pub struct ThinBox<T: ?Sized> { 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>, _marker: PhantomData<T>,
} }
@ -59,7 +61,7 @@ impl<T> ThinBox<T> {
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
pub fn new(value: T) -> Self { pub fn new(value: T) -> Self {
let meta = ptr::metadata(&value); let meta = ptr::metadata(&value);
let ptr = WithHeader::new(meta, value); let ptr = WithOpaqueHeader::new(meta, value);
ThinBox { ptr, _marker: PhantomData } ThinBox { ptr, _marker: PhantomData }
} }
} }
@ -83,7 +85,7 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
T: Unsize<Dyn>, T: Unsize<Dyn>,
{ {
let meta = ptr::metadata(&value as &Dyn); let meta = ptr::metadata(&value as &Dyn);
let ptr = WithHeader::new(meta, value); let ptr = WithOpaqueHeader::new(meta, value);
ThinBox { ptr, _marker: PhantomData } ThinBox { ptr, _marker: PhantomData }
} }
} }
@ -130,7 +132,7 @@ impl<T: ?Sized> Drop for ThinBox<T> {
unsafe { unsafe {
let value = self.deref_mut(); let value = self.deref_mut();
let value = value as *mut T; 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 { fn meta(&self) -> <T as Pointee>::Metadata {
// Safety: // Safety:
// - NonNull and valid. // - NonNull and valid.
unsafe { *self.ptr.header() } unsafe { *self.with_header().header() }
} }
fn data(&self) -> *mut u8 { 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 @@ impl<T: ?Sized> ThinBox<T> {
/// metadata (`H`) are ZSTs. /// metadata (`H`) are ZSTs.
/// 2. A pointer to a valid `T` that has a header `H` directly before the /// 2. A pointer to a valid `T` that has a header `H` directly before the
/// pointed-to location. /// pointed-to location.
#[repr(transparent)]
struct WithHeader<H>(NonNull<u8>, PhantomData<H>); 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> { impl<H> WithHeader<H> {
#[cfg(not(no_global_oom_handling))] #[cfg(not(no_global_oom_handling))]
fn new<T>(header: H, value: T) -> WithHeader<H> { fn new<T>(header: H, value: T) -> WithHeader<H> {

View File

@ -26,6 +26,13 @@ fn want_thin() {
assert!(is_thin::<i32>()); assert!(is_thin::<i32>());
} }
#[allow(dead_code)]
fn assert_covariance() {
fn thin_box<'new>(b: ThinBox<[&'static str]>) -> ThinBox<[&'new str]> {
b
}
}
#[track_caller] #[track_caller]
fn verify_aligned<T>(ptr: *const T) { fn verify_aligned<T>(ptr: *const T) {
// Use `black_box` to attempt to obscure the fact that we're calling this // Use `black_box` to attempt to obscure the fact that we're calling this