@@ -15,11 +15,14 @@ use rustc_data_structures::fx::FxHashSet;
15
15
use rustc_errors:: DiagnosticMessage ;
16
16
use rustc_hir as hir;
17
17
use rustc_hir:: { is_range_literal, Expr , ExprKind , Node } ;
18
- use rustc_middle:: ty:: layout:: { IntegerExt , LayoutOf , SizeSkeleton } ;
19
- use rustc_middle:: ty:: subst:: SubstsRef ;
20
18
use rustc_middle:: ty:: {
21
19
self , AdtKind , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt ,
22
20
} ;
21
+ use rustc_middle:: ty:: {
22
+ layout:: { IntegerExt , LayoutOf , SizeSkeleton } ,
23
+ VariantDef ,
24
+ } ;
25
+ use rustc_middle:: ty:: { subst:: SubstsRef , FieldDef } ;
23
26
use rustc_span:: def_id:: LocalDefId ;
24
27
use rustc_span:: source_map;
25
28
use rustc_span:: symbol:: sym;
@@ -770,8 +773,26 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
770
773
debug ! ( "is_repr_nullable_ptr(cx, ty = {:?})" , ty) ;
771
774
if let ty:: Adt ( ty_def, substs) = ty. kind ( ) {
772
775
let field_ty = match & ty_def. variants ( ) . raw [ ..] {
773
- [ var_one, var_two] => match ( & var_one. fields . raw [ ..] , & var_two. fields . raw [ ..] ) {
776
+ [ variant1, variant2] => match ( & variant1. fields . raw [ ..] , & variant2. fields . raw [ ..] ) {
777
+ // If one variant is empty and the other is a single field (e.g. Option<T>)
774
778
( [ ] , [ field] ) | ( [ field] , [ ] ) => field. ty ( cx. tcx , substs) ,
779
+ // If one variant has a ZST and the other has a single field (e.g. Result<T, ()> or Result<(), T>)
780
+ ( [ field1] , [ field2] ) => {
781
+ let is_zst = |field : & FieldDef , variant : & VariantDef | {
782
+ let param_env = cx. tcx . param_env ( variant. def_id ) ;
783
+ let field_ty = field. ty ( cx. tcx , substs) ;
784
+ cx. tcx
785
+ . layout_of ( param_env. and ( field_ty) )
786
+ . map_or ( false , |layout| layout. is_zst ( ) )
787
+ } ;
788
+ if is_zst ( field1, variant1) {
789
+ field2. ty ( cx. tcx , substs)
790
+ } else if is_zst ( field2, variant2) {
791
+ field1. ty ( cx. tcx , substs)
792
+ } else {
793
+ return None ;
794
+ }
795
+ }
775
796
_ => return None ,
776
797
} ,
777
798
_ => return None ,
@@ -832,9 +853,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
832
853
cache : & mut FxHashSet < Ty < ' tcx > > ,
833
854
field : & ty:: FieldDef ,
834
855
substs : SubstsRef < ' tcx > ,
856
+ allow_unit_type : bool ,
835
857
) -> FfiResult < ' tcx > {
836
858
let field_ty = field. ty ( self . cx . tcx , substs) ;
837
- if field_ty. has_opaque_types ( ) {
859
+ if field_ty. is_unit ( ) && allow_unit_type {
860
+ FfiResult :: FfiSafe
861
+ } else if field_ty. has_opaque_types ( ) {
838
862
self . check_type_for_ffi ( cache, field_ty)
839
863
} else {
840
864
let field_ty = self . cx . tcx . normalize_erasing_regions ( self . cx . param_env , field_ty) ;
@@ -850,14 +874,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
850
874
def : ty:: AdtDef < ' tcx > ,
851
875
variant : & ty:: VariantDef ,
852
876
substs : SubstsRef < ' tcx > ,
877
+ allow_unit_type : bool ,
853
878
) -> FfiResult < ' tcx > {
854
879
use FfiResult :: * ;
855
880
856
881
let transparent_safety = def. repr ( ) . transparent ( ) . then ( || {
857
882
// Can assume that at most one field is not a ZST, so only check
858
883
// that field's type for FFI-safety.
859
884
if let Some ( field) = transparent_newtype_field ( self . cx . tcx , variant) {
860
- return self . check_field_type_for_ffi ( cache, field, substs) ;
885
+ return self . check_field_type_for_ffi ( cache, field, substs, allow_unit_type ) ;
861
886
} else {
862
887
// All fields are ZSTs; this means that the type should behave
863
888
// like (), which is FFI-unsafe... except if all fields are PhantomData,
@@ -869,7 +894,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
869
894
// actually safe.
870
895
let mut all_phantom = !variant. fields . is_empty ( ) ;
871
896
for field in & variant. fields {
872
- match self . check_field_type_for_ffi ( cache, & field, substs) {
897
+ match self . check_field_type_for_ffi ( cache, & field, substs, allow_unit_type ) {
873
898
FfiSafe => {
874
899
all_phantom = false ;
875
900
}
@@ -967,25 +992,39 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
967
992
} ;
968
993
}
969
994
970
- self . check_variant_for_ffi ( cache, ty, def, def. non_enum_variant ( ) , substs)
995
+ self . check_variant_for_ffi (
996
+ cache,
997
+ ty,
998
+ def,
999
+ def. non_enum_variant ( ) ,
1000
+ substs,
1001
+ false ,
1002
+ )
971
1003
}
972
1004
AdtKind :: Enum => {
973
1005
if def. variants ( ) . is_empty ( ) {
974
1006
// Empty enums are okay... although sort of useless.
975
1007
return FfiSafe ;
976
1008
}
977
1009
1010
+ // We need to keep track if this enum is a represented as a nullable
1011
+ // pointer (or nonzero integer). This is because when we have an enum
1012
+ // such as Result<(), NonZeroU32>, we allow this to compile since the FFI
1013
+ // representation of this is as a u32
1014
+ let mut is_repr_nullable_ptr = false ;
978
1015
// Check for a repr() attribute to specify the size of the
979
1016
// discriminant.
980
1017
if !def. repr ( ) . c ( ) && !def. repr ( ) . transparent ( ) && def. repr ( ) . int . is_none ( )
981
1018
{
982
- // Special-case types like `Option<extern fn()>`.
1019
+ // Special-case types like `Option<extern fn()>` and `Result<(), NonZeroU32>` .
983
1020
if repr_nullable_ptr ( self . cx , ty, self . mode ) . is_none ( ) {
984
1021
return FfiUnsafe {
985
1022
ty,
986
1023
reason : fluent:: lint_improper_ctypes_enum_repr_reason,
987
1024
help : Some ( fluent:: lint_improper_ctypes_enum_repr_help) ,
988
1025
} ;
1026
+ } else {
1027
+ is_repr_nullable_ptr = true ;
989
1028
}
990
1029
}
991
1030
@@ -1008,7 +1047,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1008
1047
} ;
1009
1048
}
1010
1049
1011
- match self . check_variant_for_ffi ( cache, ty, def, variant, substs) {
1050
+ match self . check_variant_for_ffi (
1051
+ cache,
1052
+ ty,
1053
+ def,
1054
+ variant,
1055
+ substs,
1056
+ is_repr_nullable_ptr,
1057
+ ) {
1012
1058
FfiSafe => ( ) ,
1013
1059
r => return r,
1014
1060
}
0 commit comments