diff --git a/bilge-impl/src/bitsize.rs b/bilge-impl/src/bitsize.rs index 6c8d306..22b6bb4 100644 --- a/bilge-impl/src/bitsize.rs +++ b/bilge-impl/src/bitsize.rs @@ -121,7 +121,13 @@ fn analyze_enum(bitsize: BitSize, variants: Iter) { } fn generate_struct(item: &ItemStruct, declared_bitsize: u8) -> TokenStream { - let ItemStruct { vis, ident, fields, .. } = item; + let ItemStruct { + vis, + ident, + fields, + generics, + .. + } = item; let declared_bitsize = declared_bitsize as usize; let computed_bitsize = fields.iter().fold(quote!(0), |acc, next| { @@ -144,7 +150,7 @@ fn generate_struct(item: &ItemStruct, declared_bitsize: u8) -> TokenStream { }; quote! { - #vis struct #ident #fields_def + #vis struct #ident #generics #fields_def // constness: when we get const blocks evaluated at compile time, add a const computed_bitsize const _: () = assert!( diff --git a/bilge-impl/src/bitsize_internal.rs b/bilge-impl/src/bitsize_internal.rs index e6061a7..94e3713 100644 --- a/bilge-impl/src/bitsize_internal.rs +++ b/bilge-impl/src/bitsize_internal.rs @@ -1,6 +1,6 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; -use syn::{Attribute, Field, Item, ItemEnum, ItemStruct, Type}; +use syn::{Attribute, Field, Generics, Item, ItemEnum, ItemStruct, Type}; use crate::shared::{self, unreachable}; @@ -12,6 +12,7 @@ struct ItemIr<'a> { name: &'a Ident, /// generated item (and setters, getters, constructor, impl Bitsized) expanded: TokenStream, + generics: &'a Generics, } pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> TokenStream { @@ -21,13 +22,25 @@ pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> TokenStr let expanded = generate_struct(item, &arb_int); let attrs = &item.attrs; let name = &item.ident; - ItemIr { attrs, name, expanded } + let generics = &item.generics; + ItemIr { + attrs, + name, + expanded, + generics, + } } Item::Enum(ref item) => { let expanded = generate_enum(item); let attrs = &item.attrs; let name = &item.ident; - ItemIr { attrs, name, expanded } + let generics = &item.generics; + ItemIr { + attrs, + name, + expanded, + generics, + } } _ => unreachable(()), }; @@ -41,7 +54,19 @@ fn parse(item: TokenStream, args: TokenStream) -> (Item, TokenStream) { } fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStream { - let ItemStruct { vis, ident, fields, .. } = struct_data; + let ItemStruct { + vis, + ident, + fields, + generics, + .. + } = struct_data; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let phantom_ty = generics.type_params().map(|e| &e.ident).map(|ident| quote!(#ident)); + let phantom_lt = generics.lifetimes().map(|l| &l.lifetime).map(|lifetime| quote!(& #lifetime ())); + // TODO: integrate user-provided PhantomData somehow? (so that the user can set the variance) + let phantom = phantom_ty.chain(phantom_lt); let mut fieldless_next_int = 0; let mut previous_field_sizes = vec![]; @@ -67,11 +92,12 @@ fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStre let const_ = if cfg!(feature = "nightly") { quote!(const) } else { quote!() }; quote! { - #vis struct #ident { + #vis struct #ident #generics { /// WARNING: modifying this value directly can break invariants value: #arb_int, + _phantom: ::core::marker::PhantomData<(#(#phantom),*)> } - impl #ident { + impl #impl_generics #ident #ty_generics #where_clause { // #[inline] #[allow(clippy::too_many_arguments, clippy::type_complexity, unused_parens)] pub #const_ fn new(#( #constructor_args )*) -> Self { @@ -81,7 +107,7 @@ fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStre let mut offset = 0; let raw_value = #( #constructor_parts )|*; let value = #arb_int::new(raw_value); - Self { value } + Self { value, _phantom: ::core::marker::PhantomData } } #( #accessors )* } @@ -221,12 +247,19 @@ fn generate_enum(enum_data: &ItemEnum) -> TokenStream { /// We have _one_ `generate_common` function, which holds everything struct and enum have _in common_. /// Everything else has its own `generate_` functions. fn generate_common(ir: ItemIr, arb_int: &TokenStream) -> TokenStream { - let ItemIr { attrs, name, expanded } = ir; + let ItemIr { + attrs, + name, + expanded, + generics, + } = ir; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { #(#attrs)* #expanded - impl ::bilge::Bitsized for #name { + impl #impl_generics ::bilge::Bitsized for #name #ty_generics #where_clause { type ArbitraryInt = #arb_int; const BITS: usize = ::BITS; const MAX: Self::ArbitraryInt = ::MAX; diff --git a/bilge-impl/src/default_bits.rs b/bilge-impl/src/default_bits.rs index f8ce9df..ad1b6d2 100644 --- a/bilge-impl/src/default_bits.rs +++ b/bilge-impl/src/default_bits.rs @@ -29,7 +29,7 @@ fn generate_struct_default_impl(struct_name: &Ident, fields: &Fields) -> TokenSt let mut offset = 0; let value = #default_value; let value = <#struct_name as Bitsized>::ArbitraryInt::new(value); - Self { value } + Self { value, _phantom: ::core::marker::PhantomData } } } } diff --git a/bilge-impl/src/from_bits.rs b/bilge-impl/src/from_bits.rs index 39bbe5d..08d1da3 100644 --- a/bilge-impl/src/from_bits.rs +++ b/bilge-impl/src/from_bits.rs @@ -156,7 +156,7 @@ fn generate_struct(arb_int: TokenStream, struct_type: &Ident, fields: &Fields) - impl #const_ ::core::convert::From<#arb_int> for #struct_type { fn from(value: #arb_int) -> Self { #( #assumes )* - Self { value } + Self { value, _phantom: ::core::marker::PhantomData } } } impl #const_ ::core::convert::From<#struct_type> for #arb_int { diff --git a/bilge-impl/src/try_from_bits.rs b/bilge-impl/src/try_from_bits.rs index 3c8bf08..d77b37b 100644 --- a/bilge-impl/src/try_from_bits.rs +++ b/bilge-impl/src/try_from_bits.rs @@ -119,7 +119,7 @@ fn codegen_struct(arb_int: TokenStream, struct_type: &Ident, fields: &Fields) -> let is_ok: bool = {#is_ok}; if is_ok { - Ok(Self { value }) + Ok(Self { value, _phantom: ::core::marker::PhantomData }) } else { Err(::bilge::give_me_error()) } diff --git a/tests/struct.rs b/tests/struct.rs index a948ad9..58008b7 100644 --- a/tests/struct.rs +++ b/tests/struct.rs @@ -510,6 +510,12 @@ impl_from!(T; Generic => u2; |val| val.0); #[derive(DefaultBits, PartialEq, DebugBits, FromBits)] struct UsingGeneric(Generic<()>); +// TODO: bitsize doesn't work here yet because of the size check: it's trying to get the size of Generic in a non-generic context +// #[bitsize(2)] +// FromBits, DefaultBits, and DebugBits don't work yet because they need the generics threaded through to the correct spot. +// #[derive(PartialEq)] // TODO: FromBits, DefaultBits, DebugBits +// struct IsGeneric(Generic); + #[bitsize(2)] #[derive(DefaultBits, PartialEq, DebugBits, TryFromBits)] struct UsingGenericUnfilled(Generic<()>);