@@ -1101,6 +1101,32 @@ fn get_nullable_type<'tcx>(
1101
1101
} )
1102
1102
}
1103
1103
1104
+ /// A type is niche-optimization candidate iff:
1105
+ /// - Is a zero-sized type with alignment 1 (a “1-ZST”).
1106
+ /// - Has no fields.
1107
+ /// - Does not have the `#[non_exhaustive]` attribute.
1108
+ fn is_niche_optimization_candidate < ' tcx > (
1109
+ tcx : TyCtxt < ' tcx > ,
1110
+ param_env : ty:: ParamEnv < ' tcx > ,
1111
+ ty : Ty < ' tcx > ,
1112
+ ) -> bool {
1113
+ if tcx. layout_of ( param_env. and ( ty) ) . is_ok_and ( |layout| !layout. is_1zst ( ) ) {
1114
+ return false ;
1115
+ }
1116
+
1117
+ match ty. kind ( ) {
1118
+ ty:: Adt ( ty_def, _) => {
1119
+ let non_exhaustive = ty_def. is_variant_list_non_exhaustive ( ) ;
1120
+ let empty = ( ty_def. is_struct ( ) && ty_def. all_fields ( ) . next ( ) . is_none ( ) )
1121
+ || ( ty_def. is_enum ( ) && ty_def. variants ( ) . is_empty ( ) ) ;
1122
+
1123
+ !non_exhaustive && empty
1124
+ }
1125
+ ty:: Tuple ( tys) => tys. is_empty ( ) ,
1126
+ _ => false ,
1127
+ }
1128
+ }
1129
+
1104
1130
/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
1105
1131
/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
1106
1132
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
@@ -1117,6 +1143,22 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
1117
1143
let field_ty = match & ty_def. variants ( ) . raw [ ..] {
1118
1144
[ var_one, var_two] => match ( & var_one. fields . raw [ ..] , & var_two. fields . raw [ ..] ) {
1119
1145
( [ ] , [ field] ) | ( [ field] , [ ] ) => field. ty ( tcx, args) ,
1146
+ ( [ field1] , [ field2] ) => {
1147
+ if !tcx. features ( ) . result_ffi_guarantees {
1148
+ return None ;
1149
+ }
1150
+
1151
+ let ty1 = field1. ty ( tcx, args) ;
1152
+ let ty2 = field2. ty ( tcx, args) ;
1153
+
1154
+ if is_niche_optimization_candidate ( tcx, param_env, ty1) {
1155
+ ty2
1156
+ } else if is_niche_optimization_candidate ( tcx, param_env, ty2) {
1157
+ ty1
1158
+ } else {
1159
+ return None ;
1160
+ }
1161
+ }
1120
1162
_ => return None ,
1121
1163
} ,
1122
1164
_ => return None ,
@@ -1202,7 +1244,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1202
1244
args : GenericArgsRef < ' tcx > ,
1203
1245
) -> FfiResult < ' tcx > {
1204
1246
use FfiResult :: * ;
1205
-
1206
1247
let transparent_with_all_zst_fields = if def. repr ( ) . transparent ( ) {
1207
1248
if let Some ( field) = transparent_newtype_field ( self . cx . tcx , variant) {
1208
1249
// Transparent newtypes have at most one non-ZST field which needs to be checked..
@@ -1329,27 +1370,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1329
1370
return FfiSafe ;
1330
1371
}
1331
1372
1373
+ if def. is_variant_list_non_exhaustive ( ) && !def. did ( ) . is_local ( ) {
1374
+ return FfiUnsafe {
1375
+ ty,
1376
+ reason : fluent:: lint_improper_ctypes_non_exhaustive,
1377
+ help : None ,
1378
+ } ;
1379
+ }
1380
+
1332
1381
// Check for a repr() attribute to specify the size of the
1333
1382
// discriminant.
1334
1383
if !def. repr ( ) . c ( ) && !def. repr ( ) . transparent ( ) && def. repr ( ) . int . is_none ( )
1335
1384
{
1336
- // Special-case types like `Option<extern fn()>`.
1337
- if repr_nullable_ptr ( self . cx . tcx , self . cx . param_env , ty , self . mode )
1338
- . is_none ( )
1385
+ // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
1386
+ if let Some ( ty ) =
1387
+ repr_nullable_ptr ( self . cx . tcx , self . cx . param_env , ty , self . mode )
1339
1388
{
1340
- return FfiUnsafe {
1341
- ty,
1342
- reason : fluent:: lint_improper_ctypes_enum_repr_reason,
1343
- help : Some ( fluent:: lint_improper_ctypes_enum_repr_help) ,
1344
- } ;
1389
+ return self . check_type_for_ffi ( cache, ty) ;
1345
1390
}
1346
- }
1347
1391
1348
- if def. is_variant_list_non_exhaustive ( ) && !def. did ( ) . is_local ( ) {
1349
1392
return FfiUnsafe {
1350
1393
ty,
1351
- reason : fluent:: lint_improper_ctypes_non_exhaustive ,
1352
- help : None ,
1394
+ reason : fluent:: lint_improper_ctypes_enum_repr_reason ,
1395
+ help : Some ( fluent :: lint_improper_ctypes_enum_repr_help ) ,
1353
1396
} ;
1354
1397
}
1355
1398
0 commit comments