Skip to content

Commit

Permalink
Merge pull request #336 from KodrAus/feat/bytemuck
Browse files Browse the repository at this point in the history
Add support for bytemuck
  • Loading branch information
KodrAus committed Apr 17, 2023
2 parents 483b1bb + 98e9542 commit dc97104
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 8 deletions.
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 @@ -421,8 +421,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 @@ -435,12 +436,6 @@ pub mod __private {
pub use crate::{external::*, traits::*};

pub use core;

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

#[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);
};
}

Expand Down

0 comments on commit dc97104

Please sign in to comment.