@@ -507,7 +507,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
507507enum FfiResult < ' tcx > {
508508 FfiSafe ,
509509 FfiPhantom ( Ty < ' tcx > ) ,
510- FfiUnsafe { ty : Ty < ' tcx > , reason : & ' static str , help : Option < & ' static str > } ,
510+ FfiUnsafe { ty : Ty < ' tcx > , reason : String , help : Option < String > } ,
511511}
512512
513513fn ty_is_known_nonnull < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> bool {
@@ -613,6 +613,50 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
613613 }
614614 }
615615
616+ /// Checks if the given `VariantDef`'s field types are "ffi-safe".
617+ fn check_variant_for_ffi (
618+ & self ,
619+ cache : & mut FxHashSet < Ty < ' tcx > > ,
620+ ty : Ty < ' tcx > ,
621+ def : & ty:: AdtDef ,
622+ variant : & ty:: VariantDef ,
623+ substs : SubstsRef < ' tcx > ,
624+ ) -> FfiResult < ' tcx > {
625+ use FfiResult :: * ;
626+
627+ if def. repr . transparent ( ) {
628+ // Can assume that only one field is not a ZST, so only check
629+ // that field's type for FFI-safety.
630+ if let Some ( field) = variant. transparent_newtype_field ( self . cx . tcx , self . cx . param_env ) {
631+ self . check_field_type_for_ffi ( cache, field, substs)
632+ } else {
633+ bug ! ( "malformed transparent type" ) ;
634+ }
635+ } else {
636+ // We can't completely trust repr(C) markings; make sure the fields are
637+ // actually safe.
638+ let mut all_phantom = !variant. fields . is_empty ( ) ;
639+ for field in & variant. fields {
640+ match self . check_field_type_for_ffi ( cache, & field, substs) {
641+ FfiSafe => {
642+ all_phantom = false ;
643+ }
644+ FfiPhantom ( ..) if def. is_enum ( ) => {
645+ return FfiUnsafe {
646+ ty,
647+ reason : "this enum contains a PhantomData field" . into ( ) ,
648+ help : None ,
649+ } ;
650+ }
651+ FfiPhantom ( ..) => { }
652+ r => return r,
653+ }
654+ }
655+
656+ if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
657+ }
658+ }
659+
616660 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
617661 /// representation which can be exported to C code).
618662 fn check_type_for_ffi ( & self , cache : & mut FxHashSet < Ty < ' tcx > > , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
@@ -634,15 +678,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
634678 return FfiPhantom ( ty) ;
635679 }
636680 match def. adt_kind ( ) {
637- AdtKind :: Struct => {
681+ AdtKind :: Struct | AdtKind :: Union => {
682+ let kind = if def. is_struct ( ) { "struct" } else { "union" } ;
683+
638684 if !def. repr . c ( ) && !def. repr . transparent ( ) {
639685 return FfiUnsafe {
640686 ty,
641- reason : "this struct has unspecified layout" ,
642- help : Some (
687+ reason : format ! ( "this {} has unspecified layout" , kind ) ,
688+ help : Some ( format ! (
643689 "consider adding a `#[repr(C)]` or \
644- `#[repr(transparent)]` attribute to this struct",
645- ) ,
690+ `#[repr(transparent)]` attribute to this {}",
691+ kind
692+ ) ) ,
646693 } ;
647694 }
648695
@@ -651,93 +698,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
651698 if is_non_exhaustive && !def. did . is_local ( ) {
652699 return FfiUnsafe {
653700 ty,
654- reason : "this struct is non-exhaustive" ,
701+ reason : format ! ( "this {} is non-exhaustive" , kind ) ,
655702 help : None ,
656703 } ;
657704 }
658705
659706 if def. non_enum_variant ( ) . fields . is_empty ( ) {
660707 return FfiUnsafe {
661708 ty,
662- reason : "this struct has no fields" ,
663- help : Some ( "consider adding a member to this struct" ) ,
709+ reason : format ! ( "this {} has no fields" , kind ) ,
710+ help : Some ( format ! ( "consider adding a member to this {}" , kind ) ) ,
664711 } ;
665712 }
666713
667- if def. repr . transparent ( ) {
668- // Can assume that only one field is not a ZST, so only check
669- // that field's type for FFI-safety.
670- if let Some ( field) =
671- def. transparent_newtype_field ( cx, self . cx . param_env )
672- {
673- self . check_field_type_for_ffi ( cache, field, substs)
674- } else {
675- FfiSafe
676- }
677- } else {
678- // We can't completely trust repr(C) markings; make sure the fields are
679- // actually safe.
680- let mut all_phantom = true ;
681- for field in & def. non_enum_variant ( ) . fields {
682- let r = self . check_field_type_for_ffi ( cache, field, substs) ;
683- match r {
684- FfiSafe => {
685- all_phantom = false ;
686- }
687- FfiPhantom ( ..) => { }
688- FfiUnsafe { .. } => {
689- return r;
690- }
691- }
692- }
693-
694- if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
695- }
696- }
697- AdtKind :: Union => {
698- if !def. repr . c ( ) && !def. repr . transparent ( ) {
699- return FfiUnsafe {
700- ty,
701- reason : "this union has unspecified layout" ,
702- help : Some (
703- "consider adding a `#[repr(C)]` or \
704- `#[repr(transparent)]` attribute to this union",
705- ) ,
706- } ;
707- }
708-
709- if def. non_enum_variant ( ) . fields . is_empty ( ) {
710- return FfiUnsafe {
711- ty,
712- reason : "this union has no fields" ,
713- help : Some ( "consider adding a field to this union" ) ,
714- } ;
715- }
716-
717- let mut all_phantom = true ;
718- for field in & def. non_enum_variant ( ) . fields {
719- let field_ty = cx. normalize_erasing_regions (
720- ParamEnv :: reveal_all ( ) ,
721- field. ty ( cx, substs) ,
722- ) ;
723- // repr(transparent) types are allowed to have arbitrary ZSTs, not just
724- // PhantomData -- skip checking all ZST fields.
725- if def. repr . transparent ( ) && field_ty. is_zst ( cx, field. did ) {
726- continue ;
727- }
728- let r = self . check_type_for_ffi ( cache, field_ty) ;
729- match r {
730- FfiSafe => {
731- all_phantom = false ;
732- }
733- FfiPhantom ( ..) => { }
734- FfiUnsafe { .. } => {
735- return r;
736- }
737- }
738- }
739-
740- if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
714+ self . check_variant_for_ffi ( cache, ty, def, def. non_enum_variant ( ) , substs)
741715 }
742716 AdtKind :: Enum => {
743717 if def. variants . is_empty ( ) {
@@ -752,11 +726,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
752726 if !is_repr_nullable_ptr ( cx, ty, def, substs) {
753727 return FfiUnsafe {
754728 ty,
755- reason : "enum has no representation hint" ,
729+ reason : "enum has no representation hint" . into ( ) ,
756730 help : Some (
757731 "consider adding a `#[repr(C)]`, \
758732 `#[repr(transparent)]`, or integer `#[repr(...)]` \
759- attribute to this enum",
733+ attribute to this enum"
734+ . into ( ) ,
760735 ) ,
761736 } ;
762737 }
@@ -765,7 +740,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
765740 if def. is_variant_list_non_exhaustive ( ) && !def. did . is_local ( ) {
766741 return FfiUnsafe {
767742 ty,
768- reason : "this enum is non-exhaustive" ,
743+ reason : "this enum is non-exhaustive" . into ( ) ,
769744 help : None ,
770745 } ;
771746 }
@@ -776,51 +751,31 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
776751 if is_non_exhaustive && !variant. def_id . is_local ( ) {
777752 return FfiUnsafe {
778753 ty,
779- reason : "this enum has non-exhaustive variants" ,
754+ reason : "this enum has non-exhaustive variants" . into ( ) ,
780755 help : None ,
781756 } ;
782757 }
783758
784- for field in & variant. fields {
785- let field_ty = cx. normalize_erasing_regions (
786- ParamEnv :: reveal_all ( ) ,
787- field. ty ( cx, substs) ,
788- ) ;
789- // repr(transparent) types are allowed to have arbitrary ZSTs, not
790- // just PhantomData -- skip checking all ZST fields.
791- if def. repr . transparent ( ) && field_ty. is_zst ( cx, field. did ) {
792- continue ;
793- }
794- let r = self . check_type_for_ffi ( cache, field_ty) ;
795- match r {
796- FfiSafe => { }
797- FfiUnsafe { .. } => {
798- return r;
799- }
800- FfiPhantom ( ..) => {
801- return FfiUnsafe {
802- ty,
803- reason : "this enum contains a PhantomData field" ,
804- help : None ,
805- } ;
806- }
807- }
759+ match self . check_variant_for_ffi ( cache, ty, def, variant, substs) {
760+ FfiSafe => ( ) ,
761+ r => return r,
808762 }
809763 }
764+
810765 FfiSafe
811766 }
812767 }
813768 }
814769
815770 ty:: Char => FfiUnsafe {
816771 ty,
817- reason : "the `char` type has no C equivalent" ,
818- help : Some ( "consider using `u32` or `libc::wchar_t` instead" ) ,
772+ reason : "the `char` type has no C equivalent" . into ( ) ,
773+ help : Some ( "consider using `u32` or `libc::wchar_t` instead" . into ( ) ) ,
819774 } ,
820775
821776 ty:: Int ( ast:: IntTy :: I128 ) | ty:: Uint ( ast:: UintTy :: U128 ) => FfiUnsafe {
822777 ty,
823- reason : "128-bit integers don't currently have a known stable ABI" ,
778+ reason : "128-bit integers don't currently have a known stable ABI" . into ( ) ,
824779 help : None ,
825780 } ,
826781
@@ -829,24 +784,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
829784
830785 ty:: Slice ( _) => FfiUnsafe {
831786 ty,
832- reason : "slices have no C equivalent" ,
833- help : Some ( "consider using a raw pointer instead" ) ,
787+ reason : "slices have no C equivalent" . into ( ) ,
788+ help : Some ( "consider using a raw pointer instead" . into ( ) ) ,
834789 } ,
835790
836791 ty:: Dynamic ( ..) => {
837- FfiUnsafe { ty, reason : "trait objects have no C equivalent" , help : None }
792+ FfiUnsafe { ty, reason : "trait objects have no C equivalent" . into ( ) , help : None }
838793 }
839794
840795 ty:: Str => FfiUnsafe {
841796 ty,
842- reason : "string slices have no C equivalent" ,
843- help : Some ( "consider using `*const u8` and a length instead" ) ,
797+ reason : "string slices have no C equivalent" . into ( ) ,
798+ help : Some ( "consider using `*const u8` and a length instead" . into ( ) ) ,
844799 } ,
845800
846801 ty:: Tuple ( ..) => FfiUnsafe {
847802 ty,
848- reason : "tuples have unspecified layout" ,
849- help : Some ( "consider using a struct instead" ) ,
803+ reason : "tuples have unspecified layout" . into ( ) ,
804+ help : Some ( "consider using a struct instead" . into ( ) ) ,
850805 } ,
851806
852807 ty:: RawPtr ( ty:: TypeAndMut { ty, .. } ) | ty:: Ref ( _, ty, _) => {
@@ -860,10 +815,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
860815 Abi :: Rust | Abi :: RustIntrinsic | Abi :: PlatformIntrinsic | Abi :: RustCall => {
861816 return FfiUnsafe {
862817 ty,
863- reason : "this function pointer has Rust-specific calling convention" ,
818+ reason : "this function pointer has Rust-specific calling convention"
819+ . into ( ) ,
864820 help : Some (
865821 "consider using an `extern fn(...) -> ...` \
866- function pointer instead",
822+ function pointer instead"
823+ . into ( ) ,
867824 ) ,
868825 } ;
869826 }
@@ -897,7 +854,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
897854 // While opaque types are checked for earlier, if a projection in a struct field
898855 // normalizes to an opaque type, then it will reach this branch.
899856 ty:: Opaque ( ..) => {
900- FfiUnsafe { ty, reason : "opaque types have no C equivalent" , help : None }
857+ FfiUnsafe { ty, reason : "opaque types have no C equivalent" . into ( ) , help : None }
901858 }
902859
903860 ty:: Param ( ..)
@@ -1004,7 +961,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1004961 // argument, which after substitution, is `()`, then this branch can be hit.
1005962 FfiResult :: FfiUnsafe { ty, .. } if is_return_type && ty. is_unit ( ) => return ,
1006963 FfiResult :: FfiUnsafe { ty, reason, help } => {
1007- self . emit_ffi_unsafe_type_lint ( ty, sp, reason, help) ;
964+ self . emit_ffi_unsafe_type_lint ( ty, sp, & reason, help. as_deref ( ) ) ;
1008965 }
1009966 }
1010967 }
0 commit comments