Description
If I have a #[repr(transparent)]
wrapper around a PhantomData
, it's considered an improper C type when used in a struct field, even though the wrapped PhantomData
would be allowed. Specifically (playground)
#[repr(transparent)]
#[derive(Copy, Clone)]
struct MyPhantom(core::marker::PhantomData<u8>);
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Bar {
pub x: i32,
_marker: MyPhantom,
}
extern "C" {
pub fn foo(bar: *mut Bar);
}
produces the error
Compiling playground v0.0.1 (/playground)
warning: `extern` block uses type `MyPhantom`, which is not FFI-safe
--> src/lib.rs:13:21
|
13 | pub fn foo(bar: *mut Bar);
| ^^^^^^^^ not FFI-safe
|
= note: this struct contains only zero-sized fields
note: the type is defined here
--> src/lib.rs:3:1
|
3 | struct MyPhantom(core::marker::PhantomData<u8>);
| ^^^^^^^^^^^^^^^^
= note: `#[warn(improper_ctypes)]` on by default
warning: `playground` (lib) generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.51s
This is over-zealous for a few reasons.
-
PhantomData
on its own is allowed in C structs, so changing this to_marker: PhantomData<u8>
would solve the warning (but defeats the point of my use case1). -
#[repr(transparent)]
is supposed to be treated as the inner type for purposes of ABI, and theimproper_ctypes
lint is about ABI. -
Using
#[repr(C)]
instead of#[repr(transparent)]
solves this technically (it shuts the warning up), but seems pretty dodgy, since it's not actually laid out the way C would lay it out anymore (that is, we've guaranteed thatPhantomData
is never going to impact layout at all, and we haven't guaranteed this for zero-sized#[repr(C)]
types which are notably not a real thing in C).
TLDR: this lint fires on #[repr(transparent)]
wrappers around PhantomData
when they're used as struct fields. IMO these should be fine since PhantomData
doesn't emit a warning in that case, and #[repr(transparent)]
is supposed to defer to the inner type.
(That said, firing this lint on a type passed by pointer is pretty weird anyway, since the indirection means doesn't actually have to be a valid C type (for the ABI), which is the basic complaint behind #66373, which is not what I'm complaining about. IMO this shouldn't fire even if Bar
were being passed by value, as making the field PhantomData
(instead of a wrapper around one) doesn't fire in that case)
Footnotes
-
If you're curious about the real use case I have, a closer but still-basically-minimized version is here. It's important that the type not be a direct
PhantomData
(basically#[non_exhaustive]
is not really usable for FFI, but C has cases where considers appending new fields to the end of a type to be a non-breaking change). ↩