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

stupid branch stuff. #248

Merged
merged 5 commits into from
May 28, 2024
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: 1 addition & 1 deletion derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bytemuck_derive"
description = "derive proc-macros for `bytemuck`"
version = "1.6.1"
version = "1.7.0"
authors = ["Lokathor <[email protected]>"]
repository = "https://github.com/Lokathor/bytemuck"
readme = "README.md"
Expand Down
4 changes: 4 additions & 0 deletions derive/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

## `bytemuck_derive` changelog

## 1.7.0

* Allow generics in `derive(ByteEq, ByteHash)` https://github.com/Lokathor/bytemuck/pull/219

## 1.6.0

* This allows `CheckedBitPattern` to be derived for enums with fields.
Expand Down
149 changes: 98 additions & 51 deletions derive/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ pub trait Derivable {
fn implies_trait(_crate_name: &TokenStream) -> Option<TokenStream> {
None
}
fn asserts(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
_input: &DeriveInput, _crate_name: &TokenStream,
) -> Result<TokenStream> {
Ok(quote!())
}
fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> {
Ok(())
}
fn trait_impl(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(
_input: &DeriveInput, _crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
Ok((quote!(), quote!()))
}
fn requires_where_clause() -> bool {
Expand All @@ -49,7 +53,9 @@ impl Derivable for Pod {
Ok(syn::parse_quote!(#crate_name::Pod))
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
let repr = get_repr(&input.attrs)?;

let completly_packed =
Expand Down Expand Up @@ -106,10 +112,14 @@ impl Derivable for AnyBitPattern {
Some(quote!(#crate_name::Zeroable))
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
match &input.data {
Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern`
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input, crate_name)?),
Data::Struct(_) => {
generate_fields_are_trait(input, Self::ident(input, crate_name)?)
}
Data::Enum(_) => {
bail!("Deriving AnyBitPattern is not supported for enums")
}
Expand All @@ -128,7 +138,7 @@ impl Derivable for Zeroable {
let repr = get_repr(attributes)?;
match ty {
Data::Struct(_) => Ok(()),
Data::Enum(DataEnum { variants,.. }) => {
Data::Enum(DataEnum { variants, .. }) => {
if !repr.repr.is_integer() {
bail!("Zeroable requires the enum to be an explicit #[repr(Int)]")
}
Expand All @@ -151,15 +161,19 @@ impl Derivable for Zeroable {
}

Ok(())
},
Data::Union(_) => Ok(())
}
Data::Union(_) => Ok(()),
}
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
match &input.data {
Data::Union(_) => Ok(quote!()), // unions are always `Zeroable`
Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input, crate_name)?),
Data::Struct(_) => {
generate_fields_are_trait(input, Self::ident(input, crate_name)?)
}
Data::Enum(_) => Ok(quote!()),
}
}
Expand Down Expand Up @@ -192,7 +206,9 @@ impl Derivable for NoUninit {
}
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
if !input.generics.params.is_empty() {
bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs");
}
Expand All @@ -219,7 +235,9 @@ impl Derivable for NoUninit {
}
}

fn trait_impl(_input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(
_input: &DeriveInput, _crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
Ok((quote!(), quote!()))
}
}
Expand Down Expand Up @@ -255,7 +273,9 @@ impl Derivable for CheckedBitPattern {
}
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
if !input.generics.params.is_empty() {
bail!("CheckedBitPattern cannot be derived for structs containing generic parameters");
}
Expand All @@ -267,15 +287,23 @@ impl Derivable for CheckedBitPattern {

Ok(assert_fields_are_maybe_pod)
}
Data::Enum(_) => Ok(quote!()), /* nothing needed, already guaranteed OK by NoUninit */
Data::Enum(_) => Ok(quote!()), /* nothing needed, already guaranteed
* OK by NoUninit */
Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
}
}

fn trait_impl(input: &DeriveInput, crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
match &input.data {
Data::Struct(DataStruct { fields, .. }) => {
generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs, crate_name)
generate_checked_bit_pattern_struct(
&input.ident,
fields,
&input.attrs,
crate_name,
)
}
Data::Enum(DataEnum { variants, .. }) => {
generate_checked_bit_pattern_enum(input, variants, crate_name)
Expand Down Expand Up @@ -322,7 +350,9 @@ impl Derivable for TransparentWrapper {
Ok(syn::parse_quote!(#crate_name::TransparentWrapper<#ty>))
}

fn asserts(input: &DeriveInput, crate_name: &TokenStream) -> Result<TokenStream> {
fn asserts(
input: &DeriveInput, crate_name: &TokenStream,
) -> Result<TokenStream> {
let (impl_generics, _ty_generics, where_clause) =
input.generics.split_for_impl();
let fields = get_struct_fields(input)?;
Expand Down Expand Up @@ -390,7 +420,9 @@ impl Derivable for Contiguous {
Ok(syn::parse_quote!(#crate_name::Contiguous))
}

fn trait_impl(input: &DeriveInput, _crate_name: &TokenStream) -> Result<(TokenStream, TokenStream)> {
fn trait_impl(
input: &DeriveInput, _crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
let repr = get_repr(&input.attrs)?;

let integer_ty = if let Some(integer_ty) = repr.repr.as_integer() {
Expand Down Expand Up @@ -431,19 +463,19 @@ impl Derivable for Contiguous {
let min_lit = LitInt::new(&format!("{}", min), input.span());
let max_lit = LitInt::new(&format!("{}", max), input.span());

// `from_integer` and `into_integer` are usually provided by the trait's default implementation.
// We override this implementation because it goes through `transmute_copy`, which can lead to
// inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 .
// `from_integer` and `into_integer` are usually provided by the trait's
// default implementation. We override this implementation because it
// goes through `transmute_copy`, which can lead to inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 .

Ok((
quote!(),
quote! {
type Int = #integer_ty;

#![allow(clippy::missing_docs_in_private_items)]
#[allow(clippy::missing_docs_in_private_items)]
const MIN_VALUE: #integer_ty = #min_lit;

#![allow(clippy::missing_docs_in_private_items)]
#[allow(clippy::missing_docs_in_private_items)]
const MAX_VALUE: #integer_ty = #max_lit;

#[inline]
Expand Down Expand Up @@ -498,7 +530,8 @@ fn get_field_types<'a>(
}

fn generate_checked_bit_pattern_struct(
input_ident: &Ident, fields: &Fields, attrs: &[Attribute], crate_name: &TokenStream
input_ident: &Ident, fields: &Fields, attrs: &[Attribute],
crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span());

Expand Down Expand Up @@ -545,7 +578,8 @@ fn generate_checked_bit_pattern_struct(
}

fn generate_checked_bit_pattern_enum(
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
if enum_has_fields(variants.iter()) {
generate_checked_bit_pattern_enum_with_fields(input, variants, crate_name)
Expand Down Expand Up @@ -614,7 +648,8 @@ fn generate_checked_bit_pattern_enum_without_fields(
}

fn generate_checked_bit_pattern_enum_with_fields(
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream
input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
let representation = get_repr(&input.attrs)?;
let vis = &input.vis;
Expand All @@ -634,23 +669,28 @@ fn generate_checked_bit_pattern_enum_with_fields(

let bits_repr = Representation { repr: Repr::C, ..representation };

// the enum manually re-configured as the actual tagged union it represents,
// thus circumventing the requirements rust imposes on the tag even when using
// #[repr(C)] enum layout
// the enum manually re-configured as the actual tagged union it
// represents, thus circumventing the requirements rust imposes on
// the tag even when using #[repr(C)] enum layout
// see: https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields
let bits_ty_ident = Ident::new(&format!("{input_ident}Bits"), input.span());

// the variants union part of the tagged union. These get put into a union which gets the
// AnyBitPattern derive applied to it, thus checking that the fields of the union obey the requriements of AnyBitPattern.
// The types that actually go in the union are one more level of indirection deep: we generate new structs for each variant
// (`variant_struct_definitions`) which themselves have the `CheckedBitPattern` derive applied, thus generating `{variant_struct_ident}Bits`
// structs, which are the ones that go into this union.
let bits_ty_ident =
Ident::new(&format!("{input_ident}Bits"), input.span());

// the variants union part of the tagged union. These get put into a union
// which gets the AnyBitPattern derive applied to it, thus checking
// that the fields of the union obey the requriements of AnyBitPattern.
// The types that actually go in the union are one more level of
// indirection deep: we generate new structs for each variant
// (`variant_struct_definitions`) which themselves have the
// `CheckedBitPattern` derive applied, thus generating
// `{variant_struct_ident}Bits` structs, which are the ones that go
// into this union.
let variants_union_ident =
Ident::new(&format!("{}Variants", input.ident), input.span());

let variant_struct_idents = variants
.iter()
.map(|v| Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span()));
let variant_struct_idents = variants.iter().map(|v| {
Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
});

let variant_struct_definitions =
variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
Expand All @@ -663,8 +703,10 @@ fn generate_checked_bit_pattern_enum_with_fields(
}
});

let union_fields =
variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
let union_fields = variant_struct_idents
.clone()
.zip(variants.iter())
.map(|(variant_struct_ident, v)| {
let variant_struct_bits_ident =
Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
let field_ident = &v.ident;
Expand Down Expand Up @@ -764,17 +806,20 @@ fn generate_checked_bit_pattern_enum_with_fields(
let bits_repr = Representation { repr: Repr::C, ..representation };
let input_ident = &input.ident;

// the enum manually re-configured as the union it represents. such a union is the union of variants
// as a repr(c) struct with the discriminator type inserted at the beginning.
// in our case we union the `Bits` representation of each variant rather than the variant itself, which we generate
// via a nested `CheckedBitPattern` derive on the `variant_struct_definitions` generated below.
// the enum manually re-configured as the union it represents. such a
// union is the union of variants as a repr(c) struct with the
// discriminator type inserted at the beginning. in our case we
// union the `Bits` representation of each variant rather than the variant
// itself, which we generate via a nested `CheckedBitPattern` derive
// on the `variant_struct_definitions` generated below.
//
// see: https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields
let bits_ty_ident = Ident::new(&format!("{input_ident}Bits"), input.span());
let bits_ty_ident =
Ident::new(&format!("{input_ident}Bits"), input.span());

let variant_struct_idents = variants
.iter()
.map(|v| Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span()));
let variant_struct_idents = variants.iter().map(|v| {
Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
});

let variant_struct_definitions =
variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
Expand All @@ -788,8 +833,10 @@ fn generate_checked_bit_pattern_enum_with_fields(
}
});

let union_fields =
variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
let union_fields = variant_struct_idents
.clone()
.zip(variants.iter())
.map(|(variant_struct_ident, v)| {
let variant_struct_bits_ident =
Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
let field_ident = &v.ident;
Expand Down
1 change: 1 addition & 0 deletions src/allocation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg(feature = "extern_crate_alloc")]
#![allow(clippy::duplicated_attributes)]

//! Stuff to boost things in the `alloc` crate.
//!
Expand Down
21 changes: 12 additions & 9 deletions src/anybitpattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use crate::{Pod, Zeroable};

/// Marker trait for "plain old data" types that are valid for any bit pattern.
///
/// The requirements for this is very similar to [`Pod`],
/// except that the type can allow uninit (or padding) bytes.
/// This limits what you can do with a type of this kind, but also broadens the
/// included types to `repr(C)` `struct`s that contain padding as well as
/// `union`s. Notably, you can only cast *immutable* references and *owned*
/// values into [`AnyBitPattern`] types, not *mutable* references.
/// The requirements for this is very similar to [`Pod`], except that the type
/// can allow uninit (or padding) bytes. This limits what you can do with a type
/// of this kind, but also broadens the included types to `repr(C)` `struct`s
/// that contain padding as well as `union`s. Notably, you can only cast
/// *immutable* references and *owned* values into [`AnyBitPattern`] types, not
/// *mutable* references.
///
/// [`Pod`] is a subset of [`AnyBitPattern`], meaning that any `T: Pod` is also
/// [`AnyBitPattern`] but any `T: AnyBitPattern` is not necessarily [`Pod`].
Expand All @@ -26,8 +26,8 @@ use crate::{Pod, Zeroable};
/// below safety rules.
///
/// * *NOTE: even `C-style`, fieldless enums are intentionally **excluded** from
/// this trait, since it is **unsound** for an enum to have a discriminant value
/// that is not one of its defined variants.
/// this trait, since it is **unsound** for an enum to have a discriminant
/// value that is not one of its defined variants.
///
/// # Safety
///
Expand Down Expand Up @@ -56,6 +56,9 @@ pub unsafe trait AnyBitPattern:
unsafe impl<T: Pod> AnyBitPattern for T {}

#[cfg(feature = "zeroable_maybe_uninit")]
#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "zeroable_maybe_uninit")))]
#[cfg_attr(
feature = "nightly_docs",
doc(cfg(feature = "zeroable_maybe_uninit"))
)]
unsafe impl<T> AnyBitPattern for core::mem::MaybeUninit<T> where T: AnyBitPattern
{}
Loading
Loading