@@ -2,12 +2,13 @@ use std::borrow::Cow;
22
33use either:: Either ;
44use 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 ;
85use rustc_middle:: {
96 mir,
10- ty:: { self , Ty } ,
7+ ty:: {
8+ self ,
9+ layout:: { FnAbiOf , LayoutOf , TyAndLayout } ,
10+ Instance , Ty ,
11+ } ,
1112} ;
1213use rustc_target:: abi:: call:: { ArgAbi , ArgAttribute , ArgAttributes , FnAbi , PassMode } ;
1314use rustc_target:: abi:: { self , FieldIdx } ;
@@ -252,11 +253,43 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
252253 . collect ( )
253254 }
254255
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 > ,
258291 ) -> 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 {
260293 match ( a1, a2) {
261294 // For integers, ignore the sign.
262295 ( 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> {
265298 // For everything else we require full equality.
266299 _ => a1 == a2,
267300 }
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 ( ) )
274314 }
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
321321 }
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 {
323344 // When comparing the PassMode, we have to be smart about comparing the attributes.
324345 let arg_attr_compat = |a1 : & ArgAttributes , a2 : & ArgAttributes | {
325346 // 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> {
361382 // For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
362383 // in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
363384 // `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+ ) ;
365392 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 ;
366400 }
367- trace ! (
368- "check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
369- caller_abi,
370- callee_abi
371- ) ;
372- return false ;
373401 }
374402
375403 /// 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> {
399427 throw_ub_custom ! ( fluent:: const_eval_not_enough_caller_args) ;
400428 } ;
401429 // Check compatibility
402- if !Self :: check_argument_compat ( caller_abi, callee_abi) {
430+ if !self . check_argument_compat ( caller_abi, callee_abi) {
403431 let callee_ty = format ! ( "{}" , callee_ty) ;
404432 let caller_ty = format ! ( "{}" , caller_arg. layout( ) . ty) ;
405433 throw_ub_custom ! (
@@ -632,7 +660,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
632660 } ;
633661 for ( i, field_ty) in fields. iter ( ) . enumerate ( ) {
634662 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+ ) ] ,
636667 * self . tcx ,
637668 ) ;
638669 let callee_abi = callee_args_abis. next ( ) . unwrap ( ) ;
@@ -669,7 +700,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
669700 throw_ub_custom ! ( fluent:: const_eval_too_many_caller_args) ;
670701 }
671702 // 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 ) {
673704 let callee_ty = format ! ( "{}" , callee_fn_abi. ret. layout. ty) ;
674705 let caller_ty = format ! ( "{}" , caller_fn_abi. ret. layout. ty) ;
675706 throw_ub_custom ! (
0 commit comments