@@ -624,7 +624,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
624624 AdtKind :: Struct => {
625625 if !def. repr . c ( ) && !def. repr . transparent ( ) {
626626 return FfiUnsafe {
627- ty : ty ,
627+ ty,
628628 reason : "this struct has unspecified layout" ,
629629 help : Some ( "consider adding a `#[repr(C)]` or \
630630 `#[repr(transparent)]` attribute to this struct") ,
@@ -633,7 +633,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
633633
634634 if def. non_enum_variant ( ) . fields . is_empty ( ) {
635635 return FfiUnsafe {
636- ty : ty ,
636+ ty,
637637 reason : "this struct has no fields" ,
638638 help : Some ( "consider adding a member to this struct" ) ,
639639 } ;
@@ -669,7 +669,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
669669 AdtKind :: Union => {
670670 if !def. repr . c ( ) && !def. repr . transparent ( ) {
671671 return FfiUnsafe {
672- ty : ty ,
672+ ty,
673673 reason : "this union has unspecified layout" ,
674674 help : Some ( "consider adding a `#[repr(C)]` or \
675675 `#[repr(transparent)]` attribute to this union") ,
@@ -678,7 +678,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
678678
679679 if def. non_enum_variant ( ) . fields . is_empty ( ) {
680680 return FfiUnsafe {
681- ty : ty ,
681+ ty,
682682 reason : "this union has no fields" ,
683683 help : Some ( "consider adding a field to this union" ) ,
684684 } ;
@@ -721,7 +721,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
721721 // Special-case types like `Option<extern fn()>`.
722722 if !is_repr_nullable_ptr ( cx, ty, def, substs) {
723723 return FfiUnsafe {
724- ty : ty ,
724+ ty,
725725 reason : "enum has no representation hint" ,
726726 help : Some ( "consider adding a `#[repr(C)]`, \
727727 `#[repr(transparent)]`, or integer `#[repr(...)]` \
@@ -750,7 +750,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
750750 }
751751 FfiPhantom ( ..) => {
752752 return FfiUnsafe {
753- ty : ty ,
753+ ty,
754754 reason : "this enum contains a PhantomData field" ,
755755 help : None ,
756756 } ;
@@ -764,13 +764,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
764764 }
765765
766766 ty:: Char => FfiUnsafe {
767- ty : ty ,
767+ ty,
768768 reason : "the `char` type has no C equivalent" ,
769769 help : Some ( "consider using `u32` or `libc::wchar_t` instead" ) ,
770770 } ,
771771
772772 ty:: Int ( ast:: IntTy :: I128 ) | ty:: Uint ( ast:: UintTy :: U128 ) => FfiUnsafe {
773- ty : ty ,
773+ ty,
774774 reason : "128-bit integers don't currently have a known stable ABI" ,
775775 help : None ,
776776 } ,
@@ -779,25 +779,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
779779 ty:: Bool | ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Float ( ..) | ty:: Never => FfiSafe ,
780780
781781 ty:: Slice ( _) => FfiUnsafe {
782- ty : ty ,
782+ ty,
783783 reason : "slices have no C equivalent" ,
784784 help : Some ( "consider using a raw pointer instead" ) ,
785785 } ,
786786
787787 ty:: Dynamic ( ..) => FfiUnsafe {
788- ty : ty ,
788+ ty,
789789 reason : "trait objects have no C equivalent" ,
790790 help : None ,
791791 } ,
792792
793793 ty:: Str => FfiUnsafe {
794- ty : ty ,
794+ ty,
795795 reason : "string slices have no C equivalent" ,
796796 help : Some ( "consider using `*const u8` and a length instead" ) ,
797797 } ,
798798
799799 ty:: Tuple ( ..) => FfiUnsafe {
800- ty : ty ,
800+ ty,
801801 reason : "tuples have unspecified layout" ,
802802 help : Some ( "consider using a struct instead" ) ,
803803 } ,
@@ -811,7 +811,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
811811 match sig. abi ( ) {
812812 Abi :: Rust | Abi :: RustIntrinsic | Abi :: PlatformIntrinsic | Abi :: RustCall => {
813813 return FfiUnsafe {
814- ty : ty ,
814+ ty,
815815 reason : "this function pointer has Rust-specific calling convention" ,
816816 help : Some ( "consider using an `extern fn(...) -> ...` \
817817 function pointer instead") ,
@@ -855,36 +855,87 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
855855 ty:: UnnormalizedProjection ( ..) |
856856 ty:: Projection ( ..) |
857857 ty:: Opaque ( ..) |
858- ty:: FnDef ( ..) => bug ! ( "Unexpected type in foreign function" ) ,
858+ ty:: FnDef ( ..) => bug ! ( "unexpected type in foreign function: {:?}" , ty) ,
859+ }
860+ }
861+
862+ fn emit_ffi_unsafe_type_lint (
863+ & mut self ,
864+ ty : Ty < ' tcx > ,
865+ sp : Span ,
866+ note : & str ,
867+ help : Option < & str > ,
868+ ) {
869+ let mut diag = self . cx . struct_span_lint (
870+ IMPROPER_CTYPES ,
871+ sp,
872+ & format ! ( "`extern` block uses type `{}`, which is not FFI-safe" , ty) ,
873+ ) ;
874+ diag. span_label ( sp, "not FFI-safe" ) ;
875+ if let Some ( help) = help {
876+ diag. help ( help) ;
877+ }
878+ diag. note ( note) ;
879+ if let ty:: Adt ( def, _) = ty. sty {
880+ if let Some ( sp) = self . cx . tcx . hir ( ) . span_if_local ( def. did ) {
881+ diag. span_note ( sp, "type defined here" ) ;
882+ }
883+ }
884+ diag. emit ( ) ;
885+ }
886+
887+ fn check_for_opaque_ty ( & mut self , sp : Span , ty : Ty < ' tcx > ) -> bool {
888+ use crate :: rustc:: ty:: TypeFoldable ;
889+
890+ struct ProhibitOpaqueTypes < ' tcx > {
891+ ty : Option < Ty < ' tcx > > ,
892+ } ;
893+
894+ impl < ' tcx > ty:: fold:: TypeVisitor < ' tcx > for ProhibitOpaqueTypes < ' tcx > {
895+ fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> bool {
896+ if let ty:: Opaque ( ..) = ty. sty {
897+ self . ty = Some ( ty) ;
898+ true
899+ } else {
900+ ty. super_visit_with ( self )
901+ }
902+ }
903+ }
904+
905+ let mut visitor = ProhibitOpaqueTypes { ty : None } ;
906+ ty. visit_with ( & mut visitor) ;
907+ if let Some ( ty) = visitor. ty {
908+ self . emit_ffi_unsafe_type_lint (
909+ ty,
910+ sp,
911+ "opaque types have no C equivalent" ,
912+ None ,
913+ ) ;
914+ true
915+ } else {
916+ false
859917 }
860918 }
861919
862920 fn check_type_for_ffi_and_report_errors ( & mut self , sp : Span , ty : Ty < ' tcx > ) {
921+ // We have to check for opaque types before `normalize_erasing_regions`,
922+ // which will replace opaque types with their underlying concrete type.
923+ if self . check_for_opaque_ty ( sp, ty) {
924+ // We've already emitted an error due to an opaque type.
925+ return ;
926+ }
927+
863928 // it is only OK to use this function because extern fns cannot have
864929 // any generic types right now:
865930 let ty = self . cx . tcx . normalize_erasing_regions ( ParamEnv :: reveal_all ( ) , ty) ;
866931
867932 match self . check_type_for_ffi ( & mut FxHashSet :: default ( ) , ty) {
868933 FfiResult :: FfiSafe => { }
869934 FfiResult :: FfiPhantom ( ty) => {
870- self . cx . span_lint ( IMPROPER_CTYPES ,
871- sp,
872- & format ! ( "`extern` block uses type `{}` which is not FFI-safe: \
873- composed only of PhantomData", ty) ) ;
935+ self . emit_ffi_unsafe_type_lint ( ty, sp, "composed only of `PhantomData`" , None ) ;
874936 }
875- FfiResult :: FfiUnsafe { ty : unsafe_ty, reason, help } => {
876- let msg = format ! ( "`extern` block uses type `{}` which is not FFI-safe: {}" ,
877- unsafe_ty, reason) ;
878- let mut diag = self . cx . struct_span_lint ( IMPROPER_CTYPES , sp, & msg) ;
879- if let Some ( s) = help {
880- diag. help ( s) ;
881- }
882- if let ty:: Adt ( def, _) = unsafe_ty. sty {
883- if let Some ( sp) = self . cx . tcx . hir ( ) . span_if_local ( def. did ) {
884- diag. span_note ( sp, "type defined here" ) ;
885- }
886- }
887- diag. emit ( ) ;
937+ FfiResult :: FfiUnsafe { ty, reason, help } => {
938+ self . emit_ffi_unsafe_type_lint ( ty, sp, reason, help) ;
888939 }
889940 }
890941 }
0 commit comments