Skip to content

Make NPO-optimized enums containing structs with just a single "nullable" field "FFI-safe" #148997

@yyny

Description

@yyny

Code

#![allow(dead_code)]
use std::ptr::NonNull;
use std::marker::PhantomData;

#[repr(C)]
struct Pointer<'a, T: ?Sized> {
    pointer: NonNull<T>,
    _phantom: PhantomData<&'a T>,
}

#[repr(C)]
struct Slice<'a, T: ?Sized> {
    pointer: NonNull<T>,
    length: u64,
    _phantom: PhantomData<&'a T>,
}

#[repr(C)]
struct Value<'vm, T: ?Sized> {
    ty: &'vm (),
    value: Option<&'vm T>,
}

unsafe extern "C" {
    // Unambiguous: If `None`, the `Pointer.pointer` will be `null`.
    fn pointer<'a>(_: Option<Pointer<'a, u8>>) -> Option<Pointer<'a, u8>>;
    // Unambiguous: If `None`, the `Slice.pointer` will be `null`.
    fn slice<'a>(_: Option<Slice<'a, u8>>) -> Option<Slice<'a, u8>>;
    // Unambiguous: If `None`, the `Value.type` will be `null`.
    fn value<'a>(_: Option<Value<'a, u8>>) -> Option<Value<'a, u8>>;
}

Current output

warning: `extern` block uses type `Option<Pointer<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:26:23
   |
26 |     fn pointer<'a>(_: Option<Pointer<'a, u8>>) -> Option<Pointer<'a, u8>>;
   |                       ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint
   = note: `#[warn(improper_ctypes)]` on by default

warning: `extern` block uses type `Option<Pointer<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:26:51
   |
26 |     fn pointer<'a>(_: Option<Pointer<'a, u8>>) -> Option<Pointer<'a, u8>>;
   |                                                   ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint

warning: `extern` block uses type `Option<Slice<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:28:21
   |
28 |     fn slice<'a>(_: Option<Slice<'a, u8>>) -> Option<Slice<'a, u8>>;
   |                     ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint

warning: `extern` block uses type `Option<Slice<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:28:47
   |
28 |     fn slice<'a>(_: Option<Slice<'a, u8>>) -> Option<Slice<'a, u8>>;
   |                                               ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint

warning: `extern` block uses type `Option<Value<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:30:21
   |
30 |     fn value<'a>(_: Option<Value<'a, u8>>) -> Option<Value<'a, u8>>;
   |                     ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint

warning: `extern` block uses type `Option<Value<'_, u8>>`, which is not FFI-safe
  --> src/lib.rs:30:47
   |
30 |     fn value<'a>(_: Option<Value<'a, u8>>) -> Option<Value<'a, u8>>;
   |                                               ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
   |
   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
   = note: enum has no representation hint

warning: `playground` (lib) generated 6 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.54s

Desired output

Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.59s

Rationale and extra context

All enums with the following properties should be considered "FFI-safe":

  • they have the "Null Pointer Optimization" (NPO) applied to them
  • the value they contain is "FFI-safe"
  • the value they contain has exactly one field that enables NPO, and this field has exactly one "invalid" value

In other words, an NPO-optimized enum is FFI-safe if the "null" value can be unambiguously determined.

arguably, NPO-optimized enums should also be considered FFI-safe when the "null" value can not be unambiguously determined, but that is a rabbit hole that I don't want to get into right now, and probably worthy of an RFC.

Other cases

Adding #[repr(transparent)] to struct Pointer can remove the first few warnings. However, #[repr(transparent)] also changes the calling convention, which may not be desired.

Rust Version

$ rustc --version --verbose
rustc 1.91.1 (ed61e7d7e 2025-11-07)
binary: rustc
commit-hash: ed61e7d7e242494fb7057f2657300d9e77bb4fcb
commit-date: 2025-11-07
host: x86_64-unknown-linux-gnu
release: 1.91.1
LLVM version: 21.1.2

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ABIArea: Concerning the application binary interface (ABI)A-FFIArea: Foreign function interface (FFI)C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions