@@ -1054,6 +1054,32 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>
1054
1054
} )
1055
1055
}
1056
1056
1057
+ /// A type is niche_optimization_candiate iff:
1058
+ /// - Is a zero-sized type with alignment 1 (a “1-ZST”).
1059
+ /// - Has no fields.
1060
+ /// - Does not have the #[non_exhaustive] attribute.
1061
+ fn is_niche_optimization_candidate < ' tcx > (
1062
+ tcx : TyCtxt < ' tcx > ,
1063
+ param_env : ty:: ParamEnv < ' tcx > ,
1064
+ ty : Ty < ' tcx > ,
1065
+ ) -> bool {
1066
+ if !tcx. layout_of ( param_env. and ( ty) ) . is_ok_and ( |layout| layout. is_1zst ( ) ) {
1067
+ return false ;
1068
+ }
1069
+
1070
+ match ty. kind ( ) {
1071
+ ty:: Adt ( ty_def, _) => {
1072
+ let non_exhaustive = ty_def. is_variant_list_non_exhaustive ( )
1073
+ || ty_def. variants ( ) . iter ( ) . any ( |variant| variant. is_field_list_non_exhaustive ( ) ) ;
1074
+ let contains_no_fields = ty_def. all_fields ( ) . next ( ) . is_none ( ) ;
1075
+
1076
+ !non_exhaustive && contains_no_fields
1077
+ }
1078
+ ty:: Tuple ( tys) => tys. is_empty ( ) ,
1079
+ _ => false ,
1080
+ }
1081
+ }
1082
+
1057
1083
/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
1058
1084
/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
1059
1085
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
@@ -1070,6 +1096,26 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
1070
1096
let field_ty = match & ty_def. variants ( ) . raw [ ..] {
1071
1097
[ var_one, var_two] => match ( & var_one. fields . raw [ ..] , & var_two. fields . raw [ ..] ) {
1072
1098
( [ ] , [ field] ) | ( [ field] , [ ] ) => field. ty ( tcx, args) ,
1099
+ ( [ field1] , [ field2] ) => {
1100
+ // TODO: We pass all the checks here although individual enum variants has
1101
+ // checks for FFI safety even when niche optimized which needs to be
1102
+ // suppressed. for types like `Result<PhantomData<()>, E>`, PhantomData has
1103
+ // it's own lint for FFI which needs to be suppressed: `composed only of
1104
+ // `PhantomData``. This is true for other custom types as well `struct
1105
+ // Example;` which emits `this struct has unspecified layout` and suggests to
1106
+ // add `#[repr(C)]` and when that is done, linter emits `this struct has no
1107
+ // fields`, all under the `improper_ctypes_definitions` lint group
1108
+ let ty1 = field1. ty ( tcx, args) ;
1109
+ let ty2 = field2. ty ( tcx, args) ;
1110
+
1111
+ if is_niche_optimization_candidate ( tcx, param_env, ty1) {
1112
+ ty2
1113
+ } else if is_niche_optimization_candidate ( tcx, param_env, ty2) {
1114
+ ty1
1115
+ } else {
1116
+ return None ;
1117
+ }
1118
+ }
1073
1119
_ => return None ,
1074
1120
} ,
1075
1121
_ => return None ,
@@ -1286,7 +1332,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1286
1332
// discriminant.
1287
1333
if !def. repr ( ) . c ( ) && !def. repr ( ) . transparent ( ) && def. repr ( ) . int . is_none ( )
1288
1334
{
1289
- // Special-case types like `Option<extern fn()>`.
1335
+ // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
1290
1336
if repr_nullable_ptr ( self . cx . tcx , self . cx . param_env , ty, self . mode )
1291
1337
. is_none ( )
1292
1338
{
0 commit comments