// SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{fold::impl_fold, kw, Errors, HdlAttr}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::collections::{BTreeMap, HashMap, HashSet}; use syn::{ fold::{fold_generics, Fold}, parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::{Brace, Paren, Where}, Block, ConstParam, Expr, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics, Ident, Index, ItemImpl, Lifetime, LifetimeParam, Member, Path, Token, Type, TypeParam, TypePath, Visibility, WhereClause, WherePredicate, }; #[derive(Clone, Debug)] pub(crate) struct Bounds(pub(crate) Punctuated); impl_fold! { struct Bounds<>(Punctuated); } impl Parse for Bounds { fn parse(input: ParseStream) -> syn::Result { Ok(Bounds(Punctuated::parse_terminated(input)?)) } } impl From> for Bounds { fn from(value: Option) -> Self { Self(value.map_or_else(Punctuated::new, |v| v.predicates)) } } impl ToTokens for Bounds { fn to_tokens(&self, tokens: &mut TokenStream) { self.0.to_tokens(tokens) } } #[derive(Debug, Clone)] pub(crate) struct ParsedField { pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) name: Member, pub(crate) ty: Type, } impl ParsedField { pub(crate) fn var_name(&self) -> Ident { format_ident!("__v_{}", self.name) } } pub(crate) fn get_field_name( index: usize, name: Option, ty_span: impl FnOnce() -> Span, ) -> Member { match name { Some(name) => Member::Named(name), None => Member::Unnamed(Index { index: index as _, span: ty_span(), }), } } pub(crate) fn get_field_names(fields: &Fields) -> impl Iterator + '_ { fields .iter() .enumerate() .map(|(index, field)| get_field_name(index, field.ident.clone(), || field.ty.span())) } impl ParsedField { pub(crate) fn parse_fields( errors: &mut Errors, fields: &mut Fields, in_enum: bool, ) -> (FieldsKind, Vec>) { let mut unit_fields = Punctuated::new(); let (fields_kind, fields) = match fields { Fields::Named(fields) => (FieldsKind::Named(fields.brace_token), &mut fields.named), Fields::Unnamed(fields) => { (FieldsKind::Unnamed(fields.paren_token), &mut fields.unnamed) } Fields::Unit => (FieldsKind::Unit, &mut unit_fields), }; let fields = fields .iter_mut() .enumerate() .map(|(index, field)| { let options = errors .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut field.attrs)) .unwrap_or_default(); let name = get_field_name(index, field.ident.clone(), || field.ty.span()); if in_enum && !matches!(field.vis, Visibility::Inherited) { errors.error(&field.vis, "field visibility not allowed in enums"); } ParsedField { options, vis: field.vis.clone(), name, ty: field.ty.clone(), } }) .collect(); (fields_kind, fields) } } #[derive(Copy, Clone, Debug)] pub(crate) enum FieldsKind { Unit, Named(Brace), Unnamed(Paren), } impl FieldsKind { pub(crate) fn into_fields_named( brace_token: Brace, fields: impl IntoIterator, ) -> Fields { Fields::Named(FieldsNamed { brace_token, named: Punctuated::from_iter(fields), }) } pub(crate) fn into_fields_unnamed( paren_token: Paren, fields: impl IntoIterator, ) -> Fields { Fields::Unnamed(FieldsUnnamed { paren_token, unnamed: Punctuated::from_iter(fields), }) } pub(crate) fn into_fields(self, fields: impl IntoIterator) -> Fields { match self { FieldsKind::Unit => { let mut fields = fields.into_iter().peekable(); let Some(first_field) = fields.peek() else { return Fields::Unit; }; if first_field.ident.is_some() { Self::into_fields_named(Default::default(), fields) } else { Self::into_fields_unnamed(Default::default(), fields) } } FieldsKind::Named(brace_token) => Self::into_fields_named(brace_token, fields), FieldsKind::Unnamed(paren_token) => Self::into_fields_unnamed(paren_token, fields), } } } pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path { match target { Some((_, _, target)) => target.clone(), None => item_ident.clone().into(), } } pub(crate) struct ValueDeriveGenerics { pub(crate) generics: Generics, pub(crate) static_type_generics: Generics, } impl ValueDeriveGenerics { pub(crate) fn get(mut generics: Generics, where_: &Option<(Where, Paren, Bounds)>) -> Self { let mut static_type_generics = generics.clone(); if let Some((_, _, bounds)) = where_ { generics .make_where_clause() .predicates .extend(bounds.0.iter().cloned()); static_type_generics .where_clause .clone_from(&generics.where_clause); } else { let type_params = Vec::from_iter(generics.type_params().map(|v| v.ident.clone())); let predicates = &mut generics.make_where_clause().predicates; let static_type_predicates = &mut static_type_generics.make_where_clause().predicates; for type_param in type_params { predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value}); predicates.push(parse_quote! { <#type_param as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type }); static_type_predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value}); static_type_predicates.push(parse_quote! { <#type_param as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::StaticType }); static_type_predicates.push(parse_quote! { < <#type_param as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type >::MaskType: ::fayalite::ty::StaticType }); } } Self { generics, static_type_generics, } } } pub(crate) fn derive_clone_hash_eq_partialeq_for_struct( the_struct_ident: &Ident, generics: &Generics, field_names: &[Name], ) -> TokenStream { let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] impl #impl_generics ::fayalite::__std::clone::Clone for #the_struct_ident #type_generics #where_clause { fn clone(&self) -> Self { Self { #(#field_names: ::fayalite::__std::clone::Clone::clone(&self.#field_names),)* } } } #[automatically_derived] impl #impl_generics ::fayalite::__std::hash::Hash for #the_struct_ident #type_generics #where_clause { #[allow(unused_variables)] fn hash<__H: ::fayalite::__std::hash::Hasher>(&self, hasher: &mut __H) { #(::fayalite::__std::hash::Hash::hash(&self.#field_names, hasher);)* } } #[automatically_derived] impl #impl_generics ::fayalite::__std::cmp::Eq for #the_struct_ident #type_generics #where_clause { } #[automatically_derived] impl #impl_generics ::fayalite::__std::cmp::PartialEq for #the_struct_ident #type_generics #where_clause { #[allow(unused_variables)] #[allow(clippy::nonminimal_bool)] fn eq(&self, other: &Self) -> ::fayalite::__std::primitive::bool { true #(&& ::fayalite::__std::cmp::PartialEq::eq( &self.#field_names, &other.#field_names, ))* } } } } pub(crate) fn append_field(fields: &mut Fields, mut field: Field) -> Member { let ident = field.ident.clone().expect("ident is supplied"); match fields { Fields::Named(FieldsNamed { named, .. }) => { named.push(field); Member::Named(ident) } Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { field.ident = None; field.colon_token = None; let index = unnamed.len(); unnamed.push(field); Member::Unnamed(index.into()) } Fields::Unit => { *fields = Fields::Named(FieldsNamed { brace_token: Default::default(), named: Punctuated::from_iter([field]), }); Member::Named(ident) } } } #[derive(Clone, Debug)] pub(crate) struct BuilderField { pub(crate) names: HashSet, pub(crate) mapped_value: Expr, pub(crate) mapped_type: Type, pub(crate) where_clause: Option, pub(crate) builder_field_name: Ident, pub(crate) type_param: Ident, } #[derive(Debug)] pub(crate) struct Builder { struct_name: Ident, vis: Visibility, fields: BTreeMap, } #[derive(Debug)] pub(crate) struct BuilderWithFields { struct_name: Ident, vis: Visibility, phantom_type_param: Ident, phantom_type_field: Ident, fields: Vec<(String, BuilderField)>, } impl Builder { pub(crate) fn new(struct_name: Ident, vis: Visibility) -> Self { Self { struct_name, vis, fields: BTreeMap::new(), } } pub(crate) fn insert_field( &mut self, name: Member, map_value: impl FnOnce(&Ident) -> Expr, map_type: impl FnOnce(&Ident) -> Type, where_clause: impl FnOnce(&Ident) -> Option, ) { self.fields .entry(name.to_token_stream().to_string()) .or_insert_with_key(|name| { let builder_field_name = format_ident!("field_{}", name, span = self.struct_name.span()); let type_param = format_ident!("__T_{}", name, span = self.struct_name.span()); BuilderField { names: HashSet::new(), mapped_value: map_value(&builder_field_name), mapped_type: map_type(&type_param), where_clause: where_clause(&type_param), builder_field_name, type_param, } }) .names .insert(name); } pub(crate) fn finish_filling_in_fields(self) -> BuilderWithFields { let Self { struct_name, vis, fields, } = self; let fields = Vec::from_iter(fields); BuilderWithFields { phantom_type_param: Ident::new("__Phantom", struct_name.span()), phantom_type_field: Ident::new("__phantom", struct_name.span()), struct_name, vis, fields, } } } impl BuilderWithFields { pub(crate) fn get_field(&self, name: &Member) -> Option<(usize, &BuilderField)> { let index = self .fields .binary_search_by_key(&&*name.to_token_stream().to_string(), |v| &*v.0) .ok()?; Some((index, &self.fields[index].1)) } pub(crate) fn ty( &self, specified_fields: impl IntoIterator, phantom_type: Option<&Type>, other_fields_are_any_type: bool, ) -> TypePath { let Self { struct_name, vis: _, phantom_type_param, phantom_type_field: _, fields, } = self; let span = struct_name.span(); let mut arguments = Vec::from_iter(fields.iter().map(|(_, BuilderField { type_param, .. })| { if other_fields_are_any_type { parse_quote_spanned! {span=> #type_param } } else { parse_quote_spanned! {span=> () } } })); for (name, ty) in specified_fields { let Some((index, _)) = self.get_field(&name) else { panic!("field not found: {}", name.to_token_stream()); }; arguments[index] = ty; } let phantom_type_param = phantom_type.is_none().then_some(phantom_type_param); parse_quote_spanned! {span=> #struct_name::<#phantom_type_param #phantom_type #(, #arguments)*> } } pub(crate) fn append_generics( &self, specified_fields: impl IntoIterator, has_phantom_type_param: bool, other_fields_are_any_type: bool, generics: &mut Generics, ) { let Self { struct_name: _, vis: _, phantom_type_param, phantom_type_field: _, fields, } = self; if has_phantom_type_param { generics.params.push(GenericParam::from(TypeParam::from( phantom_type_param.clone(), ))); } if !other_fields_are_any_type { return; } let mut type_params = Vec::from_iter( fields .iter() .map(|(_, BuilderField { type_param, .. })| Some(type_param)), ); for name in specified_fields { let Some((index, _)) = self.get_field(&name) else { panic!("field not found: {}", name.to_token_stream()); }; type_params[index] = None; } generics.params.extend( type_params .into_iter() .filter_map(|v| Some(GenericParam::from(TypeParam::from(v?.clone())))), ); } pub(crate) fn make_build_method( &self, build_fn_name: &Ident, specified_fields: impl IntoIterator, generics: &Generics, phantom_type: &Type, return_ty: &Type, mut body: Block, ) -> ItemImpl { let Self { struct_name, vis, phantom_type_param: _, phantom_type_field, fields, } = self; let span = struct_name.span(); let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name)); let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); let empty_arg = parse_quote_spanned! {span=> () }; let mut ty_arguments = vec![empty_arg; fields.len()]; let empty_field_pat = quote_spanned! {span=> : _ }; let mut field_pats = vec![Some(empty_field_pat); fields.len()]; for (name, ty) in specified_fields { let Some((index, _)) = self.get_field(&name) else { panic!("field not found: {}", name.to_token_stream()); }; ty_arguments[index] = ty; field_pats[index] = None; } body.stmts.insert( 0, parse_quote_spanned! {span=> let Self { #(#field_names #field_pats,)* #phantom_type_field: _, } = self; }, ); parse_quote_spanned! {span=> #[automatically_derived] impl #impl_generics #struct_name<#phantom_type #(, #ty_arguments)*> #where_clause { #[allow(non_snake_case, dead_code)] #vis fn #build_fn_name(self) -> #return_ty #body } } } } impl ToTokens for BuilderWithFields { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { struct_name, vis, phantom_type_param, phantom_type_field, fields, } = self; let span = struct_name.span(); let mut any_generics = Generics::default(); self.append_generics([], true, true, &mut any_generics); let empty_ty = self.ty([], None, false); let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name)); let field_type_params = Vec::from_iter(fields.iter().map(|v| &v.1.type_param)); quote_spanned! {span=> #[allow(non_camel_case_types)] #[non_exhaustive] #vis struct #struct_name #any_generics { #(#field_names: #field_type_params,)* #phantom_type_field: ::fayalite::__std::marker::PhantomData<#phantom_type_param>, } #[automatically_derived] impl<#phantom_type_param> #empty_ty { fn new() -> Self { Self { #(#field_names: (),)* #phantom_type_field: ::fayalite::__std::marker::PhantomData, } } } } .to_tokens(tokens); for (field_index, (_, field)) in self.fields.iter().enumerate() { let initial_fields = &fields[..field_index]; let final_fields = &fields[field_index..][1..]; let initial_type_params = Vec::from_iter(initial_fields.iter().map(|v| &v.1.type_param)); let final_type_params = Vec::from_iter(final_fields.iter().map(|v| &v.1.type_param)); let initial_field_names = Vec::from_iter(initial_fields.iter().map(|v| &v.1.builder_field_name)); let final_field_names = Vec::from_iter(final_fields.iter().map(|v| &v.1.builder_field_name)); let BuilderField { names: _, mapped_value, mapped_type, where_clause, builder_field_name, type_param, } = field; quote_spanned! {span=> #[automatically_derived] #[allow(non_camel_case_types, dead_code)] impl<#phantom_type_param #(, #initial_type_params)* #(, #final_type_params)*> #struct_name< #phantom_type_param, #(#initial_type_params,)* (), #(#final_type_params,)* > { #vis fn #builder_field_name<#type_param>( self, #builder_field_name: #type_param, ) -> #struct_name< #phantom_type_param, #(#initial_type_params,)* #mapped_type, #(#final_type_params,)* > #where_clause { let Self { #(#initial_field_names,)* #builder_field_name: (), #(#final_field_names,)* #phantom_type_field: _, } = self; let #builder_field_name = #mapped_value; #struct_name { #(#field_names,)* #phantom_type_field: ::fayalite::__std::marker::PhantomData, } } } } .to_tokens(tokens); } } } pub(crate) struct MapIdents { pub(crate) map: HashMap, } impl Fold for &MapIdents { fn fold_ident(&mut self, i: Ident) -> Ident { self.map.get(&i).cloned().unwrap_or(i) } } pub(crate) struct DupGenerics { pub(crate) combined: Generics, pub(crate) maps: M, } pub(crate) fn merge_punctuated( target: &mut Punctuated, source: Punctuated, make_punct: impl FnOnce() -> P, ) { if source.is_empty() { return; } if target.is_empty() { *target = source; return; } if !target.trailing_punct() { target.push_punct(make_punct()); } target.extend(source.into_pairs()); } pub(crate) fn merge_generics(target: &mut Generics, source: Generics) { let Generics { lt_token, params, gt_token, where_clause, } = source; let span = lt_token.map(|v| v.span).unwrap_or_else(|| params.span()); target.lt_token = target.lt_token.or(lt_token); merge_punctuated(&mut target.params, params, || Token![,](span)); target.gt_token = target.gt_token.or(gt_token); if let Some(where_clause) = where_clause { if let Some(target_where_clause) = &mut target.where_clause { let WhereClause { where_token, predicates, } = where_clause; let span = where_token.span; target_where_clause.where_token = where_token; merge_punctuated(&mut target_where_clause.predicates, predicates, || { Token![,](span) }); } else { target.where_clause = Some(where_clause); } } } impl DupGenerics> { pub(crate) fn new_dyn(generics: &Generics, count: usize) -> Self { let mut maps = Vec::from_iter((0..count).map(|_| MapIdents { map: HashMap::new(), })); for param in &generics.params { let (GenericParam::Lifetime(LifetimeParam { lifetime: Lifetime { ident, .. }, .. }) | GenericParam::Type(TypeParam { ident, .. }) | GenericParam::Const(ConstParam { ident, .. })) = param; for (i, map_idents) in maps.iter_mut().enumerate() { map_idents .map .insert(ident.clone(), format_ident!("__{}_{}", ident, i)); } } let mut combined = Generics::default(); for map_idents in maps.iter() { merge_generics( &mut combined, fold_generics(&mut { map_idents }, generics.clone()), ); } Self { combined, maps } } } impl DupGenerics<[MapIdents; COUNT]> { pub(crate) fn new(generics: &Generics) -> Self { let DupGenerics { combined, maps } = DupGenerics::new_dyn(generics, COUNT); Self { combined, maps: maps.try_into().ok().unwrap(), } } } pub(crate) fn add_where_predicate( target: &mut Generics, span: Span, where_predicate: WherePredicate, ) { let WhereClause { where_token: _, predicates, } = target.where_clause.get_or_insert_with(|| WhereClause { where_token: Token![where](span), predicates: Punctuated::new(), }); if !predicates.empty_or_trailing() { predicates.push_punct(Token![,](span)); } predicates.push_value(where_predicate); } pub(crate) fn make_connect_impl( connect_inexact: Option<(crate::kw::connect_inexact,)>, generics: &Generics, ty_ident: &Ident, field_types: impl IntoIterator, ) -> TokenStream { let span = ty_ident.span(); let impl_generics; let combined_generics; let where_clause; let lhs_generics; let lhs_type_generics; let rhs_generics; let rhs_type_generics; if connect_inexact.is_some() { let DupGenerics { mut combined, maps: [lhs_map, rhs_map], } = DupGenerics::new(generics); for field_type in field_types { let lhs_type = (&lhs_map).fold_type(field_type.clone()); let rhs_type = (&rhs_map).fold_type(field_type); add_where_predicate( &mut combined, span, parse_quote_spanned! {span=> #lhs_type: ::fayalite::ty::Connect<#rhs_type> }, ); } combined_generics = combined; (impl_generics, _, where_clause) = combined_generics.split_for_impl(); lhs_generics = (&lhs_map).fold_generics(generics.clone()); (_, lhs_type_generics, _) = lhs_generics.split_for_impl(); rhs_generics = (&rhs_map).fold_generics(generics.clone()); (_, rhs_type_generics, _) = rhs_generics.split_for_impl(); } else { let mut generics = generics.clone(); for field_type in field_types { add_where_predicate( &mut generics, span, parse_quote_spanned! {span=> #field_type: ::fayalite::ty::Connect<#field_type> }, ); } combined_generics = generics; (impl_generics, lhs_type_generics, where_clause) = combined_generics.split_for_impl(); rhs_type_generics = lhs_type_generics.clone(); } quote_spanned! {span=> #[automatically_derived] #[allow(non_camel_case_types)] impl #impl_generics ::fayalite::ty::Connect<#ty_ident #rhs_type_generics> for #ty_ident #lhs_type_generics #where_clause { } } }