From e67e1655854b8f2c94dff80f4da2e595bd1ceaa8 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 27 Jun 2022 10:05:55 -0700 Subject: [PATCH] Make `ThinBox` covariant in `T` Just like `Box`, we want `ThinBox` to be covariant in `T`, but the projection in `WithHeader<::Metadata>` was making it invariant. This is now hidden as `WithOpaqueHeader`, which we type-cast whenever the real `WithHeader` type is needed. --- library/alloc/src/boxed/thin.rs | 33 +++++++++++++++++++++++++++------ 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 807c035fdbd..627b52be052 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -29,7 +29,9 @@ /// ``` #[unstable(feature = "thin_box", issue = "92791")] pub struct ThinBox { - ptr: WithHeader<::Metadata>, + // This is essentially `WithHeader<::Metadata>`, + // but that would be invariant in `T`, and we want covariance. + ptr: WithOpaqueHeader, _marker: PhantomData, } @@ -49,7 +51,7 @@ impl ThinBox { #[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 } } } @@ -73,7 +75,7 @@ pub fn new_unsize(value: T) -> Self T: Unsize, { let meta = ptr::metadata(&value as &Dyn); - let ptr = WithHeader::new(meta, value); + let ptr = WithOpaqueHeader::new(meta, value); ThinBox { ptr, _marker: PhantomData } } } @@ -120,7 +122,7 @@ fn drop(&mut self) { unsafe { let value = self.deref_mut(); let value = value as *mut T; - self.ptr.drop::(value); + self.with_header().drop::(value); } } } @@ -130,11 +132,16 @@ impl ThinBox { fn meta(&self) -> ::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<::Metadata> { + // SAFETY: both types are transparent to `NonNull` + unsafe { &*((&self.ptr) as *const WithOpaqueHeader as *const WithHeader<_>) } } } @@ -143,8 +150,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(NonNull, PhantomData); +/// An opaque representation of `WithHeader` to avoid the +/// projection invariance of `::Metadata`. +#[repr(transparent)] +struct WithOpaqueHeader(NonNull); + +impl WithOpaqueHeader { + #[cfg(not(no_global_oom_handling))] + fn new(header: H, value: T) -> Self { + let ptr = WithHeader::new(header, value); + Self(ptr.0) + } +} + impl WithHeader { #[cfg(not(no_global_oom_handling))] fn new(header: H, value: T) -> WithHeader { 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 @@ trait Tr {} assert!(is_thin::()); } +#[allow(dead_code)] +fn assert_covariance() { + fn thin_box<'new>(b: ThinBox<[&'static str]>) -> ThinBox<[&'new str]> { + b + } +} + #[track_caller] fn verify_aligned(ptr: *const T) { // Use `black_box` to attempt to obscure the fact that we're calling this