Auto merge of #124747 - MasterAwesome:master, r=davidtwco
Support Result<T, E> across FFI when niche optimization can be used (v2) This PR is identical to #122253, which was approved and merged but then removed from master by a force-push due to a [CI bug](https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra/topic/ci.20broken.3F). r? ghost Original PR description: --- 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. Implementation of https://github.com/rust-lang/rfcs/pull/3391 Tracking issue: https://github.com/rust-lang/rust/issues/110503 Additional ABI and codegen tests were added in https://github.com/rust-lang/rust/pull/115372
This commit is contained in:
commit
80420a693f
8 changed files with 830 additions and 77 deletions
|
@ -1101,6 +1101,32 @@ fn get_nullable_type<'tcx>(
|
|||
})
|
||||
}
|
||||
|
||||
/// A type is niche-optimization candidate iff:
|
||||
/// - Is a zero-sized type with alignment 1 (a “1-ZST”).
|
||||
/// - Has no fields.
|
||||
/// - Does not have the `#[non_exhaustive]` attribute.
|
||||
fn is_niche_optimization_candidate<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| !layout.is_1zst()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
ty::Adt(ty_def, _) => {
|
||||
let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
|
||||
let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none())
|
||||
|| (ty_def.is_enum() && ty_def.variants().is_empty());
|
||||
|
||||
!non_exhaustive && empty
|
||||
}
|
||||
ty::Tuple(tys) => tys.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
|
||||
/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
|
||||
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
|
||||
|
@ -1117,6 +1143,22 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
|
|||
let field_ty = match &ty_def.variants().raw[..] {
|
||||
[var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
|
||||
([], [field]) | ([field], []) => field.ty(tcx, args),
|
||||
([field1], [field2]) => {
|
||||
if !tcx.features().result_ffi_guarantees {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty1 = field1.ty(tcx, args);
|
||||
let ty2 = field2.ty(tcx, args);
|
||||
|
||||
if is_niche_optimization_candidate(tcx, param_env, ty1) {
|
||||
ty2
|
||||
} else if is_niche_optimization_candidate(tcx, param_env, ty2) {
|
||||
ty1
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
_ => return None,
|
||||
|
@ -1202,7 +1244,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
args: GenericArgsRef<'tcx>,
|
||||
) -> FfiResult<'tcx> {
|
||||
use FfiResult::*;
|
||||
|
||||
let transparent_with_all_zst_fields = if def.repr().transparent() {
|
||||
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
|
||||
// Transparent newtypes have at most one non-ZST field which needs to be checked..
|
||||
|
@ -1329,22 +1370,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
return FfiSafe;
|
||||
}
|
||||
|
||||
// Check for a repr() attribute to specify the size of the
|
||||
// discriminant.
|
||||
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
|
||||
{
|
||||
// Special-case types like `Option<extern fn()>`.
|
||||
if repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode)
|
||||
.is_none()
|
||||
{
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_enum_repr_reason,
|
||||
help: Some(fluent::lint_improper_ctypes_enum_repr_help),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
|
@ -1353,6 +1378,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
|||
};
|
||||
}
|
||||
|
||||
// Check for a repr() attribute to specify the size of the
|
||||
// discriminant.
|
||||
if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
|
||||
{
|
||||
// Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
|
||||
if let Some(ty) =
|
||||
repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode)
|
||||
{
|
||||
return self.check_type_for_ffi(cache, ty);
|
||||
}
|
||||
|
||||
return FfiUnsafe {
|
||||
ty,
|
||||
reason: fluent::lint_improper_ctypes_enum_repr_reason,
|
||||
help: Some(fluent::lint_improper_ctypes_enum_repr_help),
|
||||
};
|
||||
}
|
||||
|
||||
// Check the contained variants.
|
||||
for variant in def.variants() {
|
||||
let is_non_exhaustive = variant.is_field_list_non_exhaustive();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue