forked from libre-chip/fayalite
775 lines
26 KiB
Rust
775 lines
26 KiB
Rust
// 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<WherePredicate, Token![,]>);
|
|
|
|
impl_fold! {
|
|
struct Bounds<>(Punctuated<WherePredicate, Token![,]>);
|
|
}
|
|
|
|
impl Parse for Bounds {
|
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
Ok(Bounds(Punctuated::parse_terminated(input)?))
|
|
}
|
|
}
|
|
|
|
impl From<Option<WhereClause>> for Bounds {
|
|
fn from(value: Option<WhereClause>) -> 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<O> {
|
|
pub(crate) options: HdlAttr<O>,
|
|
pub(crate) vis: Visibility,
|
|
pub(crate) name: Member,
|
|
pub(crate) ty: Type,
|
|
}
|
|
|
|
impl<O> ParsedField<O> {
|
|
pub(crate) fn var_name(&self) -> Ident {
|
|
format_ident!("__v_{}", self.name)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn get_field_name(
|
|
index: usize,
|
|
name: Option<Ident>,
|
|
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<Item = Member> + '_ {
|
|
fields
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, field)| get_field_name(index, field.ident.clone(), || field.ty.span()))
|
|
}
|
|
|
|
impl<O: Parse + Default> ParsedField<O> {
|
|
pub(crate) fn parse_fields(
|
|
errors: &mut Errors,
|
|
fields: &mut Fields,
|
|
in_enum: bool,
|
|
) -> (FieldsKind, Vec<ParsedField<O>>) {
|
|
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<Item = syn::Field>,
|
|
) -> Fields {
|
|
Fields::Named(FieldsNamed {
|
|
brace_token,
|
|
named: Punctuated::from_iter(fields),
|
|
})
|
|
}
|
|
pub(crate) fn into_fields_unnamed(
|
|
paren_token: Paren,
|
|
fields: impl IntoIterator<Item = syn::Field>,
|
|
) -> Fields {
|
|
Fields::Unnamed(FieldsUnnamed {
|
|
paren_token,
|
|
unnamed: Punctuated::from_iter(fields),
|
|
})
|
|
}
|
|
pub(crate) fn into_fields(self, fields: impl IntoIterator<Item = syn::Field>) -> 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<Value = #type_param>
|
|
});
|
|
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<Value = #type_param>
|
|
});
|
|
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<Name: ToTokens>(
|
|
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<Member>,
|
|
pub(crate) mapped_value: Expr,
|
|
pub(crate) mapped_type: Type,
|
|
pub(crate) where_clause: Option<WhereClause>,
|
|
pub(crate) builder_field_name: Ident,
|
|
pub(crate) type_param: Ident,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct Builder {
|
|
struct_name: Ident,
|
|
vis: Visibility,
|
|
fields: BTreeMap<String, BuilderField>,
|
|
}
|
|
|
|
#[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<WhereClause>,
|
|
) {
|
|
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<Item = (Member, Type)>,
|
|
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<Item = Member>,
|
|
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<Item = (Member, Type)>,
|
|
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<Ident, Ident>,
|
|
}
|
|
|
|
impl Fold for &MapIdents {
|
|
fn fold_ident(&mut self, i: Ident) -> Ident {
|
|
self.map.get(&i).cloned().unwrap_or(i)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct DupGenerics<M> {
|
|
pub(crate) combined: Generics,
|
|
pub(crate) maps: M,
|
|
}
|
|
|
|
pub(crate) fn merge_punctuated<T, P: Default>(
|
|
target: &mut Punctuated<T, P>,
|
|
source: Punctuated<T, P>,
|
|
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);
|
|
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
|
|
});
|
|
} else {
|
|
target.where_clause = Some(where_clause);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DupGenerics<Vec<MapIdents>> {
|
|
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<const COUNT: usize> 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,
|
|
predicates: Punctuated::new(),
|
|
});
|
|
if !predicates.empty_or_trailing() {
|
|
predicates.push_punct(Token);
|
|
}
|
|
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<Item = Type>,
|
|
) -> 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
|
|
{
|
|
}
|
|
}
|
|
}
|