Skip to content

Commit 9dd3288

Browse files
authored
Rollup merge of rust-lang#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 <rust-lang#92791 (comment)>.
2 parents e2ed8d7 + e67e165 commit 9dd3288

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

library/alloc/src/boxed/thin.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use core::ptr::{self, NonNull};
3131
/// ```
3232
#[unstable(feature = "thin_box", issue = "92791")]
3333
pub struct ThinBox<T: ?Sized> {
34-
ptr: WithHeader<<T as Pointee>::Metadata>,
34+
// This is essentially `WithHeader<<T as Pointee>::Metadata>`,
35+
// but that would be invariant in `T`, and we want covariance.
36+
ptr: WithOpaqueHeader,
3537
_marker: PhantomData<T>,
3638
}
3739

@@ -59,7 +61,7 @@ impl<T> ThinBox<T> {
5961
#[cfg(not(no_global_oom_handling))]
6062
pub fn new(value: T) -> Self {
6163
let meta = ptr::metadata(&value);
62-
let ptr = WithHeader::new(meta, value);
64+
let ptr = WithOpaqueHeader::new(meta, value);
6365
ThinBox { ptr, _marker: PhantomData }
6466
}
6567
}
@@ -83,7 +85,7 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
8385
T: Unsize<Dyn>,
8486
{
8587
let meta = ptr::metadata(&value as &Dyn);
86-
let ptr = WithHeader::new(meta, value);
88+
let ptr = WithOpaqueHeader::new(meta, value);
8789
ThinBox { ptr, _marker: PhantomData }
8890
}
8991
}
@@ -130,7 +132,7 @@ impl<T: ?Sized> Drop for ThinBox<T> {
130132
unsafe {
131133
let value = self.deref_mut();
132134
let value = value as *mut T;
133-
self.ptr.drop::<T>(value);
135+
self.with_header().drop::<T>(value);
134136
}
135137
}
136138
}
@@ -140,11 +142,16 @@ impl<T: ?Sized> ThinBox<T> {
140142
fn meta(&self) -> <T as Pointee>::Metadata {
141143
// Safety:
142144
// - NonNull and valid.
143-
unsafe { *self.ptr.header() }
145+
unsafe { *self.with_header().header() }
144146
}
145147

146148
fn data(&self) -> *mut u8 {
147-
self.ptr.value()
149+
self.with_header().value()
150+
}
151+
152+
fn with_header(&self) -> &WithHeader<<T as Pointee>::Metadata> {
153+
// SAFETY: both types are transparent to `NonNull<u8>`
154+
unsafe { &*((&self.ptr) as *const WithOpaqueHeader as *const WithHeader<_>) }
148155
}
149156
}
150157

@@ -153,8 +160,22 @@ impl<T: ?Sized> ThinBox<T> {
153160
/// metadata (`H`) are ZSTs.
154161
/// 2. A pointer to a valid `T` that has a header `H` directly before the
155162
/// pointed-to location.
163+
#[repr(transparent)]
156164
struct WithHeader<H>(NonNull<u8>, PhantomData<H>);
157165

166+
/// An opaque representation of `WithHeader<H>` to avoid the
167+
/// projection invariance of `<T as Pointee>::Metadata`.
168+
#[repr(transparent)]
169+
struct WithOpaqueHeader(NonNull<u8>);
170+
171+
impl WithOpaqueHeader {
172+
#[cfg(not(no_global_oom_handling))]
173+
fn new<H, T>(header: H, value: T) -> Self {
174+
let ptr = WithHeader::new(header, value);
175+
Self(ptr.0)
176+
}
177+
}
178+
158179
impl<H> WithHeader<H> {
159180
#[cfg(not(no_global_oom_handling))]
160181
fn new<T>(header: H, value: T) -> WithHeader<H> {

library/alloc/tests/thin_box.rs

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ fn want_thin() {
2626
assert!(is_thin::<i32>());
2727
}
2828

29+
#[allow(dead_code)]
30+
fn assert_covariance() {
31+
fn thin_box<'new>(b: ThinBox<[&'static str]>) -> ThinBox<[&'new str]> {
32+
b
33+
}
34+
}
35+
2936
#[track_caller]
3037
fn verify_aligned<T>(ptr: *const T) {
3138
// Use `black_box` to attempt to obscure the fact that we're calling this

0 commit comments

Comments
 (0)