Skip to content

Commit ee8cea8

Browse files
authored
Rollup merge of rust-lang#90066 - yaahc:thinbox, r=joshtriplett
Add new ThinBox type for 1 stack pointer wide heap allocated trait objects **Zulip Thread**: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/ThinBox Based on https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs Tracking Issue: rust-lang#92791 Usage Trial: https://github.com/yaahc/pgx/pull/1/files ## TODO - [x] make sure to test with #[repr(align(1024))] structs etc
2 parents 340f649 + a87a0d0 commit ee8cea8

File tree

12 files changed

+388
-2
lines changed

12 files changed

+388
-2
lines changed

library/alloc/src/boxed.rs

+5
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ use crate::str::from_boxed_utf8_unchecked;
163163
#[cfg(not(no_global_oom_handling))]
164164
use crate::vec::Vec;
165165

166+
#[unstable(feature = "thin_box", issue = "92791")]
167+
pub use thin::ThinBox;
168+
169+
mod thin;
170+
166171
/// A pointer type for heap allocation.
167172
///
168173
/// See the [module-level documentation](../../std/boxed/index.html) for more.

library/alloc/src/boxed/thin.rs

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Based on
2+
// https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs
3+
// by matthieu-m
4+
use crate::alloc::{self, Layout, LayoutError};
5+
use core::fmt::{self, Debug, Display, Formatter};
6+
use core::marker::{PhantomData, Unsize};
7+
use core::mem;
8+
use core::ops::{Deref, DerefMut};
9+
use core::ptr::Pointee;
10+
use core::ptr::{self, NonNull};
11+
12+
/// ThinBox.
13+
///
14+
/// A thin pointer for heap allocation, regardless of T.
15+
///
16+
/// # Examples
17+
///
18+
/// ```
19+
/// #![feature(thin_box)]
20+
/// use std::boxed::ThinBox;
21+
///
22+
/// let five = ThinBox::new(5);
23+
/// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]);
24+
///
25+
/// use std::mem::{size_of, size_of_val};
26+
/// let size_of_ptr = size_of::<*const ()>();
27+
/// assert_eq!(size_of_ptr, size_of_val(&five));
28+
/// assert_eq!(size_of_ptr, size_of_val(&thin_slice));
29+
/// ```
30+
#[unstable(feature = "thin_box", issue = "92791")]
31+
pub struct ThinBox<T: ?Sized> {
32+
ptr: WithHeader<<T as Pointee>::Metadata>,
33+
_marker: PhantomData<T>,
34+
}
35+
36+
#[unstable(feature = "thin_box", issue = "92791")]
37+
impl<T> ThinBox<T> {
38+
/// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
39+
/// the stack.
40+
///
41+
/// # Examples
42+
///
43+
/// ```
44+
/// #![feature(thin_box)]
45+
/// use std::boxed::ThinBox;
46+
///
47+
/// let five = ThinBox::new(5);
48+
/// ```
49+
#[cfg(not(no_global_oom_handling))]
50+
pub fn new(value: T) -> Self {
51+
let meta = ptr::metadata(&value);
52+
let ptr = WithHeader::new(meta, value);
53+
ThinBox { ptr, _marker: PhantomData }
54+
}
55+
}
56+
57+
#[unstable(feature = "thin_box", issue = "92791")]
58+
impl<Dyn: ?Sized> ThinBox<Dyn> {
59+
/// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
60+
/// the stack.
61+
///
62+
/// # Examples
63+
///
64+
/// ```
65+
/// #![feature(thin_box)]
66+
/// use std::boxed::ThinBox;
67+
///
68+
/// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]);
69+
/// ```
70+
#[cfg(not(no_global_oom_handling))]
71+
pub fn new_unsize<T>(value: T) -> Self
72+
where
73+
T: Unsize<Dyn>,
74+
{
75+
let meta = ptr::metadata(&value as &Dyn);
76+
let ptr = WithHeader::new(meta, value);
77+
ThinBox { ptr, _marker: PhantomData }
78+
}
79+
}
80+
81+
#[unstable(feature = "thin_box", issue = "92791")]
82+
impl<T: ?Sized + Debug> Debug for ThinBox<T> {
83+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84+
Debug::fmt(self.deref(), f)
85+
}
86+
}
87+
88+
#[unstable(feature = "thin_box", issue = "92791")]
89+
impl<T: ?Sized + Display> Display for ThinBox<T> {
90+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
91+
Display::fmt(self.deref(), f)
92+
}
93+
}
94+
95+
#[unstable(feature = "thin_box", issue = "92791")]
96+
impl<T: ?Sized> Deref for ThinBox<T> {
97+
type Target = T;
98+
99+
fn deref(&self) -> &T {
100+
let value = self.data();
101+
let metadata = self.meta();
102+
let pointer = ptr::from_raw_parts(value as *const (), metadata);
103+
unsafe { &*pointer }
104+
}
105+
}
106+
107+
#[unstable(feature = "thin_box", issue = "92791")]
108+
impl<T: ?Sized> DerefMut for ThinBox<T> {
109+
fn deref_mut(&mut self) -> &mut T {
110+
let value = self.data();
111+
let metadata = self.meta();
112+
let pointer = ptr::from_raw_parts_mut::<T>(value as *mut (), metadata);
113+
unsafe { &mut *pointer }
114+
}
115+
}
116+
117+
#[unstable(feature = "thin_box", issue = "92791")]
118+
impl<T: ?Sized> Drop for ThinBox<T> {
119+
fn drop(&mut self) {
120+
unsafe {
121+
let value = self.deref_mut();
122+
let value = value as *mut T;
123+
self.ptr.drop::<T>(value);
124+
}
125+
}
126+
}
127+
128+
#[unstable(feature = "thin_box", issue = "92791")]
129+
impl<T: ?Sized> ThinBox<T> {
130+
fn meta(&self) -> <T as Pointee>::Metadata {
131+
// Safety:
132+
// - NonNull and valid.
133+
unsafe { *self.ptr.header() }
134+
}
135+
136+
fn data(&self) -> *mut u8 {
137+
self.ptr.value()
138+
}
139+
}
140+
141+
/// A pointer to type-erased data, guaranteed to have a header `H` before the pointed-to location.
142+
struct WithHeader<H>(NonNull<u8>, PhantomData<H>);
143+
144+
impl<H> WithHeader<H> {
145+
#[cfg(not(no_global_oom_handling))]
146+
fn new<T>(header: H, value: T) -> WithHeader<H> {
147+
let value_layout = Layout::new::<T>();
148+
let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else {
149+
// We pass an empty layout here because we do not know which layout caused the
150+
// arithmetic overflow in `Layout::extend` and `handle_alloc_error` takes `Layout` as
151+
// its argument rather than `Result<Layout, LayoutError>`, also this function has been
152+
// stable since 1.28 ._.
153+
//
154+
// On the other hand, look at this gorgeous turbofish!
155+
alloc::handle_alloc_error(Layout::new::<()>());
156+
};
157+
158+
unsafe {
159+
let ptr = alloc::alloc(layout);
160+
161+
if ptr.is_null() {
162+
alloc::handle_alloc_error(layout);
163+
}
164+
// Safety:
165+
// - The size is at least `aligned_header_size`.
166+
let ptr = ptr.add(value_offset) as *mut _;
167+
168+
let ptr = NonNull::new_unchecked(ptr);
169+
170+
let result = WithHeader(ptr, PhantomData);
171+
ptr::write(result.header(), header);
172+
ptr::write(result.value().cast(), value);
173+
174+
result
175+
}
176+
}
177+
178+
// Safety:
179+
// - Assumes that `value` can be dereferenced.
180+
unsafe fn drop<T: ?Sized>(&self, value: *mut T) {
181+
unsafe {
182+
// SAFETY: Layout must have been computable if we're in drop
183+
let (layout, value_offset) =
184+
Self::alloc_layout(Layout::for_value_raw(value)).unwrap_unchecked();
185+
186+
ptr::drop_in_place::<T>(value);
187+
// We only drop the value because the Pointee trait requires that the metadata is copy
188+
// aka trivially droppable
189+
alloc::dealloc(self.0.as_ptr().sub(value_offset), layout);
190+
}
191+
}
192+
193+
fn header(&self) -> *mut H {
194+
// Safety:
195+
// - At least `size_of::<H>()` bytes are allocated ahead of the pointer.
196+
// - We know that H will be aligned because the middle pointer is aligned to the greater
197+
// of the alignment of the header and the data and the header size includes the padding
198+
// needed to align the header. Subtracting the header size from the aligned data pointer
199+
// will always result in an aligned header pointer, it just may not point to the
200+
// beginning of the allocation.
201+
unsafe { self.0.as_ptr().sub(Self::header_size()) as *mut H }
202+
}
203+
204+
fn value(&self) -> *mut u8 {
205+
self.0.as_ptr()
206+
}
207+
208+
const fn header_size() -> usize {
209+
mem::size_of::<H>()
210+
}
211+
212+
fn alloc_layout(value_layout: Layout) -> Result<(Layout, usize), LayoutError> {
213+
Layout::new::<H>().extend(value_layout)
214+
}
215+
}

