11use rustc_middle:: query:: Providers ;
22use rustc_middle:: ty:: layout:: {
3- IntegerExt , LayoutCx , LayoutError , LayoutOf , NaiveAbi , NaiveLayout , TyAndNaiveLayout ,
3+ IntegerExt , LayoutCx , LayoutError , LayoutOf , NaiveAbi , NaiveLayout , NaiveNiches ,
4+ TyAndNaiveLayout ,
45} ;
56use rustc_middle:: ty:: { self , ReprOptions , Ty , TyCtxt , TypeVisitableExt } ;
6-
77use rustc_span:: DUMMY_SP ;
88use rustc_target:: abi:: * ;
99
10+ use std:: ops:: Bound ;
11+
1012use crate :: layout:: { compute_array_count, ptr_metadata_scalar} ;
1113
1214pub fn provide ( providers : & mut Providers ) {
@@ -61,8 +63,9 @@ fn naive_layout_of_uncached<'tcx>(
6163 let tcx = cx. tcx ;
6264 let dl = cx. data_layout ( ) ;
6365
64- let scalar = |value : Primitive | NaiveLayout {
66+ let scalar = |niched : bool , value : Primitive | NaiveLayout {
6567 abi : NaiveAbi :: Scalar ( value) ,
68+ niches : if niched { NaiveNiches :: Some } else { NaiveNiches :: None } ,
6669 size : value. size ( dl) ,
6770 align : value. align ( dl) . abi ,
6871 exact : true ,
@@ -105,26 +108,30 @@ fn naive_layout_of_uncached<'tcx>(
105108
106109 Ok ( match * ty. kind ( ) {
107110 // 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 ) ) ,
117123
118124 // The never type.
119125 ty:: Never => NaiveLayout { abi : NaiveAbi :: Uninhabited , ..NaiveLayout :: EMPTY } ,
120126
121127 // Potentially-wide pointers.
122128 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 ) ) ;
124130 if let Some ( metadata) = ptr_metadata_scalar ( cx, pointee) ? {
125131 // Effectively a (ptr, meta) tuple.
132+ let meta = scalar ( !metadata. is_always_valid ( dl) , metadata. primitive ( ) ) ;
126133 let l = data_ptr
127- . concat ( & scalar ( metadata . primitive ( ) ) , dl)
134+ . concat ( & meta , dl)
128135 . ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?;
129136 l. pad_to_align ( l. align )
130137 } else {
@@ -134,8 +141,9 @@ fn naive_layout_of_uncached<'tcx>(
134141 }
135142
136143 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) ) ) ?
139147 }
140148
141149 // Arrays and slices.
@@ -149,13 +157,16 @@ fn naive_layout_of_uncached<'tcx>(
149157 . size
150158 . checked_mul ( count, cx)
151159 . ok_or_else ( || error ( cx, LayoutError :: SizeOverflow ( ty) ) ) ?,
160+ niches : if count == 0 { NaiveNiches :: None } else { element. niches } ,
152161 ..* element
153162 }
154163 }
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+ } ,
159170
160171 ty:: FnDef ( ..) => NaiveLayout :: EMPTY ,
161172
@@ -166,7 +177,9 @@ fn naive_layout_of_uncached<'tcx>(
166177
167178 // FIXME(reference_niches): try to actually compute a reasonable layout estimate,
168179 // 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+ }
170183
171184 ty:: Closure ( _, ref substs) => {
172185 univariant ( & mut substs. as_closure ( ) . upvar_tys ( ) , & ReprOptions :: default ( ) ) ?
@@ -175,14 +188,20 @@ fn naive_layout_of_uncached<'tcx>(
175188 ty:: Tuple ( tys) => univariant ( & mut tys. iter ( ) , & ReprOptions :: default ( ) ) ?,
176189
177190 ty:: Adt ( def, substs) if def. is_union ( ) => {
191+ assert_eq ! ( def. variants( ) . len( ) , 1 , "union should have a single variant" ) ;
178192 let repr = def. repr ( ) ;
179193 let pack = repr. pack . unwrap_or ( Align :: MAX ) ;
180194 if repr. pack . is_some ( ) && repr. align . is_some ( ) {
181195 cx. tcx . sess . delay_span_bug ( DUMMY_SP , "union cannot be packed and aligned" ) ;
182196 return Err ( error ( cx, LayoutError :: Unknown ( ty) ) ) ;
183197 }
184198
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+
186205 for f in & def. variants ( ) [ FIRST_VARIANT ] . fields {
187206 let field = cx. naive_layout_of ( f. ty ( tcx, substs) ) ?;
188207 layout = layout. union ( & field. packed ( pack) ) ;
@@ -201,24 +220,87 @@ fn naive_layout_of_uncached<'tcx>(
201220
202221 ty:: Adt ( def, substs) => {
203222 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 {
212224 // An ADT with no inhabited variants should have an uninhabited ABI.
213225 abi : NaiveAbi :: Uninhabited ,
214226 ..NaiveLayout :: EMPTY
215227 } ;
216228
217- let layout = def. variants ( ) . iter ( ) . try_fold ( base, |layout, v| {
229+ let mut empty_variants = 0 ;
230+ for v in def. variants ( ) {
218231 let mut fields = v. fields . iter ( ) . map ( |f| f. ty ( tcx, substs) ) ;
219232 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+
222304 layout. pad_to_align ( layout. align )
223305 }
224306
0 commit comments