@@ -222,13 +222,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
222
222
}
223
223
224
224
/// 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 > ) {
230
226
// 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 ) ) {
232
228
assert ! ( layout. abi. is_uninhabited( ) ) ;
233
229
}
234
230
@@ -237,83 +233,182 @@ fn sanity_check_layout<'tcx>(
237
233
}
238
234
239
235
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 ( ) {
242
276
Abi :: Scalar ( scalar) => {
243
277
// No padding in scalars.
278
+ let size = scalar. size ( cx) ;
279
+ let align = scalar. align ( cx) . abi ;
244
280
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:#?}"
248
284
) ;
249
285
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:#?}"
253
289
) ;
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
+ }
254
335
}
255
336
Abi :: Vector { count, element } => {
256
337
// No padding in vectors. Alignment can be strengthened, though.
257
338
assert ! (
258
- layout. align( ) . abi >= element. align( & tcx ) . abi,
339
+ layout. layout . align( ) . abi >= element. align( cx ) . abi,
259
340
"alignment mismatch between ABI and layout in {layout:#?}"
260
341
) ;
261
- let size = element. size ( & tcx ) * count;
342
+ let size = element. size ( cx ) * count;
262
343
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) ,
265
346
"size mismatch between ABI and layout in {layout:#?}"
266
347
) ;
267
348
}
268
349
Abi :: ScalarPair ( scalar1, scalar2) => {
269
350
// Sanity-check scalar pairs. These are a bit more flexible and support
270
351
// padding, but we can at least ensure both fields actually fit into the layout
271
352
// 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 ;
274
355
assert ! (
275
- layout. align( ) . abi >= cmp:: max( align1, align2) ,
356
+ layout. layout . align( ) . abi >= cmp:: max( align1, align2) ,
276
357
"alignment mismatch between ABI and layout in {layout:#?}" ,
277
358
) ;
278
- let field2_offset = scalar1. size ( & tcx ) . align_to ( align2) ;
359
+ let field2_offset = scalar1. size ( cx ) . align_to ( align2) ;
279
360
assert ! (
280
- layout. size( ) >= field2_offset + scalar2. size( & tcx ) ,
361
+ layout. layout . size( ) >= field2_offset + scalar2. size( cx ) ,
281
362
"size mismatch between ABI and layout in {layout:#?}"
282
363
) ;
283
364
}
284
365
Abi :: Uninhabited | Abi :: Aggregate { .. } => { } // Nothing to check.
285
366
}
286
367
}
287
368
288
- check_layout_abi ( tcx , layout . layout ) ;
369
+ check_layout_abi ( cx , layout) ;
289
370
290
371
if let Variants :: Multiple { variants, .. } = & layout. variants {
291
- for variant in variants {
292
- check_layout_abi ( tcx, * variant) ;
372
+ for variant in variants. iter ( ) {
293
373
// No nested "multiple".
294
374
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.
306
377
if variant. size ( ) > layout. size {
307
378
bug ! (
308
379
"Type with size {} bytes has variant with size {} bytes: {layout:#?}" ,
309
380
layout. size. bytes( ) ,
310
381
variant. size( ) . bytes( ) ,
311
382
)
312
383
}
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
+ }
313
403
// 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
+ } ;
314
407
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
+ }
317
412
( Abi :: Uninhabited , _) => true ,
318
413
( Abi :: Aggregate { .. } , _) => true ,
319
414
_ => false ,
@@ -372,7 +467,7 @@ fn layout_of<'tcx>(
372
467
373
468
cx. record_layout_for_printing ( layout) ;
374
469
375
- sanity_check_layout ( tcx , param_env , & layout) ;
470
+ sanity_check_layout ( & cx , & layout) ;
376
471
377
472
Ok ( layout)
378
473
} )
0 commit comments