@@ -6,12 +6,16 @@ use rustc_middle::{
66 mir,
77 ty:: {
88 self ,
9- layout:: { FnAbiOf , LayoutOf , TyAndLayout } ,
9+ layout:: { FnAbiOf , IntegerExt , LayoutOf , TyAndLayout } ,
1010 Instance , Ty ,
1111 } ,
1212} ;
13- use rustc_target:: abi:: call:: { ArgAbi , FnAbi , PassMode } ;
14- use rustc_target:: abi:: { self , FieldIdx } ;
13+ use rustc_span:: sym;
14+ use rustc_target:: abi:: FieldIdx ;
15+ use rustc_target:: abi:: {
16+ call:: { ArgAbi , FnAbi , PassMode } ,
17+ Integer ,
18+ } ;
1519use rustc_target:: spec:: abi:: Abi ;
1620
1721use super :: {
@@ -255,6 +259,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
255259
256260 /// Find the wrapped inner type of a transparent wrapper.
257261 /// Must not be called on 1-ZST (as they don't have a uniquely defined "wrapped field").
262+ ///
263+ /// We work with `TyAndLayout` here since that makes it much easier to iterate over all fields.
258264 fn unfold_transparent ( & self , layout : TyAndLayout < ' tcx > ) -> TyAndLayout < ' tcx > {
259265 match layout. ty . kind ( ) {
260266 ty:: Adt ( adt_def, _) if adt_def. repr ( ) . transparent ( ) => {
@@ -278,70 +284,124 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
278284 }
279285 }
280286
287+ /// Unwrap types that are guaranteed a null-pointer-optimization
288+ fn unfold_npo ( & self , ty : Ty < ' tcx > ) -> InterpResult < ' tcx , Ty < ' tcx > > {
289+ // Check if this is `Option` wrapping some type.
290+ let inner_ty = match ty. kind ( ) {
291+ ty:: Adt ( def, args) if self . tcx . is_diagnostic_item ( sym:: Option , def. did ( ) ) => {
292+ args[ 0 ] . as_type ( ) . unwrap ( )
293+ }
294+ _ => {
295+ // Not an `Option`.
296+ return Ok ( ty) ;
297+ }
298+ } ;
299+ // Check if the inner type is one of the NPO-guaranteed ones.
300+ Ok ( match inner_ty. kind ( ) {
301+ ty:: Ref ( ..) => {
302+ // Option<&T> behaves like &T
303+ inner_ty
304+ }
305+ ty:: Adt ( def, _)
306+ if self . tcx . has_attr ( def. did ( ) , sym:: rustc_nonnull_optimization_guaranteed) =>
307+ {
308+ // For non-null-guaranteed structs, unwrap newtypes.
309+ self . unfold_transparent ( self . layout_of ( inner_ty) ?) . ty
310+ }
311+ _ => {
312+ // Everything else we do not unfold.
313+ ty
314+ }
315+ } )
316+ }
317+
281318 /// Check if these two layouts look like they are fn-ABI-compatible.
282319 /// (We also compare the `PassMode`, so this doesn't have to check everything. But it turns out
283320 /// that only checking the `PassMode` is insufficient.)
284321 fn layout_compat (
285322 & self ,
286323 caller_layout : TyAndLayout < ' tcx > ,
287324 callee_layout : TyAndLayout < ' tcx > ,
288- ) -> bool {
325+ ) -> InterpResult < ' tcx , bool > {
326+ // Fast path: equal types are definitely compatible.
289327 if caller_layout. ty == callee_layout. ty {
290- // Fast path: equal types are definitely compatible.
291- return true ;
328+ return Ok ( true ) ;
292329 }
293-
294- match caller_layout. abi {
295- // For Scalar/Vector/ScalarPair ABI, we directly compare them.
296- // NOTE: this is *not* a stable guarantee! It just reflects a property of our current
297- // ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
298- // when used directly by-value but not considered compatible as a struct field or array
299- // element.
300- abi :: Abi :: Scalar ( .. ) | abi :: Abi :: ScalarPair ( .. ) | abi :: Abi :: Vector { .. } => {
301- caller_layout . abi . eq_up_to_validity ( & callee_layout . abi )
302- }
303- _ => {
304- // Everything else is compatible only if they newtype-wrap the same type, or if they are both 1-ZST .
305- // (The latter part is needed to ensure e.g. that `struct Zst` is compatible with `struct Wrap((), Zst)`.)
306- // This is conservative, but also means that our check isn't quite so heavily dependent on the `PassMode` ,
307- // which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
308- if caller_layout . is_1zst ( ) || callee_layout . is_1zst ( ) {
309- // If either is a 1-ZST, both must be .
310- caller_layout . is_1zst ( ) && callee_layout . is_1zst ( )
311- } else {
312- // Neither is a 1-ZST, so we can check what they are wrapping.
313- self . unfold_transparent ( caller_layout ) . ty
314- == self . unfold_transparent ( callee_layout ) . ty
330+ // 1-ZST are compatible with all 1-ZST (and with nothing else).
331+ if caller_layout. is_1zst ( ) || callee_layout . is_1zst ( ) {
332+ return Ok ( caller_layout . is_1zst ( ) && callee_layout . is_1zst ( ) ) ;
333+ }
334+ // Unfold newtypes and NPO optimizations.
335+ let caller_ty = self . unfold_npo ( self . unfold_transparent ( caller_layout ) . ty ) ? ;
336+ let callee_ty = self . unfold_npo ( self . unfold_transparent ( callee_layout ) . ty ) ? ;
337+ // Now see if these inner types are compatible.
338+
339+ // Compatible pointer types.
340+ let pointee_ty = | ty : Ty < ' tcx > | {
341+ // We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>` .
342+ Some ( match ty . kind ( ) {
343+ ty :: Ref ( _ , ty , _ ) => * ty ,
344+ ty :: RawPtr ( mt ) => mt . ty ,
345+ // We should only accept `Box` with the default allocator.
346+ // It's hard to test for that though so we accept every 1-ZST allocator .
347+ ty :: Adt ( def , args )
348+ if def . is_box ( )
349+ && self . layout_of ( args [ 1 ] . expect_ty ( ) ) . is_ok_and ( |l| l . is_1zst ( ) ) =>
350+ {
351+ args [ 0 ] . expect_ty ( )
315352 }
316- }
353+ _ => return None ,
354+ } )
355+ } ;
356+ if let ( Some ( left) , Some ( right) ) = ( pointee_ty ( caller_ty) , pointee_ty ( callee_ty) ) {
357+ // This is okay if they have the same metadata type.
358+ let meta_ty = |ty : Ty < ' tcx > | {
359+ let ( meta, only_if_sized) = ty. ptr_metadata_ty ( * self . tcx , |ty| ty) ;
360+ assert ! (
361+ !only_if_sized,
362+ "there should be no more 'maybe has that metadata' types during interpretation"
363+ ) ;
364+ meta
365+ } ;
366+ return Ok ( meta_ty ( left) == meta_ty ( right) ) ;
317367 }
368+
369+ // Compatible integer types (in particular, usize vs ptr-sized-u32/u64).
370+ let int_ty = |ty : Ty < ' tcx > | {
371+ Some ( match ty. kind ( ) {
372+ ty:: Int ( ity) => ( Integer :: from_int_ty ( & self . tcx , * ity) , /* signed */ true ) ,
373+ ty:: Uint ( uty) => ( Integer :: from_uint_ty ( & self . tcx , * uty) , /* signed */ false ) ,
374+ _ => return None ,
375+ } )
376+ } ;
377+ if let ( Some ( left) , Some ( right) ) = ( int_ty ( caller_ty) , int_ty ( callee_ty) ) {
378+ // This is okay if they are the same integer type.
379+ return Ok ( left == right) ;
380+ }
381+
382+ // Fall back to exact equality.
383+ // FIXME: We are missing the rules for "repr(C) wrapping compatible types".
384+ Ok ( caller_ty == callee_ty)
318385 }
319386
320387 fn check_argument_compat (
321388 & self ,
322389 caller_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
323390 callee_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
324- ) -> bool {
325- // Ideally `PassMode` would capture everything there is about argument passing, but that is
326- // not the case: in `FnAbi::llvm_type`, also parts of the layout and type information are
327- // used. So we need to check that *both* sufficiently agree to ensures the arguments are
328- // compatible.
329- // For instance, `layout_compat` is needed to reject `i32` vs `f32`, which is not reflected
330- // in `PassMode`. `mode_compat` is needed to reject `u8` vs `bool`, which have the same
331- // `abi::Primitive` but different `arg_ext`.
332- if self . layout_compat ( caller_abi. layout , callee_abi. layout )
333- && caller_abi. mode . eq_abi ( & callee_abi. mode )
334- {
335- // Something went very wrong if our checks don't imply layout ABI compatibility.
336- assert ! ( caller_abi. layout. eq_abi( & callee_abi. layout) ) ;
337- return true ;
391+ ) -> InterpResult < ' tcx , bool > {
392+ // We do not want to accept things as ABI-compatible that just "happen to be" compatible on the current target,
393+ // so we implement a type-based check that reflects the guaranteed rules for ABI compatibility.
394+ if self . layout_compat ( caller_abi. layout , callee_abi. layout ) ? {
395+ // Ensure that our checks imply actual ABI compatibility for this concrete call.
396+ assert ! ( caller_abi. eq_abi( & callee_abi) ) ;
397+ return Ok ( true ) ;
338398 } else {
339399 trace ! (
340400 "check_argument_compat: incompatible ABIs:\n caller: {:?}\n callee: {:?}" ,
341401 caller_abi,
342402 callee_abi
343403 ) ;
344- return false ;
404+ return Ok ( false ) ;
345405 }
346406 }
347407
@@ -360,6 +420,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
360420 ' tcx : ' x ,
361421 ' tcx : ' y ,
362422 {
423+ assert_eq ! ( callee_ty, callee_abi. layout. ty) ;
363424 if matches ! ( callee_abi. mode, PassMode :: Ignore ) {
364425 // This one is skipped. Still must be made live though!
365426 if !already_live {
@@ -371,8 +432,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
371432 let Some ( ( caller_arg, caller_abi) ) = caller_args. next ( ) else {
372433 throw_ub_custom ! ( fluent:: const_eval_not_enough_caller_args) ;
373434 } ;
435+ assert_eq ! ( caller_arg. layout( ) . layout, caller_abi. layout. layout) ;
436+ // Sadly we cannot assert that `caller_arg.layout().ty` and `caller_abi.layout.ty` are
437+ // equal; in closures the types sometimes differ. We just hope that `caller_abi` is the
438+ // right type to print to the user.
439+
374440 // Check compatibility
375- if !self . check_argument_compat ( caller_abi, callee_abi) {
441+ if !self . check_argument_compat ( caller_abi, callee_abi) ? {
376442 let callee_ty = format ! ( "{}" , callee_ty) ;
377443 let caller_ty = format ! ( "{}" , caller_arg. layout( ) . ty) ;
378444 throw_ub_custom ! (
@@ -583,7 +649,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
583649 // taking into account the `spread_arg`. If we could write
584650 // this is a single iterator (that handles `spread_arg`), then
585651 // `pass_argument` would be the loop body. It takes care to
586- // not advance `caller_iter` for ZSTs .
652+ // not advance `caller_iter` for ignored arguments .
587653 let mut callee_args_abis = callee_fn_abi. args . iter ( ) ;
588654 for local in body. args_iter ( ) {
589655 // Construct the destination place for this argument. At this point all
@@ -645,7 +711,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
645711 throw_ub_custom ! ( fluent:: const_eval_too_many_caller_args) ;
646712 }
647713 // Don't forget to check the return type!
648- if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) {
714+ if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) ? {
649715 let callee_ty = format ! ( "{}" , callee_fn_abi. ret. layout. ty) ;
650716 let caller_ty = format ! ( "{}" , caller_fn_abi. ret. layout. ty) ;
651717 throw_ub_custom ! (
@@ -674,7 +740,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
674740 Ok ( ( ) ) => Ok ( ( ) ) ,
675741 }
676742 }
677- // cannot use the shim here, because that will only result in infinite recursion
743+ // `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be
744+ // codegen'd / interpreted as virtual calls through the vtable.
678745 ty:: InstanceDef :: Virtual ( def_id, idx) => {
679746 let mut args = args. to_vec ( ) ;
680747 // We have to implement all "object safe receivers". So we have to go search for a
@@ -798,18 +865,26 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
798865 }
799866
800867 // Adjust receiver argument. Layout can be any (thin) ptr.
868+ let receiver_ty = Ty :: new_mut_ptr ( self . tcx . tcx , dyn_ty) ;
801869 args[ 0 ] = FnArg :: Copy (
802870 ImmTy :: from_immediate (
803871 Scalar :: from_maybe_pointer ( adjusted_receiver, self ) . into ( ) ,
804- self . layout_of ( Ty :: new_mut_ptr ( self . tcx . tcx , dyn_ty ) ) ?,
872+ self . layout_of ( receiver_ty ) ?,
805873 )
806874 . into ( ) ,
807875 ) ;
808876 trace ! ( "Patched receiver operand to {:#?}" , args[ 0 ] ) ;
877+ // Need to also adjust the type in the ABI. Strangely, the layout there is actually
878+ // already fine! Just the type is bogus. This is due to what `force_thin_self_ptr`
879+ // does in `fn_abi_new_uncached`; supposedly, codegen relies on having the bogus
880+ // type, so we just patch this up locally.
881+ let mut caller_fn_abi = caller_fn_abi. clone ( ) ;
882+ caller_fn_abi. args [ 0 ] . layout . ty = receiver_ty;
883+
809884 // recurse with concrete function
810885 self . eval_fn_call (
811886 FnVal :: Instance ( fn_inst) ,
812- ( caller_abi, caller_fn_abi) ,
887+ ( caller_abi, & caller_fn_abi) ,
813888 & args,
814889 with_caller_location,
815890 destination,
0 commit comments