@@ -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,40 +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
- // No question
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
- if caller_abi. layout . is_unsized ( ) || callee_abi. layout . is_unsized ( ) {
276
- // No, no, no. We require the types to *exactly* match for unsized arguments. If
277
- // these are somehow unsized "in a different way" (say, `dyn Trait` vs `[i32]`),
278
- // then who knows what happens.
279
- return 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
280
321
}
281
- // This is tricky. Some ABIs split aggregates up into multiple registers etc, so we have
282
- // to be super careful here. For the scalar ABIs we conveniently already have all the
283
- // newtypes unwrapped etc, so in those cases we can just compare the scalar components.
284
- // Everything else we just reject for now.
285
- match ( caller_abi. layout . abi , callee_abi. layout . abi ) {
286
- // Different valid ranges are okay (the validity check will complain if this leads
287
- // to invalid transmutes).
288
- ( abi:: Abi :: Scalar ( caller) , abi:: Abi :: Scalar ( callee) ) => {
289
- primitive_abi_compat ( caller. primitive ( ) , callee. primitive ( ) )
290
- }
291
- (
292
- abi:: Abi :: ScalarPair ( caller1, caller2) ,
293
- abi:: Abi :: ScalarPair ( callee1, callee2) ,
294
- ) => {
295
- primitive_abi_compat ( caller1. primitive ( ) , callee1. primitive ( ) )
296
- && primitive_abi_compat ( caller2. primitive ( ) , callee2. primitive ( ) )
297
- }
298
- // Be conservative.
299
- _ => false ,
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 ( ) )
300
325
}
301
- } ;
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 {
302
344
// When comparing the PassMode, we have to be smart about comparing the attributes.
303
345
let arg_attr_compat = |a1 : & ArgAttributes , a2 : & ArgAttributes | {
304
346
// There's only one regular attribute that matters for the call ABI: InReg.
@@ -333,23 +375,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
333
375
_ => false ,
334
376
} ;
335
377
336
- // We have to check both. `layout_compat` is needed to reject e.g. `i32` vs `f32`,
337
- // which is not reflected in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`,
338
- // which have the same `abi::Primitive` but different `arg_ext`.
339
- if layout_compat ( ) && mode_compat ( ) {
378
+ // Ideally `PassMode` would capture everything there is about argument passing, but that is
379
+ // not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
380
+ // used. So we need to check that *both* sufficiently agree to ensures the arguments are
381
+ // compatible.
382
+ // For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
383
+ // in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
384
+ // `abi::Primitive` but different `arg_ext`.
385
+ if self . layout_compat ( caller_abi. layout , callee_abi. layout ) && mode_compat ( ) {
340
386
// Something went very wrong if our checks don't even imply that the layout is the same.
341
387
assert ! (
342
388
caller_abi. layout. size == callee_abi. layout. size
343
389
&& caller_abi. layout. align. abi == callee_abi. layout. align. abi
390
+ && caller_abi. layout. is_sized( ) == callee_abi. layout. is_sized( )
344
391
) ;
345
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 ;
346
400
}
347
- trace ! (
348
- "check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
349
- caller_abi,
350
- callee_abi
351
- ) ;
352
- return false ;
353
401
}
354
402
355
403
/// Initialize a single callee argument, checking the types for compatibility.
@@ -379,7 +427,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
379
427
throw_ub_custom ! ( fluent:: const_eval_not_enough_caller_args) ;
380
428
} ;
381
429
// Check compatibility
382
- if !Self :: check_argument_compat ( caller_abi, callee_abi) {
430
+ if !self . check_argument_compat ( caller_abi, callee_abi) {
383
431
let callee_ty = format ! ( "{}" , callee_ty) ;
384
432
let caller_ty = format ! ( "{}" , caller_arg. layout( ) . ty) ;
385
433
throw_ub_custom ! (
@@ -612,7 +660,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
612
660
} ;
613
661
for ( i, field_ty) in fields. iter ( ) . enumerate ( ) {
614
662
let dest = dest. project_deeper (
615
- & [ ProjectionElem :: Field ( FieldIdx :: from_usize ( i) , field_ty) ] ,
663
+ & [ mir:: ProjectionElem :: Field (
664
+ FieldIdx :: from_usize ( i) ,
665
+ field_ty,
666
+ ) ] ,
616
667
* self . tcx ,
617
668
) ;
618
669
let callee_abi = callee_args_abis. next ( ) . unwrap ( ) ;
@@ -649,7 +700,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
649
700
throw_ub_custom ! ( fluent:: const_eval_too_many_caller_args) ;
650
701
}
651
702
// Don't forget to check the return type!
652
- 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 ) {
653
704
let callee_ty = format ! ( "{}" , callee_fn_abi. ret. layout. ty) ;
654
705
let caller_ty = format ! ( "{}" , caller_fn_abi. ret. layout. ty) ;
655
706
throw_ub_custom ! (
0 commit comments