diff options
| author | Dylan DPC <99973273+Dylan-DPC@users.noreply.github.com> | 2022-07-01 20:19:17 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-01 20:19:17 +0530 |
| commit | 9dd328855736e1c69f3df696e6084bbb6749d0cf (patch) | |
| tree | 9b0744a6a7769344d6bcad9f6ef41028b5cad4a4 | |
| parent | e2ed8d7ed167d7b7026cc9ea990e8fec2a1d5d46 (diff) | |
| parent | e67e1655854b8f2c94dff80f4da2e595bd1ceaa8 (diff) | |
| download | rust-9dd328855736e1c69f3df696e6084bbb6749d0cf.tar.gz rust-9dd328855736e1c69f3df696e6084bbb6749d0cf.zip | |
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>.
| -rw-r--r-- | library/alloc/src/boxed/thin.rs | 33 | ||||
| -rw-r--r-- | library/alloc/tests/thin_box.rs | 7 |
2 files changed, 34 insertions, 6 deletions
diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 09308d4d090..649ccfcaa9e 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -31,7 +31,9 @@ use core::ptr::{self, NonNull}; /// ``` #[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 @@ impl<Dyn: ?Sized> ThinBox<Dyn> { 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 @@ impl<T: ?Sized> Drop for ThinBox<T> { 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 @@ impl<T: ?Sized> ThinBox<T> { /// 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> { diff --git a/library/alloc/tests/thin_box.rs b/library/alloc/tests/thin_box.rs index 70d1db8b457..368aa564f94 100644 --- a/library/alloc/tests/thin_box.rs +++ b/library/alloc/tests/thin_box.rs @@ -26,6 +26,13 @@ fn want_thin() { 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 |
