Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for bytemuck #336

Merged
merged 3 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ exclude = ["tests", ".github"]
[dependencies]
serde = { version = "1.0", optional = true, default-features = false }
arbitrary = { version = "1.0", optional = true }
bytemuck = { version = "1.0", optional = true }
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
compiler_builtins = { version = "0.1.2", optional = true }

Expand All @@ -32,6 +33,7 @@ serde_derive = "1.0"
serde_json = "1.0"
serde_test = "1.0"
arbitrary = { version = "1.0", features = ["derive"] }
bytemuck = { version = "1.0", features = ["derive"] }

[features]
std = []
Expand Down
129 changes: 129 additions & 0 deletions src/external.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,83 @@
//! Conditional trait implementations for external libraries.

/*
How do I support a new external library?

Let's say we want to add support for `my_library`.

First, we define a macro like so:

```rust
#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(feature = "serde")]
macro_rules! __impl_external_bitflags_my_library {
(
$InternalBitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident;
)*
}
) => {
// Implementation goes here
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(not(feature = "my_library"))]
macro_rules! __impl_external_bitflags_my_library {
(
$InternalBitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident;
)*
}
) => {};
}
```

Note that the macro is actually defined twice; once for when the `my_library` feature
is available, and once for when it's not. This is because the `__impl_external_bitflags_my_library`
macro is called in an end-user's library, not in `bitflags`. In an end-user's library we don't
know whether or not a particular feature of `bitflags` is enabled, so we unconditionally call
the macro, where the body of that macro depends on the feature flag.

Now, we add our macro call to the `__impl_external_bitflags` macro body:

```rust
__impl_external_bitflags_my_library! {
$InternalBitFlags: $T {
$(
$(#[$attr $($args)*])*
$Flag;
)*
}
}
```

What about libraries that _must_ be supported through `#[derive]`?

In these cases, the attributes will need to be added to the `__declare_internal_bitflags` macro when
the internal type is declared.
*/

#[cfg(feature = "serde")]
pub mod serde_support;
#[cfg(feature = "serde")]
pub use serde;

#[cfg(feature = "arbitrary")]
pub mod arbitrary_support;
#[cfg(feature = "arbitrary")]
pub use arbitrary;

#[cfg(feature = "bytemuck")]
pub mod bytemuck_support;
#[cfg(feature = "bytemuck")]
pub use bytemuck;

/// Implements traits from external libraries for the internal bitflags type.
#[macro_export(local_inner_macros)]
Expand Down Expand Up @@ -39,6 +112,15 @@ macro_rules! __impl_external_bitflags {
)*
}
}

__impl_external_bitflags_bytemuck! {
$InternalBitFlags: $T {
$(
$(#[$attr $($args)*])*
$Flag;
)*
}
}
};
}

Expand Down Expand Up @@ -129,3 +211,50 @@ macro_rules! __impl_external_bitflags_arbitrary {
}
) => {};
}

/// Implement `Pod` and `Zeroable` for the internal bitflags type.
#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(feature = "bytemuck")]
macro_rules! __impl_external_bitflags_bytemuck {
(
$InternalBitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident;
)*
}
) => {
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Pod
unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags
where
$T: $crate::__private::bytemuck::Pod,
{

}

// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Zeroable
unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags
where
$T: $crate::__private::bytemuck::Zeroable,
{

}
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
#[cfg(not(feature = "bytemuck"))]
macro_rules! __impl_external_bitflags_bytemuck {
(
$InternalBitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident;
)*
}
) => {};
}
19 changes: 19 additions & 0 deletions src/external/bytemuck_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#[cfg(test)]
mod tests {
use bytemuck::{Pod, Zeroable};

bitflags! {
#[derive(Pod, Zeroable, Clone, Copy)]
#[repr(transparent)]
struct Color: u32 {
const RED = 0x1;
const GREEN = 0x2;
const BLUE = 0x4;
}
}

#[test]
fn test_bytemuck() {
assert_eq!(0x1, bytemuck::cast::<Color, u32>(Color::RED));
}
}
3 changes: 3 additions & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ macro_rules! __declare_internal_bitflags {
$iter_vis:vis struct $Iter:ident;
$iter_names_vis:vis struct $IterNames:ident;
) => {
// NOTE: The ABI of this type is _guaranteed_ to be the same as `T`
// This is relied on by some external libraries like `bytemuck` to make
// its `unsafe` trait impls sound.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
$vis struct $InternalBitFlags {
Expand Down
9 changes: 2 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,9 @@
//! example docs.

#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(not(test), forbid(unsafe_code))]

#![doc(html_root_url = "https://docs.rs/bitflags/2.1.0")]
#![forbid(unsafe_code)]

#[doc(inline)]
pub use traits::BitFlags;
Expand All @@ -408,12 +409,6 @@ pub mod __private {
pub use crate::{external::*, traits::*};

pub use core;

#[cfg(feature = "serde")]
pub use serde;
Copy link
Member Author

@KodrAus KodrAus Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have moved into the external module alongside their supporting code


#[cfg(feature = "arbitrary")]
pub use arbitrary;
}

/*
Expand Down
2 changes: 1 addition & 1 deletion src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ macro_rules! __declare_public_bitflags {
$vis:vis struct $BitFlags:ident;
) => {
$(#[$outer])*
$vis struct $BitFlags(<Self as $crate::__private::PublicFlags>::Internal);
$vis struct $BitFlags(<$BitFlags as $crate::__private::PublicFlags>::Internal);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was getting a build error with Self being unknown here. Not sure if it was bytemuck's derives or a change in rustc but it was easily fixed.

};
}

Expand Down