Make NPO-optimized enums containing `struct`s with just a single "nullable" field "FFI-safe"
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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