Skip to content

Commit

Permalink
WIP: bitsize derive on generic structs
Browse files Browse the repository at this point in the history
  • Loading branch information
kitlith committed Aug 30, 2023
1 parent 41843c8 commit 503fa47
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 14 deletions.
10 changes: 8 additions & 2 deletions bilge-impl/src/bitsize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@ fn analyze_enum(bitsize: BitSize, variants: Iter<Variant>) {
}

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| {
Expand All @@ -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!(
Expand Down
51 changes: 42 additions & 9 deletions bilge-impl/src/bitsize_internal.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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 {
Expand All @@ -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(()),
};
Expand All @@ -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![];
Expand All @@ -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 {
Expand All @@ -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 )*
}
Expand Down Expand Up @@ -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 = <Self::ArbitraryInt as Bitsized>::BITS;
const MAX: Self::ArbitraryInt = <Self::ArbitraryInt as Bitsized>::MAX;
Expand Down
2 changes: 1 addition & 1 deletion bilge-impl/src/default_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion bilge-impl/src/from_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion bilge-impl/src/try_from_bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down
6 changes: 6 additions & 0 deletions tests/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ impl_from!(T; Generic<T> => 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<T> 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<T>(Generic<T>);

#[bitsize(2)]
#[derive(DefaultBits, PartialEq, DebugBits, TryFromBits)]
struct UsingGenericUnfilled(Generic<()>);

0 comments on commit 503fa47

Please sign in to comment.