rust-lang / rust-lang/rust

Make NPO-optimized enums containing `struct`s with just a single "nullable" field "FFI-safe"

Open
#148,997 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-ABI A-FFI C-feature-request T-compiler T-opsem
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the unsafe extern "C" declarations in the reported src/lib.rs reproduction and investigate how the improper_ctypes warning classifies the three Option types. The change is complete when the NPO cases shown in the reproduction are accepted as FFI-safe without warnings, while the existing safety distinction remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.