library/alloc/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
#![feature(nonnull_slice_from_raw_parts)]
121121
#![feature(pattern)]
122122
#![feature(ptr_internals)]
123+
#![feature(ptr_metadata)]
123124
#![feature(receiver_trait)]
124125
#![feature(set_ptr_value)]
125126
#![feature(slice_group_by)]
@@ -152,6 +153,7 @@
152153
#![feature(fundamental)]
153154
#![cfg_attr(not(test), feature(generator_trait))]
154155
#![feature(lang_items)]
156+
#![feature(let_else)]
155157
#![feature(min_specialization)]
156158
#![feature(negative_impls)]
157159
#![feature(never_type)]

library/alloc/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#![feature(nonnull_slice_from_raw_parts)]
4040
#![feature(panic_update_hook)]
4141
#![feature(slice_flatten)]
42+
#![feature(thin_box)]
4243

4344
use std::collections::hash_map::DefaultHasher;
4445
use std::hash::{Hash, Hasher};
@@ -57,6 +58,7 @@ mod rc;
5758
mod slice;
5859
mod str;
5960
mod string;
61+
mod thin_box;
6062
mod vec;
6163
mod vec_deque;
6264

library/alloc/tests/thin_box.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use alloc::boxed::ThinBox;
2+
use core::mem::size_of;
3+
4+
#[test]
5+
fn want_niche_optimization() {
6+
fn uses_niche<T: ?Sized>() -> bool {
7+
size_of::<*const ()>() == size_of::<Option<ThinBox<T>>>()
8+
}
9+
10+
trait Tr {}
11+
assert!(uses_niche::<dyn Tr>());
12+
assert!(uses_niche::<[i32]>());
13+
assert!(uses_niche::<i32>());
14+
}
15+
16+
#[test]
17+
fn want_thin() {
18+
fn is_thin<T: ?Sized>() -> bool {
19+
size_of::<*const ()>() == size_of::<ThinBox<T>>()
20+
}
21+
22+
trait Tr {}
23+
assert!(is_thin::<dyn Tr>());
24+
assert!(is_thin::<[i32]>());
25+
assert!(is_thin::<i32>());
26+
}

