1
+ use core:: fmt:: Debug ;
1
2
use core:: mem:: size_of;
2
3
use std:: boxed:: ThinBox ;
3
4
@@ -24,3 +25,231 @@ fn want_thin() {
24
25
assert ! ( is_thin:: <[ i32 ] >( ) ) ;
25
26
assert ! ( is_thin:: <i32 >( ) ) ;
26
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
+ }
0 commit comments