@@ -254,6 +254,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
254
254
}
255
255
256
256
/// Find the wrapped inner type of a transparent wrapper.
257
+ /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
257
258
fn unfold_transparent ( & self , layout : TyAndLayout < ' tcx > ) -> TyAndLayout < ' tcx > {
258
259
match layout. ty . kind ( ) {
259
260
ty:: Adt ( adt_def, _) if adt_def. repr ( ) . transparent ( ) => {
@@ -263,11 +264,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
263
264
let field = layout. field ( self , idx) ;
264
265
if field. is_1zst ( ) { None } else { Some ( field) }
265
266
} ) ;
266
- let Some ( first) = non_1zst_fields. next ( ) else {
267
- // All fields are 1-ZST, so this is basically the same as `()`.
268
- // (We still also compare the `PassMode`, so if this target does something strange with 1-ZST there, we'll know.)
269
- return self . layout_of ( self . tcx . types . unit ) . unwrap ( ) ;
270
- } ;
267
+ let first = non_1zst_fields. next ( ) . expect ( "`unfold_transparent` called on 1-ZST" ) ;
271
268
assert ! (
272
269
non_1zst_fields. next( ) . is_none( ) ,
273
270
"more than one non-1-ZST field in a transparent type"
@@ -289,17 +286,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
289
286
caller_layout : TyAndLayout < ' tcx > ,
290
287
callee_layout : TyAndLayout < ' tcx > ,
291
288
) -> bool {
292
- fn primitive_abi_compat ( a1 : abi:: Primitive , a2 : abi:: Primitive ) -> bool {
293
- match ( a1, a2) {
294
- // For integers, ignore the sign.
295
- ( abi:: Primitive :: Int ( int_ty1, _sign1) , abi:: Primitive :: Int ( int_ty2, _sign2) ) => {
296
- int_ty1 == int_ty2
297
- }
298
- // For everything else we require full equality.
299
- _ => a1 == a2,
300
- }
301
- }
302
-
303
289
if caller_layout. ty == callee_layout. ty {
304
290
// Fast path: equal types are definitely compatible.
305
291
return true ;
@@ -308,27 +294,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
308
294
match ( caller_layout. abi , callee_layout. abi ) {
309
295
// If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
310
296
// Different valid ranges are okay (the validity check will complain if this leads to
311
- // invalid transmutes).
297
+ // invalid transmutes). Different signs are *not* okay on some targets (e.g. `extern
298
+ // "C"` on `s390x` where small integers are passed zero/sign-extended in large
299
+ // registers), so we generally reject them to increase portability.
300
+ // NOTE: this is *not* a stable guarantee! It just reflects a property of our current
301
+ // ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
302
+ // when used directly by-value but not considered compatible as a struct field or array
303
+ // element.
312
304
( abi:: Abi :: Scalar ( caller) , abi:: Abi :: Scalar ( callee) ) => {
313
- primitive_abi_compat ( caller. primitive ( ) , callee. primitive ( ) )
305
+ caller. primitive ( ) == callee. primitive ( )
314
306
}
315
307
(
316
308
abi:: Abi :: Vector { element : caller_element, count : caller_count } ,
317
309
abi:: Abi :: Vector { element : callee_element, count : callee_count } ,
318
310
) => {
319
- primitive_abi_compat ( caller_element. primitive ( ) , callee_element. primitive ( ) )
311
+ caller_element. primitive ( ) == callee_element. primitive ( )
320
312
&& caller_count == callee_count
321
313
}
322
314
( abi:: Abi :: ScalarPair ( caller1, caller2) , abi:: Abi :: ScalarPair ( callee1, callee2) ) => {
323
- primitive_abi_compat ( caller1. primitive ( ) , callee1. primitive ( ) )
324
- && primitive_abi_compat ( caller2. primitive ( ) , callee2. primitive ( ) )
315
+ caller1. primitive ( ) == callee1. primitive ( )
316
+ && caller2. primitive ( ) == callee2. primitive ( )
325
317
}
326
318
( abi:: Abi :: Aggregate { .. } , abi:: Abi :: Aggregate { .. } ) => {
327
- // Aggregates are compatible only if they newtype-wrap the same type.
319
+ // Aggregates are compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
320
+ // (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
328
321
// This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
329
322
// which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
330
- self . unfold_transparent ( caller_layout) . ty
331
- == self . unfold_transparent ( callee_layout) . ty
323
+ if caller_layout. is_1zst ( ) || callee_layout. is_1zst ( ) {
324
+ // If either is a 1-ZST, both must be.
325
+ caller_layout. is_1zst ( ) && callee_layout. is_1zst ( )
326
+ } else {
327
+ // Neither is a 1-ZST, so we can check what they are wrapping.
328
+ self . unfold_transparent ( caller_layout) . ty
329
+ == self . unfold_transparent ( callee_layout) . ty
330
+ }
332
331
}
333
332
// What remains is `Abi::Uninhabited` (which can never be passed anyway) and
334
333
// mismatching ABIs, that should all be rejected.
0 commit comments