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

Derive on structs with generics #79

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Experimental: move assert generation into bitsize_internal
This allows me to make accesses to the consts on the Bitsize trait
error.

We could nix the Assertion trait here and just use the check expression directly.
kitlith committed Sep 13, 2023
commit 3e489c55c244e0b04c9902d27876367634108fca
31 changes: 1 addition & 30 deletions bilge-impl/src/bitsize.rs
Original file line number Diff line number Diff line change
@@ -120,43 +120,14 @@ fn analyze_enum(bitsize: BitSize, variants: Iter<Variant>) {
}
}

fn generate_struct(item: &ItemStruct, declared_bitsize: u8) -> TokenStream {
let ItemStruct { ident, fields, generics, .. } = item;
let declared_bitsize = declared_bitsize as usize;

let computed_bitsize = fields.iter().fold(quote!(0), |acc, next| {
let field_size = shared::generate_type_bitsize(&next.ty);
quote!(#acc + #field_size)
});

fn generate_struct(item: &ItemStruct, _declared_bitsize: u8) -> TokenStream {
let item = ItemStruct {
attrs: Vec::new(),
..item.clone()
};

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

quote! {
#item

const _: () = {
// TODO: This is useless without methods that reference it, and this is un-namable outside of this block.
// Move or add some method implementations inside this block?
trait Assertion {
const SIZE_CHECK: ();
}

impl #impl_generics Assertion for #ident #ty_generics #where_clause {
// constness: when we get const blocks evaluated at compile time, add a const computed_bitsize
const SIZE_CHECK: () = assert!(
(#computed_bitsize) == (#declared_bitsize),
concat!("struct size and declared bit size differ: ",
// stringify!(#computed_bitsize),
" != ",
stringify!(#declared_bitsize))
);
}
};
}
}

104 changes: 66 additions & 38 deletions bilge-impl/src/bitsize_internal.rs
Original file line number Diff line number Diff line change
@@ -13,52 +13,32 @@ struct ItemIr<'a> {
/// generated item (and setters, getters, constructor, impl Bitsized)
expanded: TokenStream,
generics: &'a Generics,
check_expr: TokenStream,
}

pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> TokenStream {
let (item, arb_int) = parse(item, args);
let (item, arb_int, declared_bitsize) = parse(item, args);
let ir = match item {
Item::Struct(ref item) => {
let expanded = generate_struct(item, &arb_int);
let attrs = &item.attrs;
let name = &item.ident;
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;
let generics = &item.generics;
ItemIr {
attrs,
name,
expanded,
generics,
}
}
Item::Struct(ref item) => generate_struct(item, &arb_int, declared_bitsize),
Item::Enum(ref item) => generate_enum(item),
_ => unreachable(()),
};
generate_common(ir, &arb_int)
}

fn parse(item: TokenStream, args: TokenStream) -> (Item, TokenStream) {
fn parse(item: TokenStream, args: TokenStream) -> (Item, TokenStream, u8) {
let item = syn::parse2(item).unwrap_or_else(unreachable);
let (_declared_bitsize, arb_int) = shared::bitsize_and_arbitrary_int_from(args);
(item, arb_int)
let (declared_bitsize, arb_int) = shared::bitsize_and_arbitrary_int_from(args);
(item, arb_int, declared_bitsize)
}

fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStream {
fn generate_struct<'a>(struct_data: &'a ItemStruct, arb_int: &TokenStream, declared_bitsize: u8) -> ItemIr<'a> {
let ItemStruct {
vis,
ident,
fields,
generics,
attrs,
..
} = struct_data;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@@ -88,7 +68,7 @@ fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStre

let phantom_type = generate_phantom_type(generics);

quote! {
let expanded = quote! {
#vis struct #ident #generics #where_clause {
/// WARNING: modifying this value directly can break invariants
value: #arb_int,
@@ -108,6 +88,29 @@ fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStre
}
#( #accessors )*
}
};

let computed_bitsize = fields.iter().fold(quote!(0), |acc, next| {
let field_size = shared::generate_type_bitsize(&next.ty);
quote!(#acc + #field_size)
});

let declared_bitsize = declared_bitsize as usize;

let check_expr = quote!(assert!(
(#computed_bitsize) == (#declared_bitsize),
concat!("struct size and declared bit size differ: ",
// stringify!(#computed_bitsize),
" != ",
stringify!(#declared_bitsize))
));

ItemIr {
attrs,
name: ident,
expanded,
generics,
check_expr,
}
}

@@ -247,12 +250,26 @@ fn generate_constructor_stuff(ty: &Type, name: &Ident) -> (TokenStream, TokenStr
(constructor_arg, constructor_part)
}

fn generate_enum(enum_data: &ItemEnum) -> TokenStream {
let ItemEnum { vis, ident, variants, .. } = enum_data;
quote! {
fn generate_enum(enum_data: &ItemEnum) -> ItemIr {
let ItemEnum {
vis,
ident,
variants,
generics,
attrs,
..
} = enum_data;
let expanded = quote! {
#vis enum #ident {
#variants
}
};
ItemIr {
attrs,
name: ident,
expanded,
generics,
check_expr: quote! { () },
}
}

@@ -264,17 +281,28 @@ fn generate_common(ir: ItemIr, arb_int: &TokenStream) -> TokenStream {
name,
expanded,
generics,
check_expr,
} = ir;

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

quote! {
#(#attrs)*
#expanded
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;
}
const _: () = {
trait Assertion {
const SIZE_CHECK: ();
}

impl #impl_generics Assertion for #name #ty_generics #where_clause {
const SIZE_CHECK: () = #check_expr;
}

impl #impl_generics ::bilge::Bitsized for #name #ty_generics #where_clause {
type ArbitraryInt = #arb_int;
const BITS: usize = (<Self::ArbitraryInt as Bitsized>::BITS, <Self as Assertion>::SIZE_CHECK).0;
const MAX: Self::ArbitraryInt = (<Self::ArbitraryInt as Bitsized>::MAX, <Self as Assertion>::SIZE_CHECK).0;
}
};
Comment on lines +301 to +306
Copy link
Collaborator

Choose a reason for hiding this comment

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

that's clever! congratulations on finding this workaround!

}
}