@@ -7,7 +7,7 @@ use libffi::low::CodePtr;
77use libffi:: middle:: Type as FfiType ;
88use rustc_abi:: { HasDataLayout , Size } ;
99use rustc_data_structures:: either;
10- use rustc_middle:: ty:: layout:: HasTypingEnv ;
10+ use rustc_middle:: ty:: layout:: TyAndLayout ;
1111use rustc_middle:: ty:: { self , IntTy , Ty , UintTy } ;
1212use rustc_span:: Symbol ;
1313use serde:: { Deserialize , Serialize } ;
@@ -278,7 +278,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
278278
279279 // This should go first so that we emit unsupported before doing a bunch
280280 // of extra work for types that aren't supported yet.
281- let ty = this. ty_to_ffitype ( v. layout . ty ) ?;
281+ let ty = this. ty_to_ffitype ( v. layout ) ?;
282282
283283 // Helper to print a warning when a pointer is shared with the native code.
284284 let expose = |prov : Provenance | -> InterpResult < ' tcx > {
@@ -387,34 +387,44 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
387387 let this = self . eval_context_ref ( ) ;
388388 let mut fields = vec ! [ ] ;
389389 for field in & adt_def. non_enum_variant ( ) . fields {
390- fields. push ( this. ty_to_ffitype ( field. ty ( * this. tcx , args) ) ?) ;
390+ let layout = this. layout_of ( field. ty ( * this. tcx , args) ) ?;
391+ fields. push ( this. ty_to_ffitype ( layout) ?) ;
391392 }
392393
393394 interp_ok ( FfiType :: structure ( fields) )
394395 }
395396
396397 /// Gets the matching libffi type for a given Ty.
397- fn ty_to_ffitype ( & self , ty : Ty < ' tcx > ) -> InterpResult < ' tcx , FfiType > {
398- let this = self . eval_context_ref ( ) ;
399- interp_ok ( match ty. kind ( ) {
400- ty:: Int ( IntTy :: I8 ) => FfiType :: i8 ( ) ,
401- ty:: Int ( IntTy :: I16 ) => FfiType :: i16 ( ) ,
402- ty:: Int ( IntTy :: I32 ) => FfiType :: i32 ( ) ,
403- ty:: Int ( IntTy :: I64 ) => FfiType :: i64 ( ) ,
404- ty:: Int ( IntTy :: Isize ) => FfiType :: isize ( ) ,
405- ty:: Uint ( UintTy :: U8 ) => FfiType :: u8 ( ) ,
406- ty:: Uint ( UintTy :: U16 ) => FfiType :: u16 ( ) ,
407- ty:: Uint ( UintTy :: U32 ) => FfiType :: u32 ( ) ,
408- ty:: Uint ( UintTy :: U64 ) => FfiType :: u64 ( ) ,
409- ty:: Uint ( UintTy :: Usize ) => FfiType :: usize ( ) ,
410- ty:: RawPtr ( pointee_ty, _mut) => {
411- if !pointee_ty. is_sized ( * this. tcx , this. typing_env ( ) ) {
412- throw_unsup_format ! ( "passing a pointer to an unsized type over FFI: {}" , ty) ;
413- }
414- FfiType :: pointer ( )
415- }
416- ty:: Adt ( adt_def, args) => self . adt_to_ffitype ( ty, * adt_def, args) ?,
417- _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , ty) ,
398+ fn ty_to_ffitype ( & self , layout : TyAndLayout < ' tcx > ) -> InterpResult < ' tcx , FfiType > {
399+ use rustc_abi:: { AddressSpace , BackendRepr , Integer , Primitive } ;
400+
401+ // `BackendRepr::Scalar` is also a signal to pass this type as a scalar in the ABI. This
402+ // matches what codegen does. This does mean that we support some types whose ABI is not
403+ // stable, but that's fine -- we are anyway quite conservative in native-lib mode.
404+ if let BackendRepr :: Scalar ( s) = layout. backend_repr {
405+ // Simple sanity-check: this cannot be `repr(C)`.
406+ assert ! ( !layout. ty. ty_adt_def( ) . is_some_and( |adt| adt. repr( ) . c( ) ) ) ;
407+ return interp_ok ( match s. primitive ( ) {
408+ Primitive :: Int ( Integer :: I8 , /* signed */ true ) => FfiType :: i8 ( ) ,
409+ Primitive :: Int ( Integer :: I16 , /* signed */ true ) => FfiType :: i16 ( ) ,
410+ Primitive :: Int ( Integer :: I32 , /* signed */ true ) => FfiType :: i32 ( ) ,
411+ Primitive :: Int ( Integer :: I64 , /* signed */ true ) => FfiType :: i64 ( ) ,
412+ Primitive :: Int ( Integer :: I8 , /* signed */ false ) => FfiType :: u8 ( ) ,
413+ Primitive :: Int ( Integer :: I16 , /* signed */ false ) => FfiType :: u16 ( ) ,
414+ Primitive :: Int ( Integer :: I32 , /* signed */ false ) => FfiType :: u32 ( ) ,
415+ Primitive :: Int ( Integer :: I64 , /* signed */ false ) => FfiType :: u64 ( ) ,
416+ Primitive :: Pointer ( AddressSpace :: ZERO ) => FfiType :: pointer ( ) ,
417+ _ =>
418+ throw_unsup_format ! (
419+ "unsupported scalar argument type for native call: {}" ,
420+ layout. ty
421+ ) ,
422+ } ) ;
423+ }
424+ interp_ok ( match layout. ty . kind ( ) {
425+ // Scalar types have already been handled above.
426+ ty:: Adt ( adt_def, args) => self . adt_to_ffitype ( layout. ty , * adt_def, args) ?,
427+ _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , layout. ty) ,
418428 } )
419429 }
420430}
@@ -455,6 +465,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
455465 // pointer was passed as argument). Uninitialised memory is left as-is, but any data
456466 // exposed this way is garbage anyway.
457467 this. visit_reachable_allocs ( this. exposed_allocs ( ) , |this, alloc_id, info| {
468+ if matches ! ( info. kind, AllocKind :: Function ) {
469+ static DEDUP : AtomicBool = AtomicBool :: new ( false ) ;
470+ if !DEDUP . swap ( true , std:: sync:: atomic:: Ordering :: Relaxed ) {
471+ // Newly set, so first time we get here.
472+ this. emit_diagnostic ( NonHaltingDiagnostic :: NativeCallFnPtr ) ;
473+ }
474+ }
458475 // If there is no data behind this pointer, skip this.
459476 if !matches ! ( info. kind, AllocKind :: LiveData ) {
460477 return interp_ok ( ( ) ) ;
0 commit comments