@@ -6,7 +6,6 @@ use rustc_attr as attr;
6
6
use rustc_data_structures:: fx:: FxHashSet ;
7
7
use rustc_errors:: Applicability ;
8
8
use rustc_hir as hir;
9
- use rustc_hir:: def_id:: DefId ;
10
9
use rustc_hir:: { is_range_literal, ExprKind , Node } ;
11
10
use rustc_index:: vec:: Idx ;
12
11
use rustc_middle:: mir:: interpret:: { sign_extend, truncate} ;
@@ -511,10 +510,6 @@ enum FfiResult<'tcx> {
511
510
FfiUnsafe { ty : Ty < ' tcx > , reason : & ' static str , help : Option < & ' static str > } ,
512
511
}
513
512
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
-
518
513
fn ty_is_known_nonnull < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> bool {
519
514
match ty. kind {
520
515
ty:: FnPtr ( _) => true ,
@@ -523,7 +518,7 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
523
518
for field in field_def. all_fields ( ) {
524
519
let field_ty =
525
520
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 ) {
527
522
continue ;
528
523
}
529
524
@@ -653,32 +648,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
653
648
} ;
654
649
}
655
650
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
668
664
}
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
+ }
677
683
}
678
684
}
679
- }
680
685
681
- if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
686
+ if all_phantom { FfiPhantom ( ty) } else { FfiSafe }
687
+ }
682
688
}
683
689
AdtKind :: Union => {
684
690
if !def. repr . c ( ) && !def. repr . transparent ( ) {
@@ -708,7 +714,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
708
714
) ;
709
715
// repr(transparent) types are allowed to have arbitrary ZSTs, not just
710
716
// 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 ) {
712
718
continue ;
713
719
}
714
720
let r = self . check_type_for_ffi ( cache, field_ty) ;
@@ -774,7 +780,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
774
780
) ;
775
781
// repr(transparent) types are allowed to have arbitrary ZSTs, not
776
782
// 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 ) {
778
784
continue ;
779
785
}
780
786
let r = self . check_type_for_ffi ( cache, field_ty) ;
@@ -946,7 +952,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
946
952
}
947
953
}
948
954
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
+ ) {
950
962
// We have to check for opaque types before `normalize_erasing_regions`,
951
963
// which will replace opaque types with their underlying concrete type.
952
964
if self . check_for_opaque_ty ( sp, ty) {
@@ -957,19 +969,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
957
969
// it is only OK to use this function because extern fns cannot have
958
970
// any generic types right now:
959
971
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.
964
976
if !is_static && self . check_for_array_ty ( sp, ty) {
965
977
return ;
966
978
}
967
979
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
+
968
987
match self . check_type_for_ffi ( & mut FxHashSet :: default ( ) , ty) {
969
988
FfiResult :: FfiSafe => { }
970
989
FfiResult :: FfiPhantom ( ty) => {
971
990
self . emit_ffi_unsafe_type_lint ( ty, sp, "composed only of `PhantomData`" , None ) ;
972
991
}
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 ,
973
995
FfiResult :: FfiUnsafe { ty, reason, help } => {
974
996
self . emit_ffi_unsafe_type_lint ( ty, sp, reason, help) ;
975
997
}
@@ -982,21 +1004,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
982
1004
let sig = self . cx . tcx . erase_late_bound_regions ( & sig) ;
983
1005
984
1006
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 ) ;
986
1008
}
987
1009
988
1010
if let hir:: FnRetTy :: Return ( ref ret_hir) = decl. output {
989
1011
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 ) ;
993
1013
}
994
1014
}
995
1015
996
1016
fn check_foreign_static ( & mut self , id : hir:: HirId , span : Span ) {
997
1017
let def_id = self . cx . tcx . hir ( ) . local_def_id ( id) ;
998
1018
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 ) ;
1000
1020
}
1001
1021
}
1002
1022
0 commit comments