]> git.lizzy.rs Git - rust.git/blob - library/alloc/tests/thin_box.rs
Rollup merge of #98429 - b-naber:use-correct-substs-discriminant-cast, r=lcnr
[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 #[track_caller]
30 fn verify_aligned<T>(ptr: *const T) {
31     // Use `black_box` to attempt to obscure the fact that we're calling this
32     // function on pointers that come from box/references, which the compiler
33     // would otherwise realize is impossible (because it would mean we've
34     // already executed UB).
35     //
36     // That is, we'd *like* it to be possible for the asserts in this function
37     // to detect brokenness in the ThinBox impl.
38     //
39     // It would probably be better if we instead had these as debug_asserts
40     // inside `ThinBox`, prior to the point where we do the UB. Anyway, in
41     // practice these checks are mostly just smoke-detectors for an extremely
42     // broken `ThinBox` impl, since it's an extremely subtle piece of code.
43     let ptr = core::hint::black_box(ptr);
44     let align = core::mem::align_of::<T>();
45     assert!(
46         (ptr.addr() & (align - 1)) == 0 && !ptr.is_null(),
47         "misaligned ThinBox data; valid pointers to `{}` should be aligned to {align}: {ptr:p}",
48         core::any::type_name::<T>(),
49     );
50 }
51
52 #[track_caller]
53 fn check_thin_sized<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
54     let value = make();
55     let boxed = ThinBox::new(value.clone());
56     let val = &*boxed;
57     verify_aligned(val as *const T);
58     assert_eq!(val, &value);
59 }
60
61 #[track_caller]
62 fn check_thin_dyn<T: Debug + PartialEq + Clone>(make: impl FnOnce() -> T) {
63     let value = make();
64     let wanted_debug = format!("{value:?}");
65     let boxed: ThinBox<dyn Debug> = ThinBox::new_unsize(value.clone());
66     let val = &*boxed;
67     // wide reference -> wide pointer -> thin pointer
68     verify_aligned(val as *const dyn Debug as *const T);
69     let got_debug = format!("{val:?}");
70     assert_eq!(wanted_debug, got_debug);
71 }
72
73 macro_rules! define_test {
74     (
75         @test_name: $testname:ident;
76
77         $(#[$m:meta])*
78         struct $Type:ident($inner:ty);
79
80         $($test_stmts:tt)*
81     ) => {
82         #[test]
83         fn $testname() {
84             use core::sync::atomic::{AtomicIsize, Ordering};
85             // Define the type, and implement new/clone/drop in such a way that
86             // the number of live instances will be counted.
87             $(#[$m])*
88             #[derive(Debug, PartialEq)]
89             struct $Type {
90                 _priv: $inner,
91             }
92
93             impl Clone for $Type {
94                 fn clone(&self) -> Self {
95                     verify_aligned(self);
96                     Self::new(self._priv.clone())
97                 }
98             }
99
100             impl Drop for $Type {
101                 fn drop(&mut self) {
102                     verify_aligned(self);
103                     Self::modify_live(-1);
104                 }
105             }
106
107             impl $Type {
108                 fn new(i: $inner) -> Self {
109                     Self::modify_live(1);
110                     Self { _priv: i }
111                 }
112
113                 fn modify_live(n: isize) -> isize {
114                     static COUNTER: AtomicIsize = AtomicIsize::new(0);
115                     COUNTER.fetch_add(n, Ordering::Relaxed) + n
116                 }
117
118                 fn live_objects() -> isize {
119                     Self::modify_live(0)
120                 }
121             }
122             // Run the test statements
123             let _: () = { $($test_stmts)* };
124             // Check that we didn't leak anything, or call drop too many times.
125             assert_eq!(
126                 $Type::live_objects(), 0,
127                 "Wrong number of drops of {}, `initializations - drops` should be 0.",
128                 stringify!($Type),
129             );
130         }
131     };
132 }
133
134 define_test! {
135     @test_name: align1zst;
136     struct Align1Zst(());
137
138     check_thin_sized(|| Align1Zst::new(()));
139     check_thin_dyn(|| Align1Zst::new(()));
140 }
141
142 define_test! {
143     @test_name: align1small;
144     struct Align1Small(u8);
145
146     check_thin_sized(|| Align1Small::new(50));
147     check_thin_dyn(|| Align1Small::new(50));
148 }
149
150 define_test! {
151     @test_name: align1_size_not_pow2;
152     struct Align64NotPow2Size([u8; 79]);
153
154     check_thin_sized(|| Align64NotPow2Size::new([100; 79]));
155     check_thin_dyn(|| Align64NotPow2Size::new([100; 79]));
156 }
157
158 define_test! {
159     @test_name: align1big;
160     struct Align1Big([u8; 256]);
161
162     check_thin_sized(|| Align1Big::new([5u8; 256]));
163     check_thin_dyn(|| Align1Big::new([5u8; 256]));
164 }
165
166 // Note: `#[repr(align(2))]` is worth testing because
167 // - can have pointers which are misaligned, unlike align(1)
168 // - is still expected to have an alignment less than the alignment of a vtable.
169 define_test! {
170     @test_name: align2zst;
171     #[repr(align(2))]
172     struct Align2Zst(());
173
174     check_thin_sized(|| Align2Zst::new(()));
175     check_thin_dyn(|| Align2Zst::new(()));
176 }
177
178 define_test! {
179     @test_name: align2small;
180     #[repr(align(2))]
181     struct Align2Small(u8);
182
183     check_thin_sized(|| Align2Small::new(60));
184     check_thin_dyn(|| Align2Small::new(60));
185 }
186
187 define_test! {
188     @test_name: align2full;
189     #[repr(align(2))]
190     struct Align2Full([u8; 2]);
191     check_thin_sized(|| Align2Full::new([3u8; 2]));
192     check_thin_dyn(|| Align2Full::new([3u8; 2]));
193 }
194
195 define_test! {
196     @test_name: align2_size_not_pow2;
197     #[repr(align(2))]
198     struct Align2NotPower2Size([u8; 6]);
199
200     check_thin_sized(|| Align2NotPower2Size::new([3; 6]));
201     check_thin_dyn(|| Align2NotPower2Size::new([3; 6]));
202 }
203
204 define_test! {
205     @test_name: align2big;
206     #[repr(align(2))]
207     struct Align2Big([u8; 256]);
208
209     check_thin_sized(|| Align2Big::new([5u8; 256]));
210     check_thin_dyn(|| Align2Big::new([5u8; 256]));
211 }
212
213 define_test! {
214     @test_name: align64zst;
215     #[repr(align(64))]
216     struct Align64Zst(());
217
218     check_thin_sized(|| Align64Zst::new(()));
219     check_thin_dyn(|| Align64Zst::new(()));
220 }
221
222 define_test! {
223     @test_name: align64small;
224     #[repr(align(64))]
225     struct Align64Small(u8);
226
227     check_thin_sized(|| Align64Small::new(50));
228     check_thin_dyn(|| Align64Small::new(50));
229 }
230
231 define_test! {
232     @test_name: align64med;
233     #[repr(align(64))]
234     struct Align64Med([u8; 64]);
235     check_thin_sized(|| Align64Med::new([10; 64]));
236     check_thin_dyn(|| Align64Med::new([10; 64]));
237 }
238
239 define_test! {
240     @test_name: align64_size_not_pow2;
241     #[repr(align(64))]
242     struct Align64NotPow2Size([u8; 192]);
243
244     check_thin_sized(|| Align64NotPow2Size::new([10; 192]));
245     check_thin_dyn(|| Align64NotPow2Size::new([10; 192]));
246 }
247
248 define_test! {
249     @test_name: align64big;
250     #[repr(align(64))]
251     struct Align64Big([u8; 256]);
252
253     check_thin_sized(|| Align64Big::new([10; 256]));
254     check_thin_dyn(|| Align64Big::new([10; 256]));
255 }