@@ -222,13 +222,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
222222}
223223
224224/// Enforce some basic invariants on layouts.
225- fn sanity_check_layout < ' tcx > (
226- tcx : TyCtxt < ' tcx > ,
227- param_env : ty:: ParamEnv < ' tcx > ,
228- layout : & TyAndLayout < ' tcx > ,
229- ) {
225+ fn sanity_check_layout < ' tcx > ( cx : & LayoutCx < ' tcx , TyCtxt < ' tcx > > , layout : & TyAndLayout < ' tcx > ) {
230226 // Type-level uninhabitedness should always imply ABI uninhabitedness.
231- if tcx. conservative_is_privately_uninhabited ( param_env. and ( layout. ty ) ) {
227+ if cx . tcx . conservative_is_privately_uninhabited ( cx . param_env . and ( layout. ty ) ) {
232228 assert ! ( layout. abi. is_uninhabited( ) ) ;
233229 }
234230
@@ -237,83 +233,182 @@ fn sanity_check_layout<'tcx>(
237233 }
238234
239235 if cfg ! ( debug_assertions) {
240- fn check_layout_abi < ' tcx > ( tcx : TyCtxt < ' tcx > , layout : Layout < ' tcx > ) {
241- match layout. abi ( ) {
236+ /// Yields non-1-ZST fields of the type
237+ fn non_zst_fields < ' tcx , ' a > (
238+ cx : & ' a LayoutCx < ' tcx , TyCtxt < ' tcx > > ,
239+ layout : & ' a TyAndLayout < ' tcx > ,
240+ ) -> impl Iterator < Item = ( Size , TyAndLayout < ' tcx > ) > + ' a {
241+ ( 0 ..layout. layout . fields ( ) . count ( ) ) . filter_map ( |i| {
242+ let field = layout. field ( cx, i) ;
243+ let zst = field. is_zst ( ) && field. align . abi . bytes ( ) == 1 ;
244+ ( !zst) . then ( || ( layout. fields . offset ( i) , field) )
245+ } )
246+ }
247+
248+ fn skip_newtypes < ' tcx > (
249+ cx : & LayoutCx < ' tcx , TyCtxt < ' tcx > > ,
250+ layout : & TyAndLayout < ' tcx > ,
251+ ) -> TyAndLayout < ' tcx > {
252+ if matches ! ( layout. layout. variants( ) , Variants :: Multiple { .. } ) {
253+ // Definitely not a newtype of anything.
254+ return * layout;
255+ }
256+ let mut fields = non_zst_fields ( cx, layout) ;
257+ let Some ( first) = fields. next ( ) else {
258+ // No fields here, so this could be a primitive or enum -- either way it's not a newtype around a thing
259+ return * layout
260+ } ;
261+ if fields. next ( ) . is_none ( ) {
262+ let ( offset, first) = first;
263+ if offset == Size :: ZERO && first. layout . size ( ) == layout. size {
264+ // This is a newtype, so keep recursing.
265+ // FIXME(RalfJung): I don't think it would be correct to do any checks for
266+ // alignment here, so we don't. Is that correct?
267+ return skip_newtypes ( cx, & first) ;
268+ }
269+ }
270+ // No more newtypes here.
271+ * layout
272+ }
273+
274+ fn check_layout_abi < ' tcx > ( cx : & LayoutCx < ' tcx , TyCtxt < ' tcx > > , layout : & TyAndLayout < ' tcx > ) {
275+ match layout. layout . abi ( ) {
242276 Abi :: Scalar ( scalar) => {
243277 // No padding in scalars.
278+ let size = scalar. size ( cx) ;
279+ let align = scalar. align ( cx) . abi ;
244280 assert_eq ! (
245- layout. align ( ) . abi ,
246- scalar . align ( & tcx ) . abi ,
247- "alignment mismatch between ABI and layout in {layout:#?}"
281+ layout. layout . size ( ) ,
282+ size ,
283+ "size mismatch between ABI and layout in {layout:#?}"
248284 ) ;
249285 assert_eq ! (
250- layout. size ( ) ,
251- scalar . size ( & tcx ) ,
252- "size mismatch between ABI and layout in {layout:#?}"
286+ layout. layout . align ( ) . abi ,
287+ align ,
288+ "alignment mismatch between ABI and layout in {layout:#?}"
253289 ) ;
290+ // Check that this matches the underlying field.
291+ let inner = skip_newtypes ( cx, layout) ;
292+ assert ! (
293+ matches!( inner. layout. abi( ) , Abi :: Scalar ( _) ) ,
294+ "`Scalar` type {} is newtype around non-`Scalar` type {}" ,
295+ layout. ty,
296+ inner. ty
297+ ) ;
298+ match inner. layout . fields ( ) {
299+ FieldsShape :: Primitive => {
300+ // Fine.
301+ }
302+ FieldsShape :: Arbitrary { .. } => {
303+ // Should be an enum, the only field is the discriminant.
304+ assert ! (
305+ inner. ty. is_enum( ) ,
306+ "`Scalar` layout for non-primitive non-enum type {}" ,
307+ inner. ty
308+ ) ;
309+ assert_eq ! (
310+ inner. layout. fields( ) . count( ) ,
311+ 1 ,
312+ "`Scalar` layout for multiple-field type in {inner:#?}" ,
313+ ) ;
314+ let offset = inner. layout . fields ( ) . offset ( 0 ) ;
315+ let field = inner. field ( cx, 0 ) ;
316+ // The field should be at the right offset, and match the `scalar` layout.
317+ assert_eq ! (
318+ offset,
319+ Size :: ZERO ,
320+ "`Scalar` field at non-0 offset in {inner:#?}" ,
321+ ) ;
322+ assert_eq ! (
323+ field. size, size,
324+ "`Scalar` field with bad size in {inner:#?}" ,
325+ ) ;
326+ assert_eq ! (
327+ field. align. abi, align,
328+ "`Scalar` field with bad align in {inner:#?}" ,
329+ ) ;
330+ }
331+ _ => {
332+ panic ! ( "`Scalar` layout for non-primitive non-enum type {}" , inner. ty) ;
333+ }
334+ }
254335 }
255336 Abi :: Vector { count, element } => {
256337 // No padding in vectors. Alignment can be strengthened, though.
257338 assert ! (
258- layout. align( ) . abi >= element. align( & tcx ) . abi,
339+ layout. layout . align( ) . abi >= element. align( cx ) . abi,
259340 "alignment mismatch between ABI and layout in {layout:#?}"
260341 ) ;
261- let size = element. size ( & tcx ) * count;
342+ let size = element. size ( cx ) * count;
262343 assert_eq ! (
263- layout. size( ) ,
264- size. align_to( tcx . data_layout( ) . vector_align( size) . abi) ,
344+ layout. layout . size( ) ,
345+ size. align_to( cx . data_layout( ) . vector_align( size) . abi) ,
265346 "size mismatch between ABI and layout in {layout:#?}"
266347 ) ;
267348 }
268349 Abi :: ScalarPair ( scalar1, scalar2) => {
269350 // Sanity-check scalar pairs. These are a bit more flexible and support
270351 // padding, but we can at least ensure both fields actually fit into the layout
271352 // and the alignment requirement has not been weakened.
272- let align1 = scalar1. align ( & tcx ) . abi ;
273- let align2 = scalar2. align ( & tcx ) . abi ;
353+ let align1 = scalar1. align ( cx ) . abi ;
354+ let align2 = scalar2. align ( cx ) . abi ;
274355 assert ! (
275- layout. align( ) . abi >= cmp:: max( align1, align2) ,
356+ layout. layout . align( ) . abi >= cmp:: max( align1, align2) ,
276357 "alignment mismatch between ABI and layout in {layout:#?}" ,
277358 ) ;
278- let field2_offset = scalar1. size ( & tcx ) . align_to ( align2) ;
359+ let field2_offset = scalar1. size ( cx ) . align_to ( align2) ;
279360 assert ! (
280- layout. size( ) >= field2_offset + scalar2. size( & tcx ) ,
361+ layout. layout . size( ) >= field2_offset + scalar2. size( cx ) ,
281362 "size mismatch between ABI and layout in {layout:#?}"
282363 ) ;
283364 }
284365 Abi :: Uninhabited | Abi :: Aggregate { .. } => { } // Nothing to check.
285366 }
286367 }
287368
288- check_layout_abi ( tcx , layout . layout ) ;
369+ check_layout_abi ( cx , layout) ;
289370
290371 if let Variants :: Multiple { variants, .. } = & layout. variants {
291- for variant in variants {
292- check_layout_abi ( tcx, * variant) ;
372+ for variant in variants. iter ( ) {
293373 // No nested "multiple".
294374 assert ! ( matches!( variant. variants( ) , Variants :: Single { .. } ) ) ;
295- // Skip empty variants.
296- if variant. size ( ) == Size :: ZERO
297- || variant. fields ( ) . count ( ) == 0
298- || variant. abi ( ) . is_uninhabited ( )
299- {
300- // These are never actually accessed anyway, so we can skip them. (Note that
301- // sometimes, variants with fields have size 0, and sometimes, variants without
302- // fields have non-0 size.)
303- continue ;
304- }
305- // Variants should have the same or a smaller size as the full thing.
375+ // Variants should have the same or a smaller size as the full thing,
376+ // and same for alignment.
306377 if variant. size ( ) > layout. size {
307378 bug ! (
308379 "Type with size {} bytes has variant with size {} bytes: {layout:#?}" ,
309380 layout. size. bytes( ) ,
310381 variant. size( ) . bytes( ) ,
311382 )
312383 }
384+ if variant. align ( ) . abi > layout. align . abi {
385+ bug ! (
386+ "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}" ,
387+ layout. align. abi. bytes( ) ,
388+ variant. align( ) . abi. bytes( ) ,
389+ )
390+ }
391+ // Skip empty variants.
392+ if variant. size ( ) == Size :: ZERO
393+ || variant. fields ( ) . count ( ) == 0
394+ || variant. abi ( ) . is_uninhabited ( )
395+ {
396+ // These are never actually accessed anyway, so we can skip the coherence check
397+ // for them. They also fail that check, since they have
398+ // `Aggregate`/`Uninhbaited` ABI even when the main type is
399+ // `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
400+ // 0, and sometimes, variants without fields have non-0 size.)
401+ continue ;
402+ }
313403 // The top-level ABI and the ABI of the variants should be coherent.
404+ let scalar_coherent = |s1 : Scalar , s2 : Scalar | {
405+ s1. size ( cx) == s2. size ( cx) && s1. align ( cx) == s2. align ( cx)
406+ } ;
314407 let abi_coherent = match ( layout. abi , variant. abi ( ) ) {
315- ( Abi :: Scalar ( ..) , Abi :: Scalar ( ..) ) => true ,
316- ( Abi :: ScalarPair ( ..) , Abi :: ScalarPair ( ..) ) => true ,
408+ ( Abi :: Scalar ( s1) , Abi :: Scalar ( s2) ) => scalar_coherent ( s1, s2) ,
409+ ( Abi :: ScalarPair ( a1, b1) , Abi :: ScalarPair ( a2, b2) ) => {
410+ scalar_coherent ( a1, a2) && scalar_coherent ( b1, b2)
411+ }
317412 ( Abi :: Uninhabited , _) => true ,
318413 ( Abi :: Aggregate { .. } , _) => true ,
319414 _ => false ,
@@ -372,7 +467,7 @@ fn layout_of<'tcx>(
372467
373468 cx. record_layout_for_printing ( layout) ;
374469
375- sanity_check_layout ( tcx , param_env , & layout) ;
470+ sanity_check_layout ( & cx , & layout) ;
376471
377472 Ok ( layout)
378473 } )
0 commit comments