1
1
use rustc_middle:: query:: Providers ;
2
2
use rustc_middle:: ty:: layout:: {
3
- IntegerExt , LayoutCx , LayoutError , LayoutOf , NaiveAbi , NaiveLayout , TyAndNaiveLayout ,
3
+ IntegerExt , LayoutCx , LayoutError , LayoutOf , NaiveAbi , NaiveLayout , NaiveNiches ,
4
+ TyAndNaiveLayout ,
4
5
} ;
5
6
use rustc_middle:: ty:: { self , ReprOptions , Ty , TyCtxt , TypeVisitableExt } ;
6
-
7
7
use rustc_span:: DUMMY_SP ;
8
8
use rustc_target:: abi:: * ;
9
9
10
+ use std:: ops:: Bound ;
11
+
10
12
use crate :: layout:: { compute_array_count, ptr_metadata_scalar} ;
11
13
12
14
pub fn provide ( providers : & mut Providers ) {
@@ -61,8 +63,9 @@ fn naive_layout_of_uncached<'tcx>(
61
63
let tcx = cx. tcx ;
62
64
let dl = cx. data_layout ( ) ;
63
65
64
- let scalar = |value : Primitive | NaiveLayout {
66
+ let scalar = |niched : bool , value : Primitive | NaiveLayout {
65
67
abi : NaiveAbi :: Scalar ( value) ,
68
+ niches : if niched { NaiveNiches :: Some } else { NaiveNiches :: None } ,
66
69
size : value. size ( dl) ,
67
70
align : value. align ( dl) . abi ,
68
71
exact : true ,
@@ -105,26 +108,30 @@ fn naive_layout_of_uncached<'tcx>(
105
108
106
109
Ok ( match * ty. kind ( ) {
107
110
// Basic scalars
108
- ty:: Bool => scalar ( Int ( I8 , false ) ) ,
109
- ty:: Char => scalar ( Int ( I32 , false ) ) ,
110
- ty:: Int ( ity) => scalar ( Int ( Integer :: from_int_ty ( dl, ity) , true ) ) ,
111
- ty:: Uint ( ity) => scalar ( Int ( Integer :: from_uint_ty ( dl, ity) , false ) ) ,
112
- ty:: Float ( fty) => scalar ( match fty {
113
- ty:: FloatTy :: F32 => F32 ,
114
- ty:: FloatTy :: F64 => F64 ,
115
- } ) ,
116
- ty:: FnPtr ( _) => scalar ( Pointer ( dl. instruction_address_space ) ) ,
111
+ ty:: Bool => scalar ( true , Int ( I8 , false ) ) ,
112
+ ty:: Char => scalar ( true , Int ( I32 , false ) ) ,
113
+ ty:: Int ( ity) => scalar ( false , Int ( Integer :: from_int_ty ( dl, ity) , true ) ) ,
114
+ ty:: Uint ( ity) => scalar ( false , Int ( Integer :: from_uint_ty ( dl, ity) , false ) ) ,
115
+ ty:: Float ( fty) => scalar (
116
+ false ,
117
+ match fty {
118
+ ty:: FloatTy :: F32 => F32 ,
119
+ ty:: FloatTy :: F64 => F64 ,
120
+ } ,
121
+ ) ,
122
+ ty:: FnPtr ( _) => scalar ( true , Pointer ( dl. instruction_address_space ) ) ,
117
123
118
124
// The never type.
119
125
ty:: Never => NaiveLayout { abi : NaiveAbi :: Uninhabited , ..NaiveLayout :: EMPTY } ,
120
126
121
127
// Potentially-wide pointers.
122
128
ty:: Ref ( _, pointee, _) | ty:: RawPtr ( ty:: TypeAndMut { ty : pointee, .. } ) => {
123
- let data_ptr = scalar ( Pointer ( AddressSpace :: DATA ) ) ;
129
+ let data_ptr = scalar ( !ty . is_unsafe_ptr ( ) , Pointer ( AddressSpace :: DATA ) ) ;
124
130
if let Some ( metadata) = ptr_metadata_scalar ( cx, pointee) ? {
125
131
// Effectively a (ptr, meta) tuple.
132
+ let meta = scalar ( !metadata. is_always_valid ( dl) , metadata. primitive ( ) ) ;
126
133
let l = data_ptr
127
- . concat ( & scalar ( metadata . primitive ( ) ) , dl)
134
+ . concat ( & meta , dl)
128
135
. ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?;
129
136
l. pad_to_align ( l. align )
130
137
} else {
@@ -134,8 +141,9 @@ fn naive_layout_of_uncached<'tcx>(
134
141
}
135
142
136
143
ty:: Dynamic ( _, _, ty:: DynStar ) => {
137
- let ptr = scalar ( Pointer ( AddressSpace :: DATA ) ) ;
138
- ptr. concat ( & ptr, dl) . ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?
144
+ let ptr = scalar ( false , Pointer ( AddressSpace :: DATA ) ) ;
145
+ let vtable = scalar ( true , Pointer ( AddressSpace :: DATA ) ) ;
146
+ ptr. concat ( & vtable, dl) . ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?
139
147
}
140
148
141
149
// Arrays and slices.
@@ -149,13 +157,16 @@ fn naive_layout_of_uncached<'tcx>(
149
157
. size
150
158
. checked_mul ( count, cx)
151
159
. ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?,
160
+ niches : if count == 0 { NaiveNiches :: None } else { element. niches } ,
152
161
..* element
153
162
}
154
163
}
155
- ty:: Slice ( element) => {
156
- let element = cx. naive_layout_of ( element) ?;
157
- NaiveLayout { abi : NaiveAbi :: Unsized , size : Size :: ZERO , ..* element }
158
- }
164
+ ty:: Slice ( element) => NaiveLayout {
165
+ abi : NaiveAbi :: Unsized ,
166
+ size : Size :: ZERO ,
167
+ niches : NaiveNiches :: None ,
168
+ ..* cx. naive_layout_of ( element) ?
169
+ } ,
159
170
160
171
ty:: FnDef ( ..) => NaiveLayout :: EMPTY ,
161
172
@@ -166,7 +177,9 @@ fn naive_layout_of_uncached<'tcx>(
166
177
167
178
// FIXME(reference_niches): try to actually compute a reasonable layout estimate,
168
179
// without duplicating too much code from `generator_layout`.
169
- ty:: Generator ( ..) => NaiveLayout { exact : false , ..NaiveLayout :: EMPTY } ,
180
+ ty:: Generator ( ..) => {
181
+ NaiveLayout { exact : false , niches : NaiveNiches :: Maybe , ..NaiveLayout :: EMPTY }
182
+ }
170
183
171
184
ty:: Closure ( _, ref substs) => {
172
185
univariant ( & mut substs. as_closure ( ) . upvar_tys ( ) , & ReprOptions :: default ( ) ) ?
@@ -175,14 +188,20 @@ fn naive_layout_of_uncached<'tcx>(
175
188
ty:: Tuple ( tys) => univariant ( & mut tys. iter ( ) , & ReprOptions :: default ( ) ) ?,
176
189
177
190
ty:: Adt ( def, substs) if def. is_union ( ) => {
191
+ assert_eq ! ( def. variants( ) . len( ) , 1 , "union should have a single variant" ) ;
178
192
let repr = def. repr ( ) ;
179
193
let pack = repr. pack . unwrap_or ( Align :: MAX ) ;
180
194
if repr. pack . is_some ( ) && repr. align . is_some ( ) {
181
195
cx. tcx . sess . delay_span_bug ( DUMMY_SP , "union cannot be packed and aligned" ) ;
182
196
return Err ( error ( cx, LayoutError :: Unknown ( ty) ) ) ;
183
197
}
184
198
185
- let mut layout = NaiveLayout :: EMPTY ;
199
+ let mut layout = NaiveLayout {
200
+ // Unions never have niches.
201
+ niches : NaiveNiches :: None ,
202
+ ..NaiveLayout :: EMPTY
203
+ } ;
204
+
186
205
for f in & def. variants ( ) [ FIRST_VARIANT ] . fields {
187
206
let field = cx. naive_layout_of ( f. ty ( tcx, substs) ) ?;
188
207
layout = layout. union ( & field. packed ( pack) ) ;
@@ -201,24 +220,87 @@ fn naive_layout_of_uncached<'tcx>(
201
220
202
221
ty:: Adt ( def, substs) => {
203
222
let repr = def. repr ( ) ;
204
- let base = NaiveLayout {
205
- // For simplicity, assume that any enum has its discriminant field (if it exists)
206
- // niched inside one of the variants; this will underestimate the size (and sometimes
207
- // alignment) of enums. We also doesn't compute exact alignment for SIMD structs.
208
- // FIXME(reference_niches): Be smarter here.
209
- // Also consider adding a special case for null-optimized enums, so that we can have
210
- // `Option<&T>: PointerLike` in generic contexts.
211
- exact : !def. is_enum ( ) && !repr. simd ( ) ,
223
+ let mut layout = NaiveLayout {
212
224
// An ADT with no inhabited variants should have an uninhabited ABI.
213
225
abi : NaiveAbi :: Uninhabited ,
214
226
..NaiveLayout :: EMPTY
215
227
} ;
216
228
217
- let layout = def. variants ( ) . iter ( ) . try_fold ( base, |layout, v| {
229
+ let mut empty_variants = 0 ;
230
+ for v in def. variants ( ) {
218
231
let mut fields = v. fields . iter ( ) . map ( |f| f. ty ( tcx, substs) ) ;
219
232
let vlayout = univariant ( & mut fields, & repr) ?;
220
- Ok ( layout. union ( & vlayout) )
221
- } ) ?;
233
+
234
+ if vlayout. size == Size :: ZERO && vlayout. exact {
235
+ empty_variants += 1 ;
236
+ } else {
237
+ // Remember the niches of the last seen variant.
238
+ layout. niches = vlayout. niches ;
239
+ }
240
+
241
+ layout = layout. union ( & vlayout) ;
242
+ }
243
+
244
+ if def. is_enum ( ) {
245
+ let may_need_discr = match def. variants ( ) . len ( ) {
246
+ 0 | 1 => false ,
247
+ // Simple Option-like niche optimization.
248
+ // Handling this special case allows enums like `Option<&T>`
249
+ // to be recognized as `PointerLike` and to be transmutable
250
+ // in generic contexts.
251
+ 2 if empty_variants == 1 && layout. niches == NaiveNiches :: Some => {
252
+ layout. niches = NaiveNiches :: Maybe ; // fill up the niche.
253
+ false
254
+ }
255
+ _ => true ,
256
+ } ;
257
+
258
+ if may_need_discr || repr. inhibit_enum_layout_opt ( ) {
259
+ // For simplicity, assume that the discriminant always get niched.
260
+ // This will be wrong in many cases, which will cause the size (and
261
+ // sometimes the alignment) to be underestimated.
262
+ // FIXME(reference_niches): Be smarter here.
263
+ layout. niches = NaiveNiches :: Maybe ;
264
+ layout = layout. inexact ( ) ;
265
+ }
266
+ } else {
267
+ assert_eq ! ( def. variants( ) . len( ) , 1 , "struct should have a single variant" ) ;
268
+
269
+ // We don't compute exact alignment for SIMD structs.
270
+ if repr. simd ( ) {
271
+ layout = layout. inexact ( ) ;
272
+ }
273
+
274
+ // `UnsafeCell` hides all niches.
275
+ if def. is_unsafe_cell ( ) {
276
+ layout. niches = NaiveNiches :: None ;
277
+ }
278
+ }
279
+
280
+ let valid_range = tcx. layout_scalar_valid_range ( def. did ( ) ) ;
281
+ if valid_range != ( Bound :: Unbounded , Bound :: Unbounded ) {
282
+ let get = |bound, default| match bound {
283
+ Bound :: Unbounded => default,
284
+ Bound :: Included ( v) => v,
285
+ Bound :: Excluded ( _) => bug ! ( "exclusive `layout_scalar_valid_range` bound" ) ,
286
+ } ;
287
+
288
+ let valid_range = WrappingRange {
289
+ start : get ( valid_range. 0 , 0 ) ,
290
+ // FIXME: this is wrong for scalar-pair ABIs. Fortunately, the
291
+ // only type this could currently affect is`NonNull<T: !Sized>`,
292
+ // and the `NaiveNiches` result still ends up correct.
293
+ end : get ( valid_range. 1 , layout. size . unsigned_int_max ( ) ) ,
294
+ } ;
295
+ assert ! (
296
+ valid_range. is_in_range_for( layout. size) ,
297
+ "`layout_scalar_valid_range` values are out of bounds" ,
298
+ ) ;
299
+ if !valid_range. is_full_for ( layout. size ) {
300
+ layout. niches = NaiveNiches :: Some ;
301
+ }
302
+ }
303
+
222
304
layout. pad_to_align ( layout. align )
223
305
}
224
306
0 commit comments