]> git.lizzy.rs Git - rust.git/blob - library/alloc/tests/thin_box.rs
Link to the LLVM issue from a comment on `SpecOptionPartialEq`
[rust.git] / library / alloc / tests / thin_box.rs
1 use core::fmt::Debug;
2 use core::mem::size_of;
3 use std::boxed::ThinBox;
4
5 #[test]
6 fn want_niche_optimization() {
7     fn uses_niche<T: ?Sized>() -> bool {
8         size_of::<*const ()>() == size_of::<Option<ThinBox<T>>>()
9     }
10
11     trait Tr {}
12     assert!(uses_niche::<dyn Tr>());
13     assert!(uses_niche::<[i32]>());
14     assert!(uses_niche::<i32>());
15 }
16
17 #[test]
18 fn want_thin() {
19     fn is_thin<T: ?Sized>() -> bool {
20         size_of::<*const ()>() == size_of::<ThinBox<T>>()
21     }
22
23     trait Tr {}
24     assert!(is_thin::<dyn Tr>());
25     assert!(is_thin::<[i32]>());
26     assert!(is_thin::<i32>());
27 }
28
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
36 #[track_caller]
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).
42     //
43     // That is, we'd *like* it to be possible for the asserts in this function
44     // to detect brokenness in the ThinBox impl.
45     //
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);
51     assert!(
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>(),
56     );
57 }
58
59 #[track_caller]
60 fn check_thin_sized<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
61     let value = make();
62     let boxed = ThinBox::new(value.clone());
63     let val = &*boxed;
64     verify_aligned(val as *const T);
65     assert_eq!(val, &value);
66 }
67
68 #[track_caller]
69 fn check_thin_dyn<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
70     let value = make();
71     let wanted_debug = format!("{value:?}");
72     let boxed: ThinBox<dyn Debug> = ThinBox::new_unsize(value.clone());
73     let val = &*boxed;
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);
78 }
79
80 macro_rules! define_test {
81     (
82         @test_name: $testname:ident;
83
84         $(#[$m:meta])*
85         struct $Type:ident($inner:ty);
86
87         $($test_stmts:tt)*
88     ) => {
89         #[test]
90         fn $testname() {
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.
94             $(#[$m])*
95             #[derive(Debug, PartialEq)]
96             struct $Type {
97                 _priv: $inner,
98             }
99
100             impl Clone for $Type {
101                 fn clone(&self) -> Self {
102                     verify_aligned(self);
103                     Self::new(self._priv.clone())
104                 }
105             }
106
107             impl Drop for $Type {
108                 fn drop(&mut self) {
109                     verify_aligned(self);
110                     Self::modify_live(-1);
111                 }
112             }
113
114             impl $Type {
115                 fn new(i: $inner) -> Self {
116                     Self::modify_live(1);
117                     Self { _priv: i }
118                 }
119
120                 fn modify_live(n: isize) -> isize {
121                     static COUNTER: AtomicIsize = AtomicIsize::new(0);
122                     COUNTER.fetch_add(n, Ordering::Relaxed) + n
123                 }
124
125                 fn live_objects() -> isize {
126                     Self::modify_live(0)
127                 }
128             }
129             // Run the test statements
130             let _: () = { $($test_stmts)* };
131             // Check that we didn't leak anything, or call drop too many times.
132             assert_eq!(
133                 $Type::live_objects(), 0,
134                 "Wrong number of drops of {}, `initializations - drops` should be 0.",
135                 stringify!($Type),
136             );
137         }
138     };
139 }
140
141 define_test! {
142     @test_name: align1zst;
143     struct Align1Zst(());
144
145     check_thin_sized(|| Align1Zst::new(()));
146     check_thin_dyn(|| Align1Zst::new(()));
147 }
148
149 define_test! {
150     @test_name: align1small;
151     struct Align1Small(u8);
152
153     check_thin_sized(|| Align1Small::new(50));
154     check_thin_dyn(|| Align1Small::new(50));
155 }
156
157 define_test! {
158     @test_name: align1_size_not_pow2;
159     struct Align64NotPow2Size([u8; 79]);
160
161     check_thin_sized(|| Align64NotPow2Size::new([100; 79]));
162     check_thin_dyn(|| Align64NotPow2Size::new([100; 79]));
163 }
164
165 define_test! {
166     @test_name: align1big;
167     struct Align1Big([u8; 256]);
168
169     check_thin_sized(|| Align1Big::new([5u8; 256]));
170     check_thin_dyn(|| Align1Big::new([5u8; 256]));
171 }
172
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.
176 define_test! {
177     @test_name: align2zst;
178     #[repr(align(2))]
179     struct Align2Zst(());
180
181     check_thin_sized(|| Align2Zst::new(()));
182     check_thin_dyn(|| Align2Zst::new(()));
183 }
184
185 define_test! {
186     @test_name: align2small;
187     #[repr(align(2))]
188     struct Align2Small(u8);
189
190     check_thin_sized(|| Align2Small::new(60));
191     check_thin_dyn(|| Align2Small::new(60));
192 }
193
194 define_test! {
195     @test_name: align2full;
196     #[repr(align(2))]
197     struct Align2Full([u8; 2]);
198     check_thin_sized(|| Align2Full::new([3u8; 2]));
199     check_thin_dyn(|| Align2Full::new([3u8; 2]));
200 }
201
202 define_test! {
203     @test_name: align2_size_not_pow2;
204     #[repr(align(2))]
205     struct Align2NotPower2Size([u8; 6]);
206
207     check_thin_sized(|| Align2NotPower2Size::new([3; 6]));
208     check_thin_dyn(|| Align2NotPower2Size::new([3; 6]));
209 }
210
211 define_test! {
212     @test_name: align2big;
213     #[repr(align(2))]
214     struct Align2Big([u8; 256]);
215
216     check_thin_sized(|| Align2Big::new([5u8; 256]));
217     check_thin_dyn(|| Align2Big::new([5u8; 256]));
218 }
219
220 define_test! {
221     @test_name: align64zst;
222     #[repr(align(64))]
223     struct Align64Zst(());
224
225     check_thin_sized(|| Align64Zst::new(()));
226     check_thin_dyn(|| Align64Zst::new(()));
227 }
228
229 define_test! {
230     @test_name: align64small;
231     #[repr(align(64))]
232     struct Align64Small(u8);
233
234     check_thin_sized(|| Align64Small::new(50));
235     check_thin_dyn(|| Align64Small::new(50));
236 }
237
238 define_test! {
239     @test_name: align64med;
240     #[repr(align(64))]
241     struct Align64Med([u8; 64]);
242     check_thin_sized(|| Align64Med::new([10; 64]));
243     check_thin_dyn(|| Align64Med::new([10; 64]));
244 }
245
246 define_test! {
247     @test_name: align64_size_not_pow2;
248     #[repr(align(64))]
249     struct Align64NotPow2Size([u8; 192]);
250
251     check_thin_sized(|| Align64NotPow2Size::new([10; 192]));
252     check_thin_dyn(|| Align64NotPow2Size::new([10; 192]));
253 }
254
255 define_test! {
256     @test_name: align64big;
257     #[repr(align(64))]
258     struct Align64Big([u8; 256]);
259
260     check_thin_sized(|| Align64Big::new([10; 256]));
261     check_thin_dyn(|| Align64Big::new([10; 256]));
262 }