-
Notifications
You must be signed in to change notification settings - Fork 24
Derive on structs with generics #79
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
503fa47
8c44e5b
453f7d9
f912e29
9e1c541
123ee67
c3cc057
62441df
dd67083
5f501fe
3071cf9
d6e3b12
3e489c5
fa2c3d2
853f418
d58ce82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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}; | ||
|
|
||
|
|
@@ -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 #where_clause { | ||
| /// WARNING: modifying this value directly can break invariants | ||
| value: #arb_int, | ||
| _phantom: ::core::marker::PhantomData<(#(#phantom),*)> | ||
|
kitlith marked this conversation as resolved.
Outdated
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it correct that adding a if so, do you think it's possible (and practical) to not generate a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be possible, yes, but it would create additional conditions at the construction sites (there are 4 in this PR) and potentially any external derive macros. We could refactor those to use a common constructor method, but I figured I'd go for the approach that requires the least conditional code. I'm fine either way, though. |
||
| } | ||
| 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 = <Self::ArbitraryInt as Bitsized>::BITS; | ||
| const MAX: Self::ArbitraryInt = <Self::ArbitraryInt as Bitsized>::MAX; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.