mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
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 @@ 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> {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user