@@ -2,12 +2,13 @@ use std::borrow::Cow;
2
2
3
3
use either:: Either ;
4
4
use rustc_ast:: ast:: InlineAsmOptions ;
5
- use rustc_middle:: mir:: ProjectionElem ;
6
- use rustc_middle:: ty:: layout:: { FnAbiOf , LayoutOf , TyAndLayout } ;
7
- use rustc_middle:: ty:: Instance ;
8
5
use rustc_middle:: {
9
6
mir,
10
- ty:: { self , Ty } ,
7
+ ty:: {
8
+ self ,
9
+ layout:: { FnAbiOf , LayoutOf , TyAndLayout } ,
10
+ Instance , Ty ,
11
+ } ,
11
12
} ;
12
13
use rustc_target:: abi:: call:: { ArgAbi , ArgAttribute , ArgAttributes , FnAbi , PassMode } ;
13
14
use rustc_target:: abi:: { self , FieldIdx } ;
@@ -252,11 +253,43 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
252
253
. collect ( )
253
254
}
254
255
255
- fn check_argument_compat (
256
- caller_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
257
- callee_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
256
+ /// Find the wrapped inner type of a transparent wrapper.
257
+ fn unfold_transparent ( & self , layout : TyAndLayout < ' tcx > ) -> TyAndLayout < ' tcx > {
258
+ match layout. ty . kind ( ) {
259
+ ty:: Adt ( adt_def, _) if adt_def. repr ( ) . transparent ( ) => {
260
+ assert ! ( !adt_def. is_enum( ) ) ;
261
+ // Find the non-1-ZST field.
262
+ let mut non_1zst_fields = ( 0 ..layout. fields . count ( ) ) . filter_map ( |idx| {
263
+ let field = layout. field ( self , idx) ;
264
+ if field. is_1zst ( ) { None } else { Some ( field) }
265
+ } ) ;
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
+ } ;
271
+ assert ! (
272
+ non_1zst_fields. next( ) . is_none( ) ,
273
+ "more than one non-1-ZST field in a transparent type"
274
+ ) ;
275
+
276
+ // Found it!
277
+ self . unfold_transparent ( first)
278
+ }
279
+ // Not a transparent type, no further unfolding.
280
+ _ => layout,
281
+ }
282
+ }
283
+
284
+ /// Check if these two layouts look like they are fn-ABI-compatible.
285
+ /// (We also compare the `PassMode`, so this doesn't have to check everything. But it turns out
286
+ /// that only checking the `PassMode` is insufficient.)
287
+ fn layout_compat (
288
+ & self ,
289
+ caller_layout : TyAndLayout < ' tcx > ,
290
+ callee_layout : TyAndLayout < ' tcx > ,
258
291
) -> bool {
259
- let primitive_abi_compat = | a1 : abi:: Primitive , a2 : abi:: Primitive | -> bool {
292
+ fn primitive_abi_compat ( a1 : abi:: Primitive , a2 : abi:: Primitive ) -> bool {
260
293
match ( a1, a2) {
261
294
// For integers, ignore the sign.
262
295
( abi:: Primitive :: Int ( int_ty1, _sign1) , abi:: Primitive :: Int ( int_ty2, _sign2) ) => {
@@ -265,61 +298,49 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
265
298
// For everything else we require full equality.
266
299
_ => a1 == a2,
267
300
}
268
- } ;
269
- // Heuristic for type comparison.
270
- let layout_compat = || {
271
- if caller_abi. layout . ty == callee_abi. layout . ty {
272
- // Fast path: definitely compatible.
273
- return true ;
301
+ }
302
+
303
+ if caller_layout. ty == callee_layout. ty {
304
+ // Fast path: equal types are definitely compatible.
305
+ return true ;
306
+ }
307
+
308
+ match ( caller_layout. abi , callee_layout. abi ) {
309
+ // If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
310
+ // Different valid ranges are okay (the validity check will complain if this leads to
311
+ // invalid transmutes).
312
+ ( abi:: Abi :: Scalar ( caller) , abi:: Abi :: Scalar ( callee) ) => {
313
+ primitive_abi_compat ( caller. primitive ( ) , callee. primitive ( ) )
274
314
}
275
- // This is tricky. Some ABIs split aggregates up into multiple registers etc, so we have
276
- // to be super careful here. For the scalar ABIs we conveniently already have all the
277
- // newtypes unwrapped etc, so in those cases we can just compare the scalar components.
278
- // Everything else we just reject for now.
279
- match ( caller_abi. layout . abi , callee_abi. layout . abi ) {
280
- // Different valid ranges are okay (the validity check will complain if this leads
281
- // to invalid transmutes).
282
- ( abi:: Abi :: Scalar ( caller) , abi:: Abi :: Scalar ( callee) ) => {
283
- primitive_abi_compat ( caller. primitive ( ) , callee. primitive ( ) )
284
- }
285
- (
286
- abi:: Abi :: Vector { element : caller_element, count : caller_count } ,
287
- abi:: Abi :: Vector { element : callee_element, count : callee_count } ,
288
- ) => {
289
- primitive_abi_compat ( caller_element. primitive ( ) , callee_element. primitive ( ) )
290
- && caller_count == callee_count
291
- }
292
- (
293
- abi:: Abi :: ScalarPair ( caller1, caller2) ,
294
- abi:: Abi :: ScalarPair ( callee1, callee2) ,
295
- ) => {
296
- primitive_abi_compat ( caller1. primitive ( ) , callee1. primitive ( ) )
297
- && primitive_abi_compat ( caller2. primitive ( ) , callee2. primitive ( ) )
298
- }
299
- (
300
- abi:: Abi :: Aggregate { sized : caller_sized } ,
301
- abi:: Abi :: Aggregate { sized : callee_sized } ,
302
- ) => {
303
- // For these we rely on all the information being encoded in the `PassMode`, so
304
- // here we only habe to check in-memory compatibility.
305
- // FIXME: unwrap transparent newtype wrappers instead.
306
- if !caller_sized || !callee_sized {
307
- // No, no, no. We require the types to *exactly* match for unsized arguments. If
308
- // these are somehow unsized "in a different way" (say, `dyn Trait` vs `[i32]`),
309
- // then who knows what happens.
310
- // FIXME: ideally we'd support newtyped around unized types, but that requires ensuring
311
- // that for all values of the metadata, both types will compute the same dynamic size...
312
- // not an easy thing to check.
313
- return false ;
314
- }
315
- caller_abi. layout . size == callee_abi. layout . size
316
- && caller_abi. layout . align . abi == callee_abi. layout . align . abi
317
- }
318
- // What remains is `Abi::Uninhabited` (which can never be passed anyway) and
319
- // mismatching ABIs, that should all be rejected.
320
- _ => false ,
315
+ (
316
+ abi:: Abi :: Vector { element : caller_element, count : caller_count } ,
317
+ abi:: Abi :: Vector { element : callee_element, count : callee_count } ,
318
+ ) => {
319
+ primitive_abi_compat ( caller_element. primitive ( ) , callee_element. primitive ( ) )
320
+ && caller_count == callee_count
321
321
}
322
- } ;
322
+ ( 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 ( ) )
325
+ }
326
+ ( abi:: Abi :: Aggregate { .. } , abi:: Abi :: Aggregate { .. } ) => {
327
+ // Aggregates are compatible only if they newtype-wrap the same type.
328
+ // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode`,
329
+ // 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
332
+ }
333
+ // What remains is `Abi::Uninhabited` (which can never be passed anyway) and
334
+ // mismatching ABIs, that should all be rejected.
335
+ _ => false ,
336
+ }
337
+ }
338
+
339
+ fn check_argument_compat (
340
+ & self ,
341
+ caller_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
342
+ callee_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
343
+ ) -> bool {
323
344
// When comparing the PassMode, we have to be smart about comparing the attributes.
324
345
let arg_attr_compat = |a1 : & ArgAttributes , a2 : & ArgAttributes | {
325
346
// There's only one regular attribute that matters for the call ABI: InReg.
@@ -361,15 +382,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
361
382
// For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
362
383
// in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
363
384
// `abi::Primitive` but different `arg_ext`.
364
- if layout_compat ( ) && mode_compat ( ) {
385
+ if self . layout_compat ( caller_abi. layout , callee_abi. layout ) && mode_compat ( ) {
386
+ // Something went very wrong if our checks don't even imply that the layout is the same.
387
+ assert ! (
388
+ caller_abi. layout. size == callee_abi. layout. size
389
+ && caller_abi. layout. align. abi == callee_abi. layout. align. abi
390
+ && caller_abi. layout. is_sized( ) == callee_abi. layout. is_sized( )
391
+ ) ;
365
392
return true ;
393
+ } else {
394
+ trace ! (
395
+ "check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
396
+ caller_abi,
397
+ callee_abi
398
+ ) ;
399
+ return false ;
366
400
}
367
- trace ! (
368
- "check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
369
- caller_abi,
370
- callee_abi
371
- ) ;
372
- return false ;
373
401
}
374
402
375
403
/// Initialize a single callee argument, checking the types for compatibility.
@@ -399,7 +427,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
399
427
throw_ub_custom ! ( fluent:: const_eval_not_enough_caller_args) ;
400
428
} ;
401
429
// Check compatibility
402
- if !Self :: check_argument_compat ( caller_abi, callee_abi) {
430
+ if !self . check_argument_compat ( caller_abi, callee_abi) {
403
431
let callee_ty = format ! ( "{}" , callee_ty) ;
404
432
let caller_ty = format ! ( "{}" , caller_arg. layout( ) . ty) ;
405
433
throw_ub_custom ! (
@@ -632,7 +660,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
632
660
} ;
633
661
for ( i, field_ty) in fields. iter ( ) . enumerate ( ) {
634
662
let dest = dest. project_deeper (
635
- & [ ProjectionElem :: Field ( FieldIdx :: from_usize ( i) , field_ty) ] ,
663
+ & [ mir:: ProjectionElem :: Field (
664
+ FieldIdx :: from_usize ( i) ,
665
+ field_ty,
666
+ ) ] ,
636
667
* self . tcx ,
637
668
) ;
638
669
let callee_abi = callee_args_abis. next ( ) . unwrap ( ) ;
@@ -669,7 +700,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
669
700
throw_ub_custom ! ( fluent:: const_eval_too_many_caller_args) ;
670
701
}
671
702
// Don't forget to check the return type!
672
- if !Self :: check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) {
703
+ if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) {
673
704
let callee_ty = format ! ( "{}" , callee_fn_abi. ret. layout. ty) ;
674
705
let caller_ty = format ! ( "{}" , caller_fn_abi. ret. layout. ty) ;
675
706
throw_ub_custom ! (
0 commit comments