Skip to content

Commit 74d8244

Browse files
committed
Support Result<T, E> across FFI when niche optimization can be used
Allow allow enums like `Result<T, E>` to be used across FFI if the T/E can be niche optimized and the non-niche-optimized type is FFI safe.
1 parent 74acabe commit 74d8244

File tree

1 file changed

+47
-1
lines changed

1 file changed

+47
-1
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,32 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>
10541054
})
10551055
}
10561056

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+
10571083
/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
10581084
/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
10591085
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
@@ -1070,6 +1096,26 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
10701096
let field_ty = match &ty_def.variants().raw[..] {
10711097
[var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
10721098
([], [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+
}
10731119
_ => return None,
10741120
},
10751121
_ => return None,
@@ -1286,7 +1332,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12861332
// discriminant.
12871333
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
12881334
{
1289-
// Special-case types like `Option<extern fn()>`.
1335+
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
12901336
if repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode)
12911337
.is_none()
12921338
{

0 commit comments

Comments
 (0)