@@ -6,7 +6,6 @@ use rustc_attr as attr;
66use rustc_data_structures:: fx:: FxHashSet ;
77use rustc_errors:: Applicability ;
88use rustc_hir as hir;
9- use rustc_hir:: def_id:: DefId ;
109use rustc_hir:: { is_range_literal, ExprKind , Node } ;
1110use rustc_index:: vec:: Idx ;
1211use rustc_middle:: mir:: interpret:: { sign_extend, truncate} ;
@@ -511,10 +510,6 @@ enum FfiResult<'tcx> {
511510 FfiUnsafe { ty : Ty < ' tcx > , reason : & ' static str , help : Option < & ' static str > } ,
512511}
513512
514- fn is_zst < ' tcx > ( tcx : TyCtxt < ' tcx > , did : DefId , ty : Ty < ' tcx > ) -> bool {
515- tcx. layout_of ( tcx. param_env ( did) . and ( ty) ) . map ( |layout| layout. is_zst ( ) ) . unwrap_or ( false )
516- }
517-
518513fn ty_is_known_nonnull < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> bool {
519514 match ty. kind {
520515 ty:: FnPtr ( _) => true ,
@@ -523,7 +518,7 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
523518 for field in field_def. all_fields ( ) {
524519 let field_ty =
525520 tcx. normalize_erasing_regions ( ParamEnv :: reveal_all ( ) , field. ty ( tcx, substs) ) ;
526- if is_zst ( tcx, field. did , field_ty ) {
521+ if field_ty . is_zst ( tcx, field. did ) {
527522 continue ;
528523 }
529524
@@ -653,32 +648,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
653648 } ;
654649 }
655650
656- // We can't completely trust repr(C) and repr(transparent) markings;
657- // make sure the fields are actually safe.
658- let mut all_phantom = true ;
659- for field in & def. non_enum_variant ( ) . fields {
660- let field_ty = cx. normalize_erasing_regions (
661- ParamEnv :: reveal_all ( ) ,
662- field. ty ( cx, substs) ,
663- ) ;
664- // repr(transparent) types are allowed to have arbitrary ZSTs, not just
665- // PhantomData -- skip checking all ZST fields
666- if def. repr . transparent ( ) && is_zst ( cx, field. did , field_ty) {
667- continue ;
651+ if def. repr . transparent ( ) {
652+ // Can assume that only one field is not a ZST, so only check
653+ // that field's type for FFI-safety.
654+ if let Some ( field) =
655+ def. transparent_newtype_field ( cx, self . cx . param_env )
656+ {
657+ let field_ty = cx. normalize_erasing_regions (
658+ self . cx . param_env ,
659+ field. ty ( cx, substs) ,
660+ ) ;
661+ self . check_type_for_ffi ( cache, field_ty)
662+ } else {
663+ FfiSafe
668664 }
669- let r = self . check_type_for_ffi ( cache, field_ty) ;
670- match r {
671- FfiSafe => {
672- all_phantom = false ;
673- }
674- FfiPhantom ( ..) => { }
675- FfiUnsafe { .. } => {
676- return r;
665+ } else {
666+ // We can't completely trust repr(C) markings; make sure the fields are
667+ // actually safe.
668+ let mut all_phantom = true ;
669+ for field in & def. non_enum_variant ( ) . fields {
670+ let field_ty = cx. normalize_erasing_regions (
671+ self . cx . param_env ,
672+ field. ty ( cx, substs) ,
673+ ) ;
674+ let r = self . check_type_for_ffi ( cache, field_ty) ;
675+ match r {
676+ FfiSafe => {
677+ all_phantom = false ;
678+ }
679+ FfiPhantom ( ..) => { }
680+ FfiUnsafe { .. } => {
681+ return r;
682+ }
677683 }
678684 }
679- }
680685
681- if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
686+ if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
687+ }
682688 }
683689 AdtKind :: Union => {
684690 if !def. repr . c ( ) && !def. repr . transparent ( ) {
@@ -708,7 +714,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
708714 ) ;
709715 // repr(transparent) types are allowed to have arbitrary ZSTs, not just
710716 // PhantomData -- skip checking all ZST fields.
711- if def. repr . transparent ( ) && is_zst ( cx, field. did , field_ty ) {
717+ if def. repr . transparent ( ) && field_ty . is_zst ( cx, field. did ) {
712718 continue ;
713719 }
714720 let r = self . check_type_for_ffi ( cache, field_ty) ;
@@ -774,7 +780,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
774780 ) ;
775781 // repr(transparent) types are allowed to have arbitrary ZSTs, not
776782 // just PhantomData -- skip checking all ZST fields.
777- if def. repr . transparent ( ) && is_zst ( cx, field. did , field_ty ) {
783+ if def. repr . transparent ( ) && field_ty . is_zst ( cx, field. did ) {
778784 continue ;
779785 }
780786 let r = self . check_type_for_ffi ( cache, field_ty) ;
@@ -946,7 +952,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
946952 }
947953 }
948954
949- fn check_type_for_ffi_and_report_errors ( & mut self , sp : Span , ty : Ty < ' tcx > , is_static : bool ) {
955+ fn check_type_for_ffi_and_report_errors (
956+ & mut self ,
957+ sp : Span ,
958+ ty : Ty < ' tcx > ,
959+ is_static : bool ,
960+ is_return_type : bool ,
961+ ) {
950962 // We have to check for opaque types before `normalize_erasing_regions`,
951963 // which will replace opaque types with their underlying concrete type.
952964 if self . check_for_opaque_ty ( sp, ty) {
@@ -957,19 +969,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
957969 // it is only OK to use this function because extern fns cannot have
958970 // any generic types right now:
959971 let ty = self . cx . tcx . normalize_erasing_regions ( ParamEnv :: reveal_all ( ) , ty) ;
960- // C doesn't really support passing arrays by value.
961- // The only way to pass an array by value is through a struct.
962- // So we first test that the top level isn't an array,
963- // and then recursively check the types inside.
972+
973+ // C doesn't really support passing arrays by value - the only way to pass an array by value
974+ // is through a struct. So, first test that the top level isn't an array, and then
975+ // recursively check the types inside.
964976 if !is_static && self . check_for_array_ty ( sp, ty) {
965977 return ;
966978 }
967979
980+ // Don't report FFI errors for unit return types. This check exists here, and not in
981+ // `check_foreign_fn` (where it would make more sense) so that normalization has definitely
982+ // happened.
983+ if is_return_type && ty. is_unit ( ) {
984+ return ;
985+ }
986+
968987 match self . check_type_for_ffi ( & mut FxHashSet :: default ( ) , ty) {
969988 FfiResult :: FfiSafe => { }
970989 FfiResult :: FfiPhantom ( ty) => {
971990 self . emit_ffi_unsafe_type_lint ( ty, sp, "composed only of `PhantomData`" , None ) ;
972991 }
992+ // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
993+ // argument, which after substitution, is `()`, then this branch can be hit.
994+ FfiResult :: FfiUnsafe { ty, .. } if is_return_type && ty. is_unit ( ) => return ,
973995 FfiResult :: FfiUnsafe { ty, reason, help } => {
974996 self . emit_ffi_unsafe_type_lint ( ty, sp, reason, help) ;
975997 }
@@ -982,21 +1004,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
9821004 let sig = self . cx . tcx . erase_late_bound_regions ( & sig) ;
9831005
9841006 for ( input_ty, input_hir) in sig. inputs ( ) . iter ( ) . zip ( decl. inputs ) {
985- self . check_type_for_ffi_and_report_errors ( input_hir. span , input_ty, false ) ;
1007+ self . check_type_for_ffi_and_report_errors ( input_hir. span , input_ty, false , false ) ;
9861008 }
9871009
9881010 if let hir:: FnRetTy :: Return ( ref ret_hir) = decl. output {
9891011 let ret_ty = sig. output ( ) ;
990- if !ret_ty. is_unit ( ) {
991- self . check_type_for_ffi_and_report_errors ( ret_hir. span , ret_ty, false ) ;
992- }
1012+ self . check_type_for_ffi_and_report_errors ( ret_hir. span , ret_ty, false , true ) ;
9931013 }
9941014 }
9951015
9961016 fn check_foreign_static ( & mut self , id : hir:: HirId , span : Span ) {
9971017 let def_id = self . cx . tcx . hir ( ) . local_def_id ( id) ;
9981018 let ty = self . cx . tcx . type_of ( def_id) ;
999- self . check_type_for_ffi_and_report_errors ( span, ty, true ) ;
1019+ self . check_type_for_ffi_and_report_errors ( span, ty, true , false ) ;
10001020 }
10011021}
10021022
0 commit comments