use crate::{ hdl_type_common::{ common_derives, get_target, ItemOptions, MaybeParsed, ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, }, kw, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::TokenStream; use quote::{format_ident, quote_spanned, ToTokens}; use syn::{ parse_quote, parse_quote_spanned, punctuated::{Pair, Punctuated}, spanned::Spanned, token::Brace, AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed, GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility, }; #[derive(Clone, Debug)] pub(crate) struct ParsedBundle { pub(crate) attrs: Vec, pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) struct_token: Token![struct], pub(crate) ident: Ident, pub(crate) generics: MaybeParsed, pub(crate) fields: MaybeParsed, pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident, pub(crate) match_variant_ident: Ident, pub(crate) builder_ident: Ident, pub(crate) mask_type_builder_ident: Ident, } impl ParsedBundle { fn parse_field( errors: &mut Errors, field: &mut Field, index: usize, ) -> Option> { let Field { attrs, vis: _, mutability, ident, colon_token, ty, } = field; let ident = ident.get_or_insert_with(|| format_ident!("_{}", index, span = ty.span())); if !matches!(mutability, FieldMutability::None) { // FIXME: use mutability as the spanned tokens, // blocked on https://github.com/dtolnay/syn/issues/1717 errors.error(&ident, "field mutability is not supported"); *mutability = FieldMutability::None; } *mutability = FieldMutability::None; colon_token.get_or_insert(Token![:](ident.span())); let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)); options } fn parse(item: ItemStruct) -> syn::Result { let ItemStruct { mut attrs, vis, struct_token, ident, mut generics, fields, semi_token, } = item; let mut errors = Errors::new(); let mut options = errors .unwrap_or_default(HdlAttr::::parse_and_take_attr(&mut attrs)) .unwrap_or_default(); errors.ok(options.body.validate()); let ItemOptions { outline_generated: _, connect_inexact: _, target: _, custom_bounds, no_static: _, } = options.body; let mut fields = match fields { syn::Fields::Named(fields) => fields, syn::Fields::Unnamed(fields) => { errors.error(&fields, "#[hdl] struct must use curly braces: {}"); FieldsNamed { brace_token: Brace(fields.paren_token.span), named: fields.unnamed, } } syn::Fields::Unit => { errors.error(&fields, "#[hdl] struct must use curly braces: {}"); FieldsNamed { brace_token: Brace(semi_token.unwrap_or_default().span), named: Punctuated::default(), } } }; let mut field_flips = Vec::with_capacity(fields.named.len()); for (index, field) in fields.named.iter_mut().enumerate() { field_flips.push(Self::parse_field(&mut errors, field, index)); } let generics = if custom_bounds.is_some() { MaybeParsed::Unrecognized(generics) } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { MaybeParsed::Parsed(generics) } else { MaybeParsed::Unrecognized(generics) }; let fields = TypesParser::maybe_run(generics.as_ref(), fields, &mut errors); errors.finish()?; Ok(Self { attrs, options, vis, struct_token, generics, fields, field_flips, mask_type_ident: format_ident!("__{}__MaskType", ident), mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), match_variant_ident: format_ident!("__{}__MatchVariant", ident), mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), builder_ident: format_ident!("__{}__Builder", ident), ident, }) } } #[derive(Clone, Debug)] struct Builder { vis: Visibility, struct_token: Token![struct], ident: Ident, target: Path, generics: Generics, fields: FieldsNamed, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] enum BuilderFieldState { Unfilled, Generic, Filled, } impl Builder { fn phantom_field_name(&self) -> Ident { format_ident!("__phantom", span = self.ident.span()) } fn phantom_field(&self) -> Field { let target = &self.target; let type_generics = self.generics.split_for_impl().1; Field { attrs: vec![], vis: Visibility::Inherited, mutability: FieldMutability::None, ident: Some(self.phantom_field_name()), colon_token: Some(Token![:](self.ident.span())), ty: parse_quote_spanned! {self.ident.span()=> ::fayalite::__std::marker::PhantomData<#target #type_generics> }, } } fn builder_struct_generics( &self, mut get_field_state: impl FnMut(usize) -> BuilderFieldState, ) -> Generics { let mut retval = self.generics.clone(); for (field_index, field) in self.fields.named.iter().enumerate() { match get_field_state(field_index) { BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue, BuilderFieldState::Generic => {} } if !retval.params.empty_or_trailing() { retval.params.push_punct(Token![,](self.ident.span())); } retval.params.push_value(GenericParam::Type( type_var_for_field_name(field.ident.as_ref().unwrap()).into(), )); } retval } fn builder_struct_ty( &self, mut get_field_state: impl FnMut(usize) -> BuilderFieldState, ) -> Type { let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() { parse_quote_spanned! {self.ident.span()=> <> } } else { let builder_type_generics = self.generics.split_for_impl().1; parse_quote! { #builder_type_generics } }; for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() { let ident = ident.as_ref().unwrap(); if !ty_arguments.args.empty_or_trailing() { ty_arguments.args.push_punct(Token![,](self.ident.span())); } ty_arguments .args .push_value(match get_field_state(field_index) { BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=> ::fayalite::bundle::Unfilled<#ty> }, BuilderFieldState::Generic => { let type_var = type_var_for_field_name(ident); parse_quote_spanned! {self.ident.span()=> #type_var } } BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=> ::fayalite::expr::Expr<#ty> }, }); } let ident = &self.ident; parse_quote_spanned! {ident.span()=> #ident #ty_arguments } } } fn type_var_for_field_name(ident: &Ident) -> Ident { format_ident!("__T_{}", ident) } fn field_fn_for_field_name(ident: &Ident) -> Ident { format_ident!("field_{}", ident) } impl ToTokens for Builder { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { vis, struct_token, ident, target, generics: _, fields, } = self; let phantom_field_name = self.phantom_field_name(); let builder_struct = ItemStruct { attrs: vec![parse_quote_spanned! {ident.span()=> #[allow(non_camel_case_types, dead_code)] }], vis: vis.clone(), struct_token: *struct_token, ident: ident.clone(), generics: self.builder_struct_generics(|_| BuilderFieldState::Generic), fields: Fields::Named(FieldsNamed { brace_token: fields.brace_token, named: Punctuated::from_iter( [Pair::Punctuated( self.phantom_field(), Token![,](self.ident.span()), )] .into_iter() .chain(fields.named.pairs().map_pair_value_ref(|field| { let ident = field.ident.as_ref().unwrap(); let type_var = type_var_for_field_name(ident); Field { vis: Visibility::Inherited, ty: parse_quote_spanned! {ident.span()=> #type_var }, ..field.clone() } })), ), }), semi_token: None, }; builder_struct.to_tokens(tokens); let field_idents = Vec::from_iter( self.fields .named .iter() .map(|field| field.ident.as_ref().unwrap()), ); for ( field_index, Field { vis, ident: field_ident, ty, .. }, ) in self.fields.named.iter().enumerate() { let field_ident = field_ident.as_ref().unwrap(); let fn_ident = field_fn_for_field_name(field_ident); let fn_generics = self.builder_struct_generics(|i| { if i == field_index { BuilderFieldState::Unfilled } else { BuilderFieldState::Generic } }); let (impl_generics, _, where_clause) = fn_generics.split_for_impl(); let unfilled_ty = self.builder_struct_ty(|i| { if i == field_index { BuilderFieldState::Unfilled } else { BuilderFieldState::Generic } }); let filled_ty = self.builder_struct_ty(|i| { if i == field_index { BuilderFieldState::Filled } else { BuilderFieldState::Generic } }); let pat_fields = Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| { let field_ident = field.ident.as_ref().unwrap(); if field_index == i { quote_spanned! {self.ident.span()=> #field_ident: _, } } else { quote_spanned! {self.ident.span()=> #field_ident, } } })); quote_spanned! {self.ident.span()=> #[automatically_derived] #[allow(non_camel_case_types, dead_code)] impl #impl_generics #unfilled_ty #where_clause { #vis fn #fn_ident( self, #field_ident: ::fayalite::expr::Expr<#ty>, ) -> #filled_ty { let Self { #phantom_field_name: _, #(#pat_fields)* } = self; #ident { #phantom_field_name: ::fayalite::__std::marker::PhantomData, #(#field_idents,)* } } } } .to_tokens(tokens); } let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled); let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled); let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl(); quote_spanned! {self.ident.span()=> #[automatically_derived] #[allow(non_camel_case_types, dead_code)] impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty #unfilled_where_clause { fn default() -> Self { #ident { #phantom_field_name: ::fayalite::__std::marker::PhantomData, #(#field_idents: ::fayalite::__std::default::Default::default(),)* } } } } .to_tokens(tokens); let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled); let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled); let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl(); let type_generics = self.generics.split_for_impl().1; quote_spanned! {self.ident.span()=> #[automatically_derived] #[allow(non_camel_case_types, dead_code)] impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty #filled_where_clause { type Type = #target #type_generics; fn to_expr( &self, ) -> ::fayalite::expr::Expr<::Type> { let __ty = #target { #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* }; let __field_values = [ #(::fayalite::expr::Expr::canonical(self.#field_idents),)* ]; ::fayalite::expr::ToExpr::to_expr( &::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values), ) } } } .to_tokens(tokens); } } impl ToTokens for ParsedBundle { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { attrs, options, vis, struct_token, ident, generics, fields, field_flips, mask_type_ident, mask_type_match_variant_ident, match_variant_ident, builder_ident, mask_type_builder_ident, } = self; let ItemOptions { outline_generated: _, connect_inexact, target, custom_bounds: _, no_static, } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); item_attrs.push(common_derives(ident.span())); ItemStruct { attrs: item_attrs, vis: vis.clone(), struct_token: *struct_token, ident: ident.clone(), generics: generics.into(), fields: Fields::Named(fields.clone().into()), semi_token: None, } .to_tokens(tokens); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); let tokens = wrapped_in_const.inner(); let builder = Builder { vis: vis.clone(), struct_token: *struct_token, ident: builder_ident.clone(), target: target.clone(), generics: generics.into(), fields: fields.clone().into(), }; builder.to_tokens(tokens); let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled); let mut mask_type_fields = FieldsNamed::from(fields.clone()); for Field { ident, ty, .. } in &mut mask_type_fields.named { let ident = ident.as_ref().unwrap(); *ty = parse_quote_spanned! {ident.span()=> <#ty as ::fayalite::ty::Type>::MaskType }; } let mask_type_builder = Builder { vis: vis.clone(), struct_token: *struct_token, ident: mask_type_builder_ident.clone(), target: mask_type_ident.clone().into(), generics: generics.into(), fields: mask_type_fields.clone(), }; mask_type_builder.to_tokens(tokens); let unfilled_mask_type_builder_ty = mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); let filled_mask_type_builder_ty = mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); ItemStruct { attrs: vec![ common_derives(ident.span()), parse_quote_spanned! {ident.span()=> #[allow(non_camel_case_types, dead_code)] }, ], vis: vis.clone(), struct_token: *struct_token, ident: mask_type_ident.clone(), generics: generics.into(), fields: Fields::Named(mask_type_fields.clone()), semi_token: None, } .to_tokens(tokens); let mut mask_type_match_variant_fields = mask_type_fields; for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named { let ident = ident.as_ref().unwrap(); *ty = parse_quote_spanned! {ident.span()=> ::fayalite::expr::Expr<#ty> }; } ItemStruct { attrs: vec![ common_derives(ident.span()), parse_quote_spanned! {ident.span()=> #[allow(non_camel_case_types, dead_code)] }, ], vis: vis.clone(), struct_token: *struct_token, ident: mask_type_match_variant_ident.clone(), generics: generics.into(), fields: Fields::Named(mask_type_match_variant_fields), semi_token: None, } .to_tokens(tokens); let mut match_variant_fields = FieldsNamed::from(fields.clone()); for Field { ident, ty, .. } in &mut match_variant_fields.named { let ident = ident.as_ref().unwrap(); *ty = parse_quote_spanned! {ident.span()=> ::fayalite::expr::Expr<#ty> }; } ItemStruct { attrs: vec![ common_derives(ident.span()), parse_quote_spanned! {ident.span()=> #[allow(non_camel_case_types, dead_code)] }, ], vis: vis.clone(), struct_token: *struct_token, ident: match_variant_ident.clone(), generics: generics.into(), fields: Fields::Named(match_variant_fields), semi_token: None, } .to_tokens(tokens); let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); quote_spanned! {ident.span()=> #ident: ::fayalite::expr::Expr::field(__this, #ident_str), } })); let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); quote_spanned! {ident.span()=> #ident: ::fayalite::ty::Type::mask_type(&self.#ident), } })); let from_canonical_body_fields = Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map( |((index, field), flip)| { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); let flipped = flip.is_some(); quote_spanned! {ident.span()=> #ident: { let ::fayalite::bundle::BundleField { name: __name, flipped: __flipped, ty: __ty, } = __fields[#index]; ::fayalite::__std::assert_eq!(&*__name, #ident_str); ::fayalite::__std::assert_eq!(__flipped, #flipped); ::fayalite::ty::Type::from_canonical(__ty) }, } }, )); let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map( |(field, flip)| { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); let flipped = flip.is_some(); quote_spanned! {ident.span()=> ::fayalite::bundle::BundleField { name: ::fayalite::intern::Intern::intern(#ident_str), flipped: #flipped, ty: ::fayalite::ty::Type::canonical(&self.#ident), }, } }, )); let fields_len = fields.named().into_iter().len(); quote_spanned! {ident.span()=> #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics #where_clause { type MaskType = #mask_type_ident #type_generics; type MatchVariant = #mask_type_match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< ::MatchVariant, >; type MatchVariantsIter = ::fayalite::__std::iter::Once< ::MatchVariantAndInactiveScope, >; fn match_variants( __this: ::fayalite::expr::Expr, __module_builder: &mut ::fayalite::module::ModuleBuilder< ::fayalite::module::NormalModule, >, __source_location: ::fayalite::source_location::SourceLocation, ) -> ::MatchVariantsIter { let __retval = #mask_type_match_variant_ident { #(#match_variant_body_fields)* }; ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) } fn mask_type(&self) -> ::MaskType { *self } fn canonical(&self) -> ::fayalite::ty::CanonicalType { ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) } #[track_caller] fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { ::fayalite::__std::panic!("expected bundle"); }; let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); Self { #(#from_canonical_body_fields)* } } fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics #where_clause { type Builder = #unfilled_mask_type_builder_ty; type FilledBuilder = #filled_mask_type_builder_ty; fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } } #[automatically_derived] impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics #where_clause { fn expr_deref(__this: &::fayalite::expr::Expr) -> &::MatchVariant { let __this = *__this; let __retval = #mask_type_match_variant_ident { #(#match_variant_body_fields)* }; ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #target #type_generics #where_clause { type MaskType = #mask_type_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< ::MatchVariant, >; type MatchVariantsIter = ::fayalite::__std::iter::Once< ::MatchVariantAndInactiveScope, >; fn match_variants( __this: ::fayalite::expr::Expr, __module_builder: &mut ::fayalite::module::ModuleBuilder< ::fayalite::module::NormalModule, >, __source_location: ::fayalite::source_location::SourceLocation, ) -> ::MatchVariantsIter { let __retval = #match_variant_ident { #(#match_variant_body_fields)* }; ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) } fn mask_type(&self) -> ::MaskType { #mask_type_ident { #(#mask_type_body_fields)* } } fn canonical(&self) -> ::fayalite::ty::CanonicalType { ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) } #[track_caller] fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { ::fayalite::__std::panic!("expected bundle"); }; let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); Self { #(#from_canonical_body_fields)* } } fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics #where_clause { type Builder = #unfilled_builder_ty; type FilledBuilder = #filled_builder_ty; fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } } #[automatically_derived] impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics #where_clause { fn expr_deref(__this: &::fayalite::expr::Expr) -> &::MatchVariant { let __this = *__this; let __retval = #match_variant_ident { #(#match_variant_body_fields)* }; ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } } .to_tokens(tokens); if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { let static_generics = generics.clone().for_static_type(); let (static_impl_generics, static_type_generics, static_where_clause) = static_generics.split_for_impl(); let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); quote_spanned! {ident.span()=> #ident: ::fayalite::ty::StaticType::static_type(), } })); quote_spanned! {ident.span()=> #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics #static_where_clause { fn static_type() -> Self { ::fayalite::ty::Type::mask_type( &<#target #static_type_generics as ::fayalite::ty::StaticType>::static_type(), ) } } #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics #static_where_clause { fn static_type() -> Self { Self { #(#static_type_body_fields)* } } } } .to_tokens(tokens); } } } pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result { let item = ParsedBundle::parse(item)?; let outline_generated = item.options.body.outline_generated; let mut contents = item.to_token_stream(); if outline_generated.is_some() { contents = crate::outline_generated(contents, "hdl-bundle-"); } Ok(contents) }