2 use core::mem::size_of;
3 use std::boxed::ThinBox;
6 fn want_niche_optimization() {
7 fn uses_niche<T: ?Sized>() -> bool {
8 size_of::<*const ()>() == size_of::<Option<ThinBox<T>>>()
12 assert!(uses_niche::<dyn Tr>());
13 assert!(uses_niche::<[i32]>());
14 assert!(uses_niche::<i32>());
19 fn is_thin<T: ?Sized>() -> bool {
20 size_of::<*const ()>() == size_of::<ThinBox<T>>()
24 assert!(is_thin::<dyn Tr>());
25 assert!(is_thin::<[i32]>());
26 assert!(is_thin::<i32>());
30 fn assert_covariance() {
31 fn thin_box<'new>(b: ThinBox<[&'static str]>) -> ThinBox<[&'new str]> {
37 fn verify_aligned<T>(ptr: *const T) {
38 // Use `black_box` to attempt to obscure the fact that we're calling this
39 // function on pointers that come from box/references, which the compiler
40 // would otherwise realize is impossible (because it would mean we've
41 // already executed UB).
43 // That is, we'd *like* it to be possible for the asserts in this function
44 // to detect brokenness in the ThinBox impl.
46 // It would probably be better if we instead had these as debug_asserts
47 // inside `ThinBox`, prior to the point where we do the UB. Anyway, in
48 // practice these checks are mostly just smoke-detectors for an extremely
49 // broken `ThinBox` impl, since it's an extremely subtle piece of code.
50 let ptr = core::hint::black_box(ptr);
52 ptr.is_aligned() && !ptr.is_null(),
53 "misaligned ThinBox data; valid pointers to `{ty}` should be aligned to {align}: {ptr:p}",
54 ty = core::any::type_name::<T>(),
55 align = core::mem::align_of::<T>(),
60 fn check_thin_sized<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
62 let boxed = ThinBox::new(value.clone());
64 verify_aligned(val as *const T);
65 assert_eq!(val, &value);
69 fn check_thin_dyn<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
71 let wanted_debug = format!("{value:?}");
72 let boxed: ThinBox<dyn Debug> = ThinBox::new_unsize(value.clone());
74 // wide reference -> wide pointer -> thin pointer
75 verify_aligned(val as *const dyn Debug as *const T);
76 let got_debug = format!("{val:?}");
77 assert_eq!(wanted_debug, got_debug);
80 macro_rules! define_test {
82 @test_name: $testname:ident;
85 struct $Type:ident($inner:ty);
91 use core::sync::atomic::{AtomicIsize, Ordering};
92 // Define the type, and implement new/clone/drop in such a way that
93 // the number of live instances will be counted.
95 #[derive(Debug, PartialEq)]
100 impl Clone for $Type {
101 fn clone(&self) -> Self {
102 verify_aligned(self);
103 Self::new(self._priv.clone())
107 impl Drop for $Type {
109 verify_aligned(self);
110 Self::modify_live(-1);
115 fn new(i: $inner) -> Self {
116 Self::modify_live(1);
120 fn modify_live(n: isize) -> isize {
121 static COUNTER: AtomicIsize = AtomicIsize::new(0);
122 COUNTER.fetch_add(n, Ordering::Relaxed) + n
125 fn live_objects() -> isize {
129 // Run the test statements
130 let _: () = { $($test_stmts)* };
131 // Check that we didn't leak anything, or call drop too many times.
133 $Type::live_objects(), 0,
134 "Wrong number of drops of {}, `initializations - drops` should be 0.",
142 @test_name: align1zst;
143 struct Align1Zst(());
145 check_thin_sized(|| Align1Zst::new(()));
146 check_thin_dyn(|| Align1Zst::new(()));
150 @test_name: align1small;
151 struct Align1Small(u8);
153 check_thin_sized(|| Align1Small::new(50));
154 check_thin_dyn(|| Align1Small::new(50));
158 @test_name: align1_size_not_pow2;
159 struct Align64NotPow2Size([u8; 79]);
161 check_thin_sized(|| Align64NotPow2Size::new([100; 79]));
162 check_thin_dyn(|| Align64NotPow2Size::new([100; 79]));
166 @test_name: align1big;
167 struct Align1Big([u8; 256]);
169 check_thin_sized(|| Align1Big::new([5u8; 256]));
170 check_thin_dyn(|| Align1Big::new([5u8; 256]));
173 // Note: `#[repr(align(2))]` is worth testing because
174 // - can have pointers which are misaligned, unlike align(1)
175 // - is still expected to have an alignment less than the alignment of a vtable.
177 @test_name: align2zst;
179 struct Align2Zst(());
181 check_thin_sized(|| Align2Zst::new(()));
182 check_thin_dyn(|| Align2Zst::new(()));
186 @test_name: align2small;
188 struct Align2Small(u8);
190 check_thin_sized(|| Align2Small::new(60));
191 check_thin_dyn(|| Align2Small::new(60));
195 @test_name: align2full;
197 struct Align2Full([u8; 2]);
198 check_thin_sized(|| Align2Full::new([3u8; 2]));
199 check_thin_dyn(|| Align2Full::new([3u8; 2]));
203 @test_name: align2_size_not_pow2;
205 struct Align2NotPower2Size([u8; 6]);
207 check_thin_sized(|| Align2NotPower2Size::new([3; 6]));
208 check_thin_dyn(|| Align2NotPower2Size::new([3; 6]));
212 @test_name: align2big;
214 struct Align2Big([u8; 256]);
216 check_thin_sized(|| Align2Big::new([5u8; 256]));
217 check_thin_dyn(|| Align2Big::new([5u8; 256]));
221 @test_name: align64zst;
223 struct Align64Zst(());
225 check_thin_sized(|| Align64Zst::new(()));
226 check_thin_dyn(|| Align64Zst::new(()));
230 @test_name: align64small;
232 struct Align64Small(u8);
234 check_thin_sized(|| Align64Small::new(50));
235 check_thin_dyn(|| Align64Small::new(50));
239 @test_name: align64med;
241 struct Align64Med([u8; 64]);
242 check_thin_sized(|| Align64Med::new([10; 64]));
243 check_thin_dyn(|| Align64Med::new([10; 64]));
247 @test_name: align64_size_not_pow2;
249 struct Align64NotPow2Size([u8; 192]);
251 check_thin_sized(|| Align64NotPow2Size::new([10; 192]));
252 check_thin_dyn(|| Align64NotPow2Size::new([10; 192]));
256 @test_name: align64big;
258 struct Align64Big([u8; 256]);
260 check_thin_sized(|| Align64Big::new([10; 256]));
261 check_thin_dyn(|| Align64Big::new([10; 256]));