bitflags: bandaid fix for repr(transparent) bitflags#1170
Conversation
emilio
left a comment
There was a problem hiding this comment.
Could you rebase once your other patch is in main to remove some noise from the clippy changes (thanks for fixing those!)? In general, this feels a bit unfortunate, but I guess it's a fairly simple special-case for bitflags, at least...
The lack of something like #1029 being merged makes it so any internal pub type/newtype within my Rust code gets turned into a typedef in the C header
If you don't want that you can /// cbindgen:ignore it, right?
Will do!
I echo the sentiment... Hopefully someone with more time can swoop in and make some deeper changes to make workarounds like this unnecessary.
Nope! That just makes it worse, actually (example at the bottom)! Let's say I have either of the following: pub type Untrusted<T> = Option<NonNull<T>>;
// or
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct Untrusted<T>(Option<NonNull<T>>);My intention is two-fold:
#[unsafe(no_mangle)]
unsafe extern "C" fn find_available_devices(
dest: Option<NonNull<Option<NonNull<Vec<DeviceInfo>>>>>,
) -> libc::c_int {
int find_available_devices(struct Vec_DeviceInfo **dest);So I'd like to be able to do something like this instead in Rust: #[unsafe(no_mangle)]
unsafe extern "C" fn find_available_devices(
dest: Untrusted<Untrusted<Vec<DeviceInfo>>>
) -> libc::c_int {But the issue is that (for both int find_available_devices(Untrusted_Untrusted_Vec_DeviceInfo dest);I end up having internal implementation details leaked into the API, making the signature more complex/obscure than it really is to the end-user. (This also happens with just one layer of
"What using does Straight-up invalid code! int get_device_list(Untrusted<Untrusted<Vec<DeviceInfo>>> dest);
WARN: Cannot find a mangling for generic path GenericPath { path: Path { name: "Untrusted" }, export_name: "Untrusted", generics: [Type(Path(GenericPath { path: Path { name: "Untrusted" }, export_name: "Untrusted", generics: [Type(Path(GenericPath { path: Path { name: "[redacted]" }, export_name: "[redacted]", generics: [], ctype: None }))], ctype: None }))], ctype: None }. This usually means that a type referenced by this generic was incompatible or not found.This is why I've been relying on either (s l o w) macro expansion (to replace a (I spent a good couple days this week wrestling with all this + the bitflags stuff. I'd almost want to try cheadergen, but it's still maturing and I'm unsure if anything like the bitflags expansion is actually possible with their methodology.) |
dd505e9 to
f053cd1
Compare
Bitflags can be represented using either repr(C) or repr(transparent), but previously cbindgen would output field accesses w/ repr(transparent) bitflags. This commit falls back to using int_t typedefs and associated constants for those cases.
f053cd1 to
510833a
Compare
|
I see, thanks. So let me see if I got your use case right. Given this rust file: use std::ptr::NonNull;
struct DeviceInfo {
foo: u32,
}
pub type Untrusted<T> = Option<NonNull<T>>;
#[unsafe(no_mangle)]
unsafe extern "C" fn find_available_devices(_dest: Untrusted<Untrusted<Vec<DeviceInfo>>>) -> i32 {
todo!()
}Right now cbindgen for C++ generates: // headers omitted for brevity
struct DeviceInfo;
template<typename T = void>
struct Vec;
template<typename T>
using Untrusted = T*;
extern "C" {
int32_t find_available_devices(Untrusted<Untrusted<Vec<DeviceInfo>>> _dest);
}Which is pretty fair. For C we generate: // headers omitted for brevity.
typedef struct Vec_DeviceInfo Vec_DeviceInfo;
typedef struct Vec_DeviceInfo *Untrusted_Vec_DeviceInfo;
typedef Untrusted_Vec_DeviceInfo *Untrusted_Untrusted_Vec_DeviceInfo;
int32_t find_available_devices(Untrusted_Untrusted_Vec_DeviceInfo _dest);Which is actually pretty fair as well, right? As in, it's correct, just so happens that you care about the conciseness of the generated header, and I agree that Re. #1029, I didn't get to review that and it fell down the queue, sorry, I can try to take a look. At a glance doing a whole other global pass over all types seems unfortunate. Might be better to try to do this during monomorphisation if this mostly affects generics / C? Unfortunately I don't have that much time to dedicate to cbindgen outside of work, and for Firefox we mostly care about the C++ output... |
Mhm, you got it! I'm trying to have the headers as clean as possible for when I hand them off to those who need my library as a linkable object, which includes folks who know C well but not a lick of Rust. The more familiar/easily digestible it is, the better and faster we can get our work done without staring at autogenerated visual noise.
My C++ knowledge is near-zero, so I can't speak super confidently on how harshly this affects C++ comparatively, but given your own example and the one in #1029, having something like
It happens, c'est la vie~ Thanks for at least entertaining me with my concerns and ideas today though. ❤️ Let me know if this PR (or my previous) need any further attention or explanation, should be good to go otherwise now I think (since it's mostly unrelated to the tangent at hand). :) |
While working on #1169, I noticed that my bitflags were outputting invalid code/
#defineswhen usingrepr(transparent)and creating a new flag/mask from other bitflags.Namely, it looked like:
or even this (when using the "out-of-line"
implkind ofbitflags!invocation):I recognize (via #1096) that this isn't unique to bitflags macro expansions, but I wanted to see how easily I could fix that so I could move on with my project without changing even more of my code, just to have
cbindgenunderstand it.(The lack of something like #1029 being merged makes it so any internal
pub type/newtype within my Rust code gets turned into atypedefin the C header, which is annoying when I want to make a "hidden"Untrusted<T>pointer wrapper type but I need to instead name it something likeNonNullso that it can get folded into a plainT*pointer in the header, without needing to rely on something like macro expansion.....)This uses the same clippy-appeasing commit from #1169, just cherry-picked. Let me know if I should drop it to allow for a cleaner git history.