@@ -254,6 +254,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
254254 }
255255
256256 /// 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").
257258 fn unfold_transparent ( & self , layout : TyAndLayout < ' tcx > ) -> TyAndLayout < ' tcx > {
258259 match layout. ty . kind ( ) {
259260 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> {
263264 let field = layout. field ( self , idx) ;
264265 if field. is_1zst ( ) { None } else { Some ( field) }
265266 } ) ;
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" ) ;
271268 assert ! (
272269 non_1zst_fields. next( ) . is_none( ) ,
273270 "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> {
289286 caller_layout : TyAndLayout < ' tcx > ,
290287 callee_layout : TyAndLayout < ' tcx > ,
291288 ) -> 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-
303289 if caller_layout. ty == callee_layout. ty {
304290 // Fast path: equal types are definitely compatible.
305291 return true ;
@@ -308,27 +294,40 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
308294 match ( caller_layout. abi , callee_layout. abi ) {
309295 // If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
310296 // 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.
312304 ( abi:: Abi :: Scalar ( caller) , abi:: Abi :: Scalar ( callee) ) => {
313- primitive_abi_compat ( caller. primitive ( ) , callee. primitive ( ) )
305+ caller. primitive ( ) == callee. primitive ( )
314306 }
315307 (
316308 abi:: Abi :: Vector { element : caller_element, count : caller_count } ,
317309 abi:: Abi :: Vector { element : callee_element, count : callee_count } ,
318310 ) => {
319- primitive_abi_compat ( caller_element. primitive ( ) , callee_element. primitive ( ) )
311+ caller_element. primitive ( ) == callee_element. primitive ( )
320312 && caller_count == callee_count
321313 }
322314 ( 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 ( )
325317 }
326318 ( 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)`.)
328321 // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
329322 // 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+ }
332331 }
333332 // What remains is `Abi::Uninhabited` (which can never be passed anyway) and
334333 // mismatching ABIs, that should all be rejected.
0 commit comments