@@ -369,6 +369,37 @@ impl VisitorState {
369369 }
370370}
371371
372+ bitflags ! {
373+ /// Data that summarises how an "outer type" surrounds its inner type(s)
374+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
375+ struct OuterTyData : u8 {
376+ /// To show that there is no outer type, the current type is directly used by a `static`
377+ /// variable or a function/FnPtr
378+ const NO_OUTER_TY = 0b01 ;
379+ /// For NO_OUTER_TY cases, show that we are being directly used by a FnPtr specifically
380+ /// FIXME(ctypes): this is only used for "bad behaviour" reproduced for compatibility's sake
381+ const NO_OUTER_TY_FNPTR = 0b10 ;
382+ /// Other cases, because currently no other characteristics are used about outer types
383+ const NONE = 0b00 ;
384+ }
385+ }
386+
387+ impl OuterTyData {
388+ /// Get the proper data for a given outer type.
389+ fn from_ty < ' tcx > ( ty : Ty < ' tcx > ) -> Self {
390+ match ty. kind ( ) {
391+ ty:: FnPtr ( ..) => Self :: NO_OUTER_TY | Self :: NO_OUTER_TY_FNPTR ,
392+ ty:: RawPtr ( ..)
393+ | ty:: Ref ( ..)
394+ | ty:: Adt ( ..)
395+ | ty:: Tuple ( ..)
396+ | ty:: Array ( ..)
397+ | ty:: Slice ( _) => Self :: NONE ,
398+ k @ _ => bug ! ( "unexpected outer type {:?} of kind {:?}" , ty, k) ,
399+ }
400+ }
401+ }
402+
372403/// Visitor used to recursively traverse MIR types and evaluate FFI-safety.
373404/// It uses ``check_*`` methods as entrypoints to be called elsewhere,
374405/// and ``visit_*`` methods to recurse.
@@ -454,7 +485,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
454485 {
455486 FfiSafe
456487 } else {
457- self . visit_type ( state, inner_ty)
488+ self . visit_type ( state, OuterTyData :: from_ty ( ty ) , inner_ty)
458489 }
459490 }
460491 }
@@ -475,7 +506,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
475506 if let Some ( field) = super :: transparent_newtype_field ( self . cx . tcx , variant) {
476507 // Transparent newtypes have at most one non-ZST field which needs to be checked..
477508 let field_ty = get_type_from_field ( self . cx , field, args) ;
478- match self . visit_type ( state, field_ty) {
509+ match self . visit_type ( state, OuterTyData :: from_ty ( ty ) , field_ty) {
479510 FfiUnsafe { ty, .. } if ty. is_unit ( ) => ( ) ,
480511 r => return r,
481512 }
@@ -494,7 +525,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
494525 let mut all_phantom = !variant. fields . is_empty ( ) ;
495526 for field in & variant. fields {
496527 let field_ty = get_type_from_field ( self . cx , field, args) ;
497- all_phantom &= match self . visit_type ( state, field_ty) {
528+ all_phantom &= match self . visit_type ( state, OuterTyData :: from_ty ( ty ) , field_ty) {
498529 FfiSafe => false ,
499530 // `()` fields are FFI-safe!
500531 FfiUnsafe { ty, .. } if ty. is_unit ( ) => false ,
@@ -592,11 +623,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
592623 // Empty enums are okay... although sort of useless.
593624 return FfiSafe ;
594625 }
595- // Check for a repr() attribute to specify the size of the discriminant.
626+ // Check for a repr() attribute to specify the size of the
627+ // discriminant.
596628 if !def. repr ( ) . c ( ) && !def. repr ( ) . transparent ( ) && def. repr ( ) . int . is_none ( ) {
597629 // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
598- if let Some ( ty ) = repr_nullable_ptr ( self . cx . tcx , self . cx . typing_env ( ) , ty) {
599- return self . visit_type ( state, ty ) ;
630+ if let Some ( inner_ty ) = repr_nullable_ptr ( self . cx . tcx , self . cx . typing_env ( ) , ty) {
631+ return self . visit_type ( state, OuterTyData :: from_ty ( ty ) , inner_ty ) ;
600632 }
601633
602634 return FfiUnsafe {
@@ -628,7 +660,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
628660
629661 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
630662 /// representation which can be exported to C code).
631- fn visit_type ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
663+ fn visit_type (
664+ & mut self ,
665+ state : VisitorState ,
666+ outer_ty : OuterTyData ,
667+ ty : Ty < ' tcx > ,
668+ ) -> FfiResult < ' tcx > {
632669 use FfiResult :: * ;
633670
634671 let tcx = self . cx . tcx ;
@@ -672,7 +709,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
672709 // Pattern types are just extra invariants on the type that you need to uphold,
673710 // but only the base type is relevant for being representable in FFI.
674711 // (note: this lint was written when pattern types could only be integers constrained to ranges)
675- ty:: Pat ( pat_ty, _) => self . visit_type ( state, pat_ty) ,
712+ ty:: Pat ( pat_ty, _) => self . visit_type ( state, outer_ty , pat_ty) ,
676713
677714 // types which likely have a stable representation, if the target architecture defines those
678715 // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64
@@ -702,11 +739,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
702739 help : Some ( msg ! ( "consider using `*const u8` and a length instead" ) ) ,
703740 } ,
704741
705- ty:: Tuple ( ..) => FfiUnsafe {
706- ty,
707- reason : msg ! ( "tuples have unspecified layout" ) ,
708- help : Some ( msg ! ( "consider using a struct instead" ) ) ,
709- } ,
742+ ty:: Tuple ( tuple) => {
743+ let empty_and_safe = if tuple. is_empty ( ) {
744+ // C functions can return void
745+ outer_ty. contains ( OuterTyData :: NO_OUTER_TY ) && state. is_in_function_return ( )
746+ } else {
747+ false
748+ } ;
749+
750+ if empty_and_safe {
751+ FfiSafe
752+ } else {
753+ FfiUnsafe {
754+ ty,
755+ reason : msg ! ( "tuples have unspecified layout" ) ,
756+ help : Some ( msg ! ( "consider using a struct instead" ) ) ,
757+ }
758+ }
759+ }
710760
711761 ty:: RawPtr ( ty, _)
712762 if match ty. kind ( ) {
@@ -724,7 +774,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
724774 return self . visit_indirection ( state, ty, inner_ty, IndirectionKind :: Ref ) ;
725775 }
726776
727- ty:: Array ( inner_ty, _) => self . visit_type ( state, inner_ty) ,
777+ ty:: Array ( inner_ty, _) => {
778+ if state. is_in_function ( )
779+ && outer_ty. contains ( OuterTyData :: NO_OUTER_TY )
780+ // FIXME(ctypes): VVV-this-VVV shouldn't be the case
781+ && !outer_ty. contains ( OuterTyData :: NO_OUTER_TY_FNPTR )
782+ {
783+ // C doesn't really support passing arrays by value - the only way to pass an array by value
784+ // is through a struct.
785+ FfiResult :: FfiUnsafe {
786+ ty,
787+ reason : msg ! ( "passing raw arrays by value is not FFI-safe" ) ,
788+ help : Some ( msg ! ( "consider passing a pointer to the array" ) ) ,
789+ }
790+ } else {
791+ // let's allow phantoms to go through,
792+ // since an array of 1-ZSTs is also a 1-ZST
793+ self . visit_type ( state, OuterTyData :: from_ty ( ty) , inner_ty)
794+ }
795+ }
728796
729797 ty:: FnPtr ( sig_tys, hdr) => {
730798 let sig = sig_tys. with ( hdr) ;
@@ -740,18 +808,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
740808
741809 let sig = tcx. instantiate_bound_regions_with_erased ( sig) ;
742810 for arg in sig. inputs ( ) {
743- match self . visit_type ( VisitorState :: ARGUMENT_TY_IN_FNPTR , * arg) {
811+ match self . visit_type (
812+ VisitorState :: ARGUMENT_TY_IN_FNPTR ,
813+ OuterTyData :: from_ty ( ty) ,
814+ * arg,
815+ ) {
744816 FfiSafe => { }
745817 r => return r,
746818 }
747819 }
748820
749821 let ret_ty = sig. output ( ) ;
750- if ret_ty. is_unit ( ) {
751- return FfiSafe ;
752- }
753822
754- self . visit_type ( VisitorState :: RETURN_TY_IN_FNPTR , ret_ty)
823+ self . visit_type ( VisitorState :: RETURN_TY_IN_FNPTR , OuterTyData :: from_ty ( ty ) , ret_ty)
755824 }
756825
757826 ty:: Foreign ( ..) => FfiSafe ,
@@ -819,43 +888,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
819888 } )
820889 }
821890
822- /// Check if the type is array and emit an unsafe type lint.
823- fn check_for_array_ty ( & mut self , ty : Ty < ' tcx > ) -> PartialFfiResult < ' tcx > {
824- if let ty:: Array ( ..) = ty. kind ( ) {
825- Some ( FfiResult :: FfiUnsafe {
826- ty,
827- reason : msg ! ( "passing raw arrays by value is not FFI-safe" ) ,
828- help : Some ( msg ! ( "consider passing a pointer to the array" ) ) ,
829- } )
830- } else {
831- None
832- }
833- }
834-
835- /// Determine the FFI-safety of a single (MIR) type, given the context of how it is used.
836891 fn check_type ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
837892 let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
838893 if let Some ( res) = self . visit_for_opaque_ty ( ty) {
839894 return res;
840895 }
841896
842- // C doesn't really support passing arrays by value - the only way to pass an array by value
843- // is through a struct. So, first test that the top level isn't an array, and then
844- // recursively check the types inside.
845- if state. is_in_function ( ) {
846- if let Some ( res) = self . check_for_array_ty ( ty) {
847- return res;
848- }
849- }
850-
851- // Don't report FFI errors for unit return types. This check exists here, and not in
852- // the caller (where it would make more sense) so that normalization has definitely
853- // happened.
854- if state. is_in_function_return ( ) && ty. is_unit ( ) {
855- return FfiResult :: FfiSafe ;
856- }
857-
858- self . visit_type ( state, ty)
897+ self . visit_type ( state, OuterTyData :: NO_OUTER_TY , ty)
859898 }
860899}
861900
0 commit comments