library/std/src/error.rs

+8
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,14 @@ impl<T: Error> Error for Box<T> {
516516
}
517517
}
518518

519+
#[unstable(feature = "thin_box", issue = "92791")]
520+
impl<T: ?Sized + crate::error::Error> crate::error::Error for crate::boxed::ThinBox<T> {
521+
fn source(&self) -> Option<&(dyn crate::error::Error + 'static)> {
522+
use core::ops::Deref;
523+
self.deref().source()
524+
}
525+
}
526+
519527
#[stable(feature = "error_by_ref", since = "1.51.0")]
520528
impl<'a, T: Error + ?Sized> Error for &'a T {
521529
#[allow(deprecated, deprecated_in_future)]

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@
290290
#![feature(get_mut_unchecked)]
291291
#![feature(map_try_insert)]
292292
#![feature(new_uninit)]
293+
#![feature(thin_box)]
293294
#![feature(toowned_clone_into)]
294295
#![feature(try_reserve_kind)]
295296
#![feature(vec_into_raw_parts)]

src/test/ui/box/thin_align.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![feature(thin_box)]
2+
// run-pass
3+
use std::boxed::ThinBox;
4+
use std::error::Error;
5+
use std::ops::Deref;
6+
use std::fmt;
7+
8+
fn main() {
9+
let expected = "Foo error!";
10+
let a: ThinBox<dyn Error> = ThinBox::new_unsize(Foo(expected));
11+
let a = a.deref();
12+
let msg = a.to_string();
13+
assert_eq!(expected, msg);
14+
}
15+
16+
#[derive(Debug)]
17+
#[repr(align(1024))]
18+
struct Foo(&'static str);
19+
20+
impl fmt::Display for Foo {
21+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22+
write!(f, "{}", self.0)
23+
}
24+
}
25+
26+
impl Error for Foo {}

src/test/ui/box/thin_drop.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(thin_box)]
2+
// run-pass
3+
use std::boxed::ThinBox;
4+
use std::error::Error;
5+
use std::ops::Deref;
6+
use std::fmt;
7+
8+
fn main() {
9+
let expected = "Foo error!";
10+
let mut dropped = false;
11+
{
12+
let foo = Foo(expected, &mut dropped);
13+
let a: ThinBox<dyn Error> = ThinBox::new_unsize(foo);
14+
let a = a.deref();
15+
let msg = a.to_string();
16+
assert_eq!(expected, msg);
17+
}
18+
assert!(dropped);
19+
}
20+
21+
#[derive(Debug)]
22+
#[repr(align(1024))]
23+
struct Foo<'a>(&'static str, &'a mut bool);
24+
25+
impl Drop for Foo<'_> {
26+
fn drop(&mut self) {
27+
*self.1 = true;
28+
}
29+
}
30+
31+
impl fmt::Display for Foo<'_> {
32+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33+
write!(f, "{}", self.0)
34+
}
35+
}
36+
37+
impl Error for Foo<'_> {}

0 commit comments

Comments
 (0)