Skip to content

Commit e67e165

Browse files
committedJun 27, 2022
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.
1 parent bd2e51a commit e67e165

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
@@ -29,7 +29,9 @@ use core::ptr::{self, NonNull};
2929
/// ```
3030
#[unstable(feature = "thin_box", issue = "92791")]
3131
pub struct ThinBox<T: ?Sized> {
32-
ptr: WithHeader<<T as Pointee>::Metadata>,
32+
// This is essentially `WithHeader<<T as Pointee>::Metadata>`,
33+
// but that would be invariant in `T`, and we want covariance.
34+
ptr: WithOpaqueHeader,
3335
_marker: PhantomData<T>,
3436
}
3537

@@ -49,7 +51,7 @@ impl<T> ThinBox<T> {
4951
#[cfg(not(no_global_oom_handling))]
5052
pub fn new(value: T) -> Self {
5153
let meta = ptr::metadata(&value);
52-
let ptr = WithHeader::new(meta, value);
54+
let ptr = WithOpaqueHeader::new(meta, value);
5355
ThinBox { ptr, _marker: PhantomData }
5456
}
5557
}
@@ -73,7 +75,7 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
7375
T: Unsize<Dyn>,
7476
{
7577
let meta = ptr::metadata(&value as &Dyn);
76-
let ptr = WithHeader::new(meta, value);
78+
let ptr = WithOpaqueHeader::new(meta, value);
7779
ThinBox { ptr, _marker: PhantomData }
7880
}
7981
}
@@ -120,7 +122,7 @@ impl<T: ?Sized> Drop for ThinBox<T> {
120122
unsafe {
121123
let value = self.deref_mut();
122124
let value = value as *mut T;
123-
self.ptr.drop::<T>(value);
125+
self.with_header().drop::<T>(value);
124126
}
125127
}
126128
}
@@ -130,11 +132,16 @@ impl<T: ?Sized> ThinBox<T> {
130132
fn meta(&self) -> <T as Pointee>::Metadata {
131133
// Safety:
132134
// - NonNull and valid.
133-
unsafe { *self.ptr.header() }
135+
unsafe { *self.with_header().header() }
134136
}
135137

136138
fn data(&self) -> *mut u8 {
137-
self.ptr.value()
139+
self.with_header().value()
140+
}
141+
142+
fn with_header(&self) -> &WithHeader<<T as Pointee>::Metadata> {
143+
// SAFETY: both types are transparent to `NonNull<u8>`
144+
unsafe { &*((&self.ptr) as *const WithOpaqueHeader as *const WithHeader<_>) }
138145
}
139146
}
140147

@@ -143,8 +150,22 @@ impl<T: ?Sized> ThinBox<T> {
143150
/// metadata (`H`) are ZSTs.
144151
/// 2. A pointer to a valid `T` that has a header `H` directly before the
145152
/// pointed-to location.
153+
#[repr(transparent)]
146154
struct WithHeader<H>(NonNull<u8>, PhantomData<H>);
147155

156+
/// An opaque representation of `WithHeader<H>` to avoid the
157+
/// projection invariance of `<T as Pointee>::Metadata`.
158+
#[repr(transparent)]
159+
struct WithOpaqueHeader(NonNull<u8>);
160+
161+
impl WithOpaqueHeader {
162+
#[cfg(not(no_global_oom_handling))]
163+
fn new<H, T>(header: H, value: T) -> Self {
164+
let ptr = WithHeader::new(header, value);
165+
Self(ptr.0)
166+
}
167+
}
168+
148169
impl<H> WithHeader<H> {
149170
#[cfg(not(no_global_oom_handling))]
150171
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)
Please sign in to comment.