// SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ value_derive_common::{ append_field, derive_clone_hash_eq_partialeq_for_struct, get_target, make_connect_impl, Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics, }, Errors, HdlAttr, }; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ parse_quote, parse_quote_spanned, spanned::Spanned, FieldMutability, Generics, Ident, ItemStruct, Member, Path, Token, Visibility, }; crate::options! { #[options = StructOptions] pub(crate) enum StructOption { OutlineGenerated(outline_generated), FixedType(fixed_type), ConnectInexact(connect_inexact), Bounds(where_, Bounds), Target(target, Path), } } crate::options! { #[options = FieldOptions] pub(crate) enum FieldOption { Flip(flip), Skip(skip), } } pub(crate) struct ParsedStructNames { pub(crate) ident: Ident, pub(crate) type_struct_debug_ident: S, pub(crate) type_struct_ident: Ident, pub(crate) match_variant_ident: I, pub(crate) builder_struct_ident: I, pub(crate) mask_match_variant_ident: I, pub(crate) mask_type_ident: I, pub(crate) mask_type_debug_ident: S, pub(crate) mask_value_ident: I, pub(crate) mask_value_debug_ident: S, pub(crate) mask_builder_struct_ident: I, } pub(crate) struct ParsedStruct { pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) struct_token: Token![struct], pub(crate) generics: Generics, pub(crate) fields_kind: FieldsKind, pub(crate) fields: Vec>, pub(crate) semi_token: Option, pub(crate) skip_check_fields: bool, pub(crate) names: ParsedStructNames, Option>, } impl ParsedStruct { pub(crate) fn parse(item: &mut ItemStruct) -> syn::Result { let ItemStruct { attrs, vis, struct_token, ident, generics, fields, semi_token, } = item; let mut errors = Errors::new(); let struct_options = errors .unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)) .unwrap_or_default(); let (fields_kind, fields) = ParsedField::parse_fields(&mut errors, fields, false); errors.finish()?; Ok(ParsedStruct { options: struct_options, vis: vis.clone(), struct_token: *struct_token, generics: generics.clone(), fields_kind, fields, semi_token: *semi_token, skip_check_fields: false, names: ParsedStructNames { ident: ident.clone(), type_struct_debug_ident: None, type_struct_ident: format_ident!("__{}__Type", ident), match_variant_ident: None, builder_struct_ident: None, mask_match_variant_ident: None, mask_type_ident: None, mask_type_debug_ident: None, mask_value_ident: None, mask_value_debug_ident: None, mask_builder_struct_ident: None, }, }) } pub(crate) fn write_body( &self, target: Path, names: ParsedStructNames<&Ident, &String>, is_for_mask: bool, tokens: &mut TokenStream, ) { let Self { options, vis, struct_token, generics, fields_kind, fields, semi_token, skip_check_fields, names: _, } = self; let skip_check_fields = *skip_check_fields || is_for_mask; let ParsedStructNames { ident: struct_ident, type_struct_debug_ident, type_struct_ident, match_variant_ident, builder_struct_ident, mask_match_variant_ident: _, mask_type_ident, mask_type_debug_ident: _, mask_value_ident, mask_value_debug_ident, mask_builder_struct_ident: _, } = names; let StructOptions { outline_generated: _, where_, target: _, fixed_type, connect_inexact, } = &options.body; let ValueDeriveGenerics { generics, fixed_type_generics, } = ValueDeriveGenerics::get(generics.clone(), where_); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let unskipped_fields = fields .iter() .filter(|field| field.options.body.skip.is_none()); let _field_names = Vec::from_iter(fields.iter().map(|field| field.name.clone())); let unskipped_field_names = Vec::from_iter(unskipped_fields.clone().map(|field| field.name.clone())); let unskipped_field_name_strs = Vec::from_iter( unskipped_field_names .iter() .map(|field_name| field_name.to_token_stream().to_string()), ); let unskipped_field_vars = Vec::from_iter( unskipped_field_names .iter() .map(|field_name| format_ident!("__v_{}", field_name)), ); let unskipped_field_flips = Vec::from_iter( unskipped_fields .clone() .map(|field| field.options.body.flip.is_some()), ); let mut any_fields_skipped = false; let type_fields = Vec::from_iter(fields.iter().filter_map(|field| { let ParsedField { options, vis, name, ty, } = field; let FieldOptions { flip: _, skip } = &options.body; if skip.is_some() { any_fields_skipped = true; return None; } let ty = if is_for_mask { parse_quote! { ::fayalite::ty::AsMask<#ty> } } else { ty.to_token_stream() }; Some(syn::Field { attrs: vec![], vis: vis.clone(), mutability: FieldMutability::None, ident: match name.clone() { Member::Named(name) => Some(name), Member::Unnamed(_) => None, }, colon_token: None, ty: parse_quote! { <#ty as ::fayalite::expr::ToExpr>::Type }, }) })); let field_types = Vec::from_iter(type_fields.iter().map(|field| field.ty.clone())); let match_variant_fields = Vec::from_iter(fields.iter().zip(&type_fields).map( |(parsed_field, type_field)| { let field_ty = &parsed_field.ty; syn::Field { ty: parse_quote! { ::fayalite::expr::Expr<#field_ty> }, ..type_field.clone() } }, )); let mask_value_fields = Vec::from_iter(fields.iter().zip(&type_fields).map( |(parsed_field, type_field)| { let field_ty = &parsed_field.ty; syn::Field { ty: parse_quote! { ::fayalite::ty::AsMask<#field_ty> }, ..type_field.clone() } }, )); let mut type_struct_fields = fields_kind.into_fields(type_fields); let mut match_variant_fields = fields_kind.into_fields(match_variant_fields); let mut mask_value_fields = fields_kind.into_fields(mask_value_fields); let phantom_data_field_name = any_fields_skipped.then(|| { let phantom_data_field_name = Ident::new("__phantom_data", type_struct_ident.span()); let member = append_field( &mut type_struct_fields, syn::Field { attrs: vec![], vis: vis.clone(), mutability: FieldMutability::None, ident: Some(phantom_data_field_name.clone()), colon_token: None, ty: parse_quote_spanned! {type_struct_ident.span()=> ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> }, }, ); append_field( &mut match_variant_fields, syn::Field { attrs: vec![], vis: Visibility::Inherited, mutability: FieldMutability::None, ident: Some(phantom_data_field_name.clone()), colon_token: None, ty: parse_quote_spanned! {type_struct_ident.span()=> ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> }, }, ); append_field( &mut mask_value_fields, syn::Field { attrs: vec![], vis: Visibility::Inherited, mutability: FieldMutability::None, ident: Some(phantom_data_field_name), colon_token: None, ty: parse_quote_spanned! {type_struct_ident.span()=> ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> }, }, ); member }); let phantom_data_field_name_slice = phantom_data_field_name.as_slice(); let type_struct = ItemStruct { attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], vis: vis.clone(), struct_token: *struct_token, ident: type_struct_ident.clone(), generics: generics.clone(), fields: type_struct_fields, semi_token: *semi_token, }; type_struct.to_tokens(tokens); let match_variant_struct = ItemStruct { attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], vis: vis.clone(), struct_token: *struct_token, ident: match_variant_ident.clone(), generics: generics.clone(), fields: match_variant_fields, semi_token: *semi_token, }; match_variant_struct.to_tokens(tokens); let mask_type_body = if is_for_mask { quote! { ::fayalite::__std::clone::Clone::clone(self) } } else { let mask_value_struct = ItemStruct { attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], vis: vis.clone(), struct_token: *struct_token, ident: mask_value_ident.clone(), generics: generics.clone(), fields: mask_value_fields, semi_token: *semi_token, }; mask_value_struct.to_tokens(tokens); let debug_mask_value_body = match fields_kind { FieldsKind::Unit => quote! { f.debug_struct(#mask_value_debug_ident).finish() }, FieldsKind::Named(_) => quote! { f.debug_struct(#mask_value_debug_ident)#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*.finish() }, FieldsKind::Unnamed(_) => quote! { f.debug_tuple(#mask_value_debug_ident)#(.field(&self.#unskipped_field_names))*.finish() }, }; quote! { #[automatically_derived] impl #impl_generics ::fayalite::__std::fmt::Debug for #mask_value_ident #type_generics #where_clause { fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { #debug_mask_value_body } } }.to_tokens(tokens); quote! { #mask_type_ident { #(#unskipped_field_names: ::fayalite::ty::Type::mask_type(&self.#unskipped_field_names),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* } } }; let debug_type_body = match fields_kind { FieldsKind::Unit => quote! { f.debug_struct(#type_struct_debug_ident).finish() }, FieldsKind::Named(_) => quote! { f.debug_struct(#type_struct_debug_ident)#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*.finish() }, FieldsKind::Unnamed(_) => quote! { f.debug_tuple(#type_struct_debug_ident)#(.field(&self.#unskipped_field_names))*.finish() }, }; for the_struct_ident in [&type_struct_ident, match_variant_ident] .into_iter() .chain(is_for_mask.then_some(mask_value_ident)) { derive_clone_hash_eq_partialeq_for_struct( the_struct_ident, &generics, &Vec::from_iter( unskipped_field_names .iter() .cloned() .chain(phantom_data_field_name.clone()), ), ) .to_tokens(tokens); } let check_v = format_ident!("__v"); let field_checks = Vec::from_iter(fields.iter().map(|ParsedField { ty, name, .. }| { quote_spanned! {ty.span()=> __check_field(#check_v.#name); } })); if fixed_type.is_some() { let (impl_generics, type_generics, where_clause) = fixed_type_generics.split_for_impl(); quote! { #[automatically_derived] impl #impl_generics ::fayalite::ty::FixedType for #type_struct_ident #type_generics #where_clause { fn fixed_type() -> Self { Self { #(#unskipped_field_names: ::fayalite::ty::FixedType::fixed_type(),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* } } } } .to_tokens(tokens); } if !skip_check_fields { quote! { fn __check_field(_v: T) where ::Type: ::fayalite::ty::Type, {} fn __check_fields #impl_generics(#check_v: #target #type_generics) #where_clause { #(#field_checks)* } } .to_tokens(tokens); } let mut builder = Builder::new(builder_struct_ident.clone(), vis.clone()); for field in unskipped_fields.clone() { builder.insert_field( field.name.clone(), |v| { parse_quote_spanned! {v.span()=> ::fayalite::expr::ToExpr::to_expr(&#v) } }, |t| { parse_quote_spanned! {t.span()=> ::fayalite::expr::Expr<<<#t as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::Value> } }, |t| { parse_quote_spanned! {t.span()=> where #t: ::fayalite::expr::ToExpr, } }, ); } let builder = builder.finish_filling_in_fields(); builder.to_tokens(tokens); let build_type_fields = Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| { let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name; quote_spanned! {struct_ident.span()=> #name: ::fayalite::expr::ToExpr::ty(&#builder_field_name) } })); let build_expr_fields = Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| { let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name; quote_spanned! {struct_ident.span()=> #builder_field_name.to_canonical_dyn() } })); let build_specified_fields = unskipped_fields.clone().map( |ParsedField { options: _, vis: _, name, ty, }| { let ty = if is_for_mask { parse_quote_spanned! {name.span()=> ::fayalite::expr::Expr<::fayalite::ty::AsMask<#ty>> } } else { parse_quote_spanned! {name.span()=> ::fayalite::expr::Expr<#ty> } }; (name.clone(), ty) }, ); let build_body = parse_quote_spanned! {struct_ident.span()=> { ::fayalite::expr::ToExpr::to_expr( &::fayalite::expr::ops::BundleLiteral::new_unchecked( ::fayalite::intern::Intern::intern(&[#( #build_expr_fields, )*][..]), #type_struct_ident { #(#build_type_fields,)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* }, ), ) } }; builder .make_build_method( &Ident::new("build", struct_ident.span()), build_specified_fields, &generics, &parse_quote_spanned! {struct_ident.span()=> #type_struct_ident #type_generics }, &parse_quote_spanned! {struct_ident.span()=> ::fayalite::expr::Expr<#target #type_generics> }, build_body, ) .to_tokens(tokens); make_connect_impl( *connect_inexact, &generics, &type_struct_ident, unskipped_fields.clone().map(|field| { let ty = &field.ty; parse_quote_spanned! {field.name.span()=> <#ty as ::fayalite::expr::ToExpr>::Type } }), ) .to_tokens(tokens); let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false); quote! { #[automatically_derived] impl #impl_generics ::fayalite::__std::fmt::Debug for #type_struct_ident #type_generics #where_clause { fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { #debug_type_body } } #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics #where_clause { type CanonicalType = ::fayalite::bundle::DynBundleType; type Value = #target #type_generics; type CanonicalValue = ::fayalite::bundle::DynBundle; type MaskType = #mask_type_ident #type_generics; type MaskValue = #mask_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<#match_variant_ident #type_generics>; type MatchVariantsIter = ::fayalite::__std::iter::Once<::MatchVariantAndInactiveScope>; #[allow(unused_variables)] fn match_variants( this: ::fayalite::expr::Expr<::Value>, module_builder: &mut ::fayalite::module::ModuleBuilder, source_location: ::fayalite::source_location::SourceLocation, ) -> ::MatchVariantsIter where ::Type: ::fayalite::bundle::BundleType, { ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(#match_variant_ident { #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* })) } fn mask_type(&self) -> ::MaskType { #mask_type_body } fn canonical(&self) -> ::CanonicalType { let fields = ::fayalite::bundle::BundleType::fields(self); ::fayalite::bundle::DynBundleType::new(fields) } fn source_location(&self) -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } fn type_enum(&self) -> ::fayalite::ty::TypeEnum { ::fayalite::ty::TypeEnum::BundleType(::fayalite::ty::Type::canonical(self)) } fn from_canonical_type(t: ::CanonicalType) -> Self { let [#(#unskipped_field_vars),*] = *::fayalite::bundle::BundleType::fields(&t) else { ::fayalite::__std::panic!("wrong number of fields"); }; Self { #(#unskipped_field_names: #unskipped_field_vars.from_canonical_type_helper(#unskipped_field_name_strs, #unskipped_field_flips),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* } } } #[automatically_derived] impl #impl_generics ::fayalite::ty::TypeWithDeref for #type_struct_ident #type_generics #where_clause { #[allow(unused_variables)] fn expr_deref(this: &::fayalite::expr::Expr<::Value>) -> &::MatchVariant { ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized( #match_variant_ident { #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* } )) } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #type_struct_ident #type_generics #where_clause { type Builder = #empty_builder_ty; fn builder() -> ::Builder { #empty_builder_ty::new() } fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::FieldType<::fayalite::intern::Interned>]> { ::fayalite::intern::Intern::intern(&[#( ::fayalite::bundle::FieldType { name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs), flipped: #unskipped_field_flips, ty: ::fayalite::ty::DynType::canonical_dyn(&self.#unskipped_field_names), }, )*][..]) } fn fields_hint() -> ::fayalite::bundle::FieldsHint { ::fayalite::bundle::FieldsHint::new([#( ::fayalite::bundle::FieldType { name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs), flipped: #unskipped_field_flips, ty: ::fayalite::bundle::TypeHint::<#field_types>::intern_dyn(), }, )*], false) } } #[automatically_derived] impl #impl_generics ::fayalite::expr::ToExpr for #target #type_generics #where_clause { type Type = #type_struct_ident #type_generics; fn ty(&self) -> ::Type { #type_struct_ident { #(#unskipped_field_names: ::fayalite::expr::ToExpr::ty(&self.#unskipped_field_names),)* #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* } } fn to_expr(&self) -> ::fayalite::expr::Expr { ::fayalite::expr::Expr::from_value(self) } } #[automatically_derived] impl #impl_generics ::fayalite::ty::Value for #target #type_generics #where_clause { fn to_canonical(&self) -> <::Type as ::fayalite::ty::Type>::CanonicalValue { let ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self)); ::fayalite::bundle::DynBundle::new(ty, ::fayalite::__std::sync::Arc::new([ #(::fayalite::ty::DynValueTrait::to_canonical_dyn(&self.#unskipped_field_names),)* ])) } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleValue for #target #type_generics #where_clause { } } .to_tokens(tokens); } } impl ToTokens for ParsedStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let ParsedStructNames { ident: struct_ident, type_struct_debug_ident, type_struct_ident, match_variant_ident, builder_struct_ident, mask_match_variant_ident, mask_type_ident, mask_type_debug_ident, mask_value_ident, mask_value_debug_ident, mask_builder_struct_ident, } = &self.names; macro_rules! unwrap_or_set { ($(let $var:ident =? $fallback_value:expr;)*) => { $(let $var = $var.clone().unwrap_or_else(|| $fallback_value);)* }; } unwrap_or_set! { let type_struct_debug_ident =? format!("{struct_ident}::Type"); let match_variant_ident =? format_ident!("__{}__MatchVariant", struct_ident); let builder_struct_ident =? format_ident!("__{}__Builder", struct_ident); let mask_match_variant_ident =? format_ident!("__AsMask__{}__MatchVariant", struct_ident); let mask_type_ident =? format_ident!("__AsMask__{}__Type", struct_ident); let mask_type_debug_ident =? format!("AsMask<{struct_ident}>::Type"); let mask_value_ident =? format_ident!("__AsMask__{}", struct_ident); let mask_value_debug_ident =? format!("AsMask<{struct_ident}>"); let mask_builder_struct_ident =? format_ident!("__AsMask__{}__Builder", struct_ident); } let target = get_target(&self.options.body.target, struct_ident); let names = ParsedStructNames { ident: struct_ident.clone(), type_struct_debug_ident: &type_struct_debug_ident, type_struct_ident: type_struct_ident.clone(), match_variant_ident: &match_variant_ident, builder_struct_ident: &builder_struct_ident, mask_match_variant_ident: &mask_match_variant_ident, mask_type_ident: &mask_type_ident, mask_type_debug_ident: &mask_type_debug_ident, mask_value_ident: &mask_value_ident, mask_value_debug_ident: &mask_value_debug_ident, mask_builder_struct_ident: &mask_builder_struct_ident, }; self.write_body(target, names, false, tokens); let mask_names = ParsedStructNames { ident: mask_value_ident.clone(), type_struct_debug_ident: &mask_type_debug_ident, type_struct_ident: mask_type_ident.clone(), match_variant_ident: &mask_match_variant_ident, builder_struct_ident: &mask_builder_struct_ident, mask_match_variant_ident: &mask_match_variant_ident, mask_type_ident: &mask_type_ident, mask_type_debug_ident: &mask_type_debug_ident, mask_value_ident: &mask_value_ident, mask_value_debug_ident: &mask_value_debug_ident, mask_builder_struct_ident: &mask_builder_struct_ident, }; self.write_body(mask_value_ident.clone().into(), mask_names, true, tokens); } } pub(crate) fn value_derive_struct(mut item: ItemStruct) -> syn::Result { let item = ParsedStruct::parse(&mut item)?; let outline_generated = item.options.body.outline_generated; let mut contents = quote! { const _: () = { #item }; }; if outline_generated.is_some() { contents = crate::outline_generated(contents, "value-struct-"); } Ok(contents) }