- 
                Notifications
    
You must be signed in to change notification settings  - Fork 13.9k
 
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.
- 
PhantomDataon 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_ctypeslint 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 thatPhantomDatais 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). ↩