WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 4m56s

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 5835b995a9
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
63 changed files with 13500 additions and 13210 deletions

View file

@ -233,16 +233,20 @@ forward_fold!(syn::WherePredicate => fold_where_predicate);
no_op_fold!(syn::parse::Nothing);
no_op_fold!(syn::token::Brace);
no_op_fold!(syn::token::Bracket);
no_op_fold!(syn::token::Group);
no_op_fold!(syn::token::Paren);
no_op_fold!(syn::Token![_]);
no_op_fold!(syn::Token![,]);
no_op_fold!(syn::Token![;]);
no_op_fold!(syn::Token![:]);
no_op_fold!(syn::Token![::]);
no_op_fold!(syn::Token![..]);
no_op_fold!(syn::Token![.]);
no_op_fold!(syn::Token![#]);
no_op_fold!(syn::Token![<]);
no_op_fold!(syn::Token![=]);
no_op_fold!(syn::Token![=>]);
no_op_fold!(syn::Token![>]);
no_op_fold!(syn::Token![|]);
no_op_fold!(syn::Token![enum]);
no_op_fold!(syn::Token![extern]);
@ -251,3 +255,4 @@ no_op_fold!(syn::Token![mut]);
no_op_fold!(syn::Token![static]);
no_op_fold!(syn::Token![struct]);
no_op_fold!(syn::Token![where]);
no_op_fold!(usize);

View file

@ -0,0 +1,857 @@
use crate::{
hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField,
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<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct],
pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>,
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<HdlAttr<kw::flip>> {
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<Self> {
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::<ItemOptions>::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
errors.ok(options.body.validate());
let ItemOptions {
outline_generated: _,
target: _,
custom_bounds,
no_static: _,
no_runtime_generics: _,
} = 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 param in retval.params.iter_mut() {
match param {
GenericParam::Lifetime(_) => {}
GenericParam::Type(param) => param.default = None,
GenericParam::Const(param) => param.default = None,
}
}
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: impl ::fayalite::expr::ToExpr<Type = #ty>,
) -> #filled_ty {
let Self {
#phantom_field_name: _,
#(#pat_fields)*
} = self;
let #field_ident = ::fayalite::expr::ToExpr::to_expr(&#field_ident);
#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<<Self as ::fayalite::expr::ToExpr>::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,
::fayalite::intern::Intern::intern(&__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: _,
target,
custom_bounds: _,
no_static,
no_runtime_generics,
} = &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();
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
(generics, fields, no_runtime_generics)
{
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
let fields: Vec<_> = fields
.named
.iter()
.map(|ParsedField { ident, ty, .. }| {
let ident = ident.as_ref().unwrap();
let expr = ty.make_hdl_type_expr(context);
quote_spanned! {ident.span()=>
#ident: #expr,
}
})
.collect();
parse_quote_spanned! {ident.span()=>
#target {
#(#fields)*
}
}
})
}
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 BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #mask_type_match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
__this: ::fayalite::expr::Expr<Self>,
__source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let __retval = #mask_type_match_variant_ident {
#(#match_variant_body_fields)*
};
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::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)*][..])
}
}
impl #impl_generics #mask_type_ident #type_generics
#where_clause
{
#vis fn __bundle_builder() -> #unfilled_mask_type_builder_ty {
::fayalite::__std::default::Default::default()
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics
#where_clause
{
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::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 BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
__this: ::fayalite::expr::Expr<Self>,
__source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let __retval = #match_variant_ident {
#(#match_variant_body_fields)*
};
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::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)*][..])
}
}
impl #impl_generics #target #type_generics
#where_clause
{
#vis fn __bundle_builder() -> #unfilled_builder_ty {
::fayalite::__std::default::Default::default()
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics
#where_clause
{
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::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();
let ty = field.ty();
quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::TYPE,
}
}));
let static_mask_type_body_fields =
Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ty = field.ty();
quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
}
}));
let type_properties = format_ident!("__type_properties", span = ident.span());
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some();
let ty = field.ty();
quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES);
}
}));
let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some();
let ty = field.ty();
quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
}
}));
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
#static_where_clause
{
const TYPE: Self = Self {
#(#static_mask_type_body_fields)*
};
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = Self {
#(#static_mask_type_body_fields)*
};
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
#static_where_clause
{
const TYPE: Self = Self {
#(#static_type_body_fields)*
};
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = #mask_type_ident {
#(#static_mask_type_body_fields)*
};
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_fields)*
#type_properties.finish()
};
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
}
}
.to_tokens(tokens);
}
}
}
pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> {
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)
}

View file

@ -0,0 +1,656 @@
use crate::{
hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
ParsedType, SplitForImpl, TypesParser, WrappedInConst,
},
Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens};
use syn::{
parse_quote_spanned,
punctuated::{Pair, Punctuated},
token::{Brace, Paren},
Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident,
ItemEnum, ItemStruct, Token, Type, Variant, Visibility,
};
crate::options! {
#[options = VariantOptions]
pub(crate) enum VariantOption {}
}
crate::options! {
#[options = FieldOptions]
pub(crate) enum FieldOption {}
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedVariantField {
pub(crate) paren_token: Paren,
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<FieldOptions>,
pub(crate) ty: MaybeParsed<ParsedType, Type>,
pub(crate) comma_token: Option<Token![,]>,
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedVariant {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<VariantOptions>,
pub(crate) ident: Ident,
pub(crate) field: Option<ParsedVariantField>,
}
impl ParsedVariant {
fn parse(
errors: &mut Errors,
variant: Variant,
generics: &MaybeParsed<ParsedGenerics, Generics>,
) -> Self {
let Variant {
mut attrs,
ident,
fields,
discriminant,
} = variant;
let options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
let field = match fields {
Fields::Unnamed(FieldsUnnamed {
paren_token,
unnamed,
}) if unnamed.len() == 1 => {
let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple();
let Field {
mut attrs,
vis,
mutability,
ident: _,
colon_token: _,
ty,
} = field;
let options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
if !matches!(vis, Visibility::Inherited) {
errors.error(
&vis,
"enum variant fields must not have a visibility specifier",
);
}
if !matches!(mutability, FieldMutability::None) {
// FIXME: use mutability as the spanned tokens,
// blocked on https://github.com/dtolnay/syn/issues/1717
errors.error(&ty, "field mutability is not supported");
}
Some(ParsedVariantField {
paren_token,
attrs,
options,
ty: TypesParser::maybe_run(generics.as_ref(), ty, errors),
comma_token,
})
}
Fields::Unit => None,
Fields::Unnamed(fields) if fields.unnamed.is_empty() => None,
Fields::Named(fields) if fields.named.is_empty() => None,
Fields::Unnamed(_) | Fields::Named(_) => {
errors.error(
fields,
"enum variant must either have no fields or a single parenthesized field",
);
None
}
};
if let Some((eq, _)) = discriminant {
errors.error(eq, "custom enum discriminants are not allowed");
}
Self {
attrs,
options,
ident,
field,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedEnum {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility,
pub(crate) enum_token: Token![enum],
pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) brace_token: Brace,
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
pub(crate) match_variant_ident: Ident,
}
impl ParsedEnum {
fn parse(item: ItemEnum) -> syn::Result<Self> {
let ItemEnum {
mut attrs,
vis,
enum_token,
ident,
mut generics,
brace_token,
variants,
} = item;
let mut errors = Errors::new();
let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
errors.ok(options.body.validate());
let ItemOptions {
outline_generated: _,
target: _,
custom_bounds,
no_static: _,
no_runtime_generics: _,
} = options.body;
attrs.retain(|attr| {
if attr.path().is_ident("repr") {
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
false
} else {
true
}
});
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 variants = Punctuated::from_iter(
variants
.into_pairs()
.map_pair_value(|v| ParsedVariant::parse(&mut errors, v, &generics)),
);
errors.finish()?;
Ok(Self {
attrs,
options,
vis,
enum_token,
generics,
brace_token,
variants,
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
ident,
})
}
}
impl ToTokens for ParsedEnum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
options,
vis,
enum_token,
ident,
generics,
brace_token,
variants,
match_variant_ident,
} = self;
let ItemOptions {
outline_generated: _,
target,
custom_bounds: _,
no_static,
no_runtime_generics,
} = &options.body;
let target = get_target(target, ident);
let mut struct_attrs = attrs.clone();
struct_attrs.push(common_derives(ident.span()));
struct_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(non_snake_case)]
});
let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|ParsedVariant {
attrs: _,
options,
ident,
field,
}| {
let VariantOptions {} = options.body;
let colon_token;
let ty = if let Some(ParsedVariantField {
paren_token,
attrs: _,
options,
ty,
comma_token: _,
}) = field
{
let FieldOptions {} = options.body;
colon_token = Token![:](paren_token.span.open());
ty.clone().into()
} else {
colon_token = Token![:](ident.span());
parse_quote_spanned! {ident.span()=>
()
}
};
Field {
attrs: vec![],
vis: vis.clone(),
mutability: FieldMutability::None,
ident: Some(ident.clone()),
colon_token: Some(colon_token),
ty,
}
},
));
ItemStruct {
attrs: struct_attrs,
vis: vis.clone(),
struct_token: Token![struct](enum_token.span),
ident: ident.clone(),
generics: generics.into(),
fields: if struct_fields.is_empty() {
Fields::Unit
} else {
Fields::Named(FieldsNamed {
brace_token: *brace_token,
named: struct_fields,
})
},
semi_token: None,
}
.to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
let fields: Vec<_> = variants
.iter()
.map(|ParsedVariant { ident, field, .. }| {
if let Some(ParsedVariantField {
ty: MaybeParsed::Parsed(ty),
..
}) = field
{
let expr = ty.make_hdl_type_expr(context);
quote_spanned! {ident.span()=>
#ident: #expr,
}
} else {
quote_spanned! {ident.span()=>
#ident: (),
}
}
})
.collect();
parse_quote_spanned! {ident.span()=>
#target {
#(#fields)*
}
}
})
}
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner();
{
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner();
let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(dead_code)]
});
ItemEnum {
attrs: enum_attrs,
vis: vis.clone(),
enum_token: *enum_token,
ident: ident.clone(),
generics: generics.into(),
brace_token: *brace_token,
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|ParsedVariant {
attrs,
options: _,
ident,
field,
}| Variant {
attrs: attrs.clone(),
ident: ident.clone(),
fields: match field {
Some(ParsedVariantField {
paren_token,
attrs,
options: _,
ty,
comma_token,
}) => Fields::Unnamed(FieldsUnnamed {
paren_token: *paren_token,
unnamed: Punctuated::from_iter([Pair::new(
Field {
attrs: attrs.clone(),
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: None,
colon_token: None,
ty: ty.clone().into(),
},
*comma_token,
)]),
}),
None => Fields::Unit,
},
discriminant: None,
},
)),
}
.to_tokens(tokens);
}
let mut enum_attrs = attrs.clone();
enum_attrs.push(parse_quote_spanned! {ident.span()=>
#[allow(dead_code, non_camel_case_types)]
});
ItemEnum {
attrs: enum_attrs,
vis: vis.clone(),
enum_token: *enum_token,
ident: match_variant_ident.clone(),
generics: generics.into(),
brace_token: *brace_token,
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|ParsedVariant {
attrs,
options: _,
ident,
field,
}| Variant {
attrs: attrs.clone(),
ident: ident.clone(),
fields: match field {
Some(ParsedVariantField {
paren_token,
attrs,
options: _,
ty,
comma_token,
}) => Fields::Unnamed(FieldsUnnamed {
paren_token: *paren_token,
unnamed: Punctuated::from_iter([Pair::new(
Field {
attrs: attrs.clone(),
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: None,
colon_token: None,
ty: parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty>
},
},
*comma_token,
)]),
}),
None => Fields::Unit,
},
discriminant: None,
},
)),
}
.to_tokens(tokens);
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
if let Some(ParsedVariantField { ty, .. }) = field {
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics #target #type_generics
#where_clause
{
#[allow(non_snake_case, dead_code)]
#vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>(
self,
v: __V,
) -> ::fayalite::expr::Expr<Self> {
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::new_by_index(
self,
#index,
::fayalite::__std::option::Option::Some(
::fayalite::expr::Expr::canonical(
::fayalite::expr::ToExpr::to_expr(&v),
),
),
),
)
}
}
}
} else {
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics #target #type_generics
#where_clause
{
#[allow(non_snake_case, dead_code)]
#vis fn #ident(self) -> ::fayalite::expr::Expr<Self> {
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::new_by_index(
self,
#index,
::fayalite::__std::option::Option::None,
),
)
}
}
}
}
.to_tokens(tokens);
}
let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map(
|(index, ParsedVariant { ident, field, .. })| {
let ident_str = ident.to_string();
let val = if field.is_some() {
let missing_value_msg = format!("expected variant {ident} to have a field");
quote_spanned! {ident.span()=>
::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg))
}
} else {
quote_spanned! {ident.span()=>
::fayalite::__std::assert!(ty.is_none());
}
};
quote_spanned! {ident.span()=>
#ident: {
let ::fayalite::enum_::EnumVariant {
name,
ty,
} = variants[#index];
::fayalite::__std::assert_eq!(&*name, #ident_str);
#val
},
}
},
));
let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map(
|(index, ParsedVariant { ident, field, .. })| {
if field.is_some() {
quote_spanned! {ident.span()=>
#index => #match_variant_ident::#ident(
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::VariantAccess::new_by_index(
variant_access.base(),
variant_access.variant_index(),
),
),
),
}
} else {
quote_spanned! {ident.span()=>
#index => #match_variant_ident::#ident,
}
}
},
));
let variants_body_variants = Vec::from_iter(variants.iter().map(
|ParsedVariant {
attrs: _,
options,
ident,
field,
}| {
let VariantOptions {} = options.body;
let ident_str = ident.to_string();
match field {
Some(ParsedVariantField { options, .. }) => {
let FieldOptions {} = options.body;
quote_spanned! {ident.span()=>
::fayalite::enum_::EnumVariant {
name: ::fayalite::intern::Intern::intern(#ident_str),
ty: ::fayalite::__std::option::Option::Some(
::fayalite::ty::Type::canonical(&self.#ident),
),
},
}
}
None => quote_spanned! {ident.span()=>
::fayalite::enum_::EnumVariant {
name: ::fayalite::intern::Intern::intern(#ident_str),
ty: ::fayalite::__std::option::Option::None,
},
},
}
},
));
let variants_len = variants.len();
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
#where_clause
{
type BaseType = ::fayalite::enum_::Enum;
type MaskType = ::fayalite::int::Bool;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ::fayalite::module::Scope;
type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
fn match_variants(
this: ::fayalite::expr::Expr<Self>,
source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
::fayalite::module::enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
::fayalite::int::Bool
}
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self)))
}
#[track_caller]
#[allow(non_snake_case)]
fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self {
let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else {
::fayalite::__std::panic!("expected enum");
};
let variants = ::fayalite::enum_::EnumType::variants(&enum_);
::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants");
Self {
#(#from_canonical_body_fields)*
}
}
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics
#where_clause
{
fn match_activate_scope(
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
let (variant_access, scope) = v.activate();
(
match variant_access.variant_index() {
#(#match_active_scope_match_arms)*
#variants_len.. => ::fayalite::__std::panic!("invalid variant index"),
},
scope,
)
}
fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> {
::fayalite::intern::Intern::intern(&[
#(#variants_body_variants)*
][..])
}
}
}
.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_variants =
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
if let Some(_) = field {
quote_spanned! {ident.span()=>
#ident: ::fayalite::ty::StaticType::TYPE,
}
} else {
quote_spanned! {ident.span()=>
#ident: (),
}
}
}));
let type_properties = format_ident!("__type_properties", span = ident.span());
let type_properties_variants =
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
let variant = if let Some(ParsedVariantField { ty, .. }) = field {
quote_spanned! {ident.span()=>
::fayalite::__std::option::Option::Some(
<#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES,
)
}
} else {
quote_spanned! {ident.span()=>
::fayalite::__std::option::Option::None
}
};
quote_spanned! {ident.span()=>
let #type_properties = #type_properties.variant(#variant);
}
}));
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType
for #target #static_type_generics
#static_where_clause
{
const TYPE: Self = Self {
#(#static_type_body_variants)*
};
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType =
::fayalite::int::Bool;
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::enum_::EnumTypePropertiesBuilder::new();
#(#type_properties_variants)*
#type_properties.finish()
};
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties =
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
}
}
.to_tokens(tokens);
}
}
}
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
let item = ParsedEnum::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-enum-");
}
Ok(contents)
}

File diff suppressed because it is too large Load diff

View file

@ -13,15 +13,15 @@ use syn::{
};
mod fold;
mod hdl_bundle;
mod hdl_enum;
mod hdl_type_common;
mod module;
mod value_derive_common;
mod value_derive_enum;
mod value_derive_struct;
//mod value_derive_common;
//mod value_derive_struct;
mod kw {
pub(crate) use syn::token::{
Enum as enum_, Extern as extern_, Static as static_, Struct as struct_, Where as where_,
};
pub(crate) use syn::token::Extern as extern_;
macro_rules! custom_keyword {
($kw:ident) => {
@ -43,6 +43,7 @@ mod kw {
custom_keyword!(clock_domain);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
custom_keyword!(flip);
custom_keyword!(hdl);
custom_keyword!(input);
@ -52,6 +53,8 @@ mod kw {
custom_keyword!(memory_array);
custom_keyword!(memory_with_init);
custom_keyword!(no_reset);
custom_keyword!(no_runtime_generics);
custom_keyword!(no_static);
custom_keyword!(outline_generated);
custom_keyword!(output);
custom_keyword!(reg_builder);
@ -519,6 +522,26 @@ macro_rules! impl_extra_traits_for_options {
) => {
impl Copy for $option_enum_name {}
impl PartialEq for $option_enum_name {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl Eq for $option_enum_name {}
impl PartialOrd for $option_enum_name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $option_enum_name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.variant().cmp(&other.variant())
}
}
impl quote::IdentFragment for $option_enum_name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let _ = f;
@ -554,6 +577,66 @@ pub(crate) use impl_extra_traits_for_options;
macro_rules! options {
(
#[options = $options_name:ident]
$($tt:tt)*
) => {
crate::options! {
#[options = $options_name, punct = syn::Token![,], allow_duplicates = false]
$($tt)*
}
};
(
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true]
$(#[$($enum_meta:tt)*])*
$enum_vis:vis enum $option_enum_name:ident {
$($Variant:ident($key:ident $(, $value:ty)?),)*
}
) => {
crate::options! {
#[options = $options_name, punct = $Punct, allow_duplicates = (true)]
$(#[$($enum_meta)*])*
$enum_vis enum $option_enum_name {
$($Variant($key $(, $value)?),)*
}
}
impl Extend<$option_enum_name> for $options_name {
fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) {
iter.into_iter().for_each(|v| match v {
$($option_enum_name::$Variant(v) => {
self.$key = Some(v);
})*
});
}
}
impl FromIterator<$option_enum_name> for $options_name {
fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self {
let mut retval = Self::default();
retval.extend(iter);
retval
}
}
impl Extend<$options_name> for $options_name {
fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) {
iter.into_iter().for_each(|v| {
$(if let Some(v) = v.$key {
self.$key = Some(v);
})*
});
}
}
impl FromIterator<$options_name> for $options_name {
fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self {
let mut retval = Self::default();
retval.extend(iter);
retval
}
}
};
(
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr]
$(#[$($enum_meta:tt)*])*
$enum_vis:vis enum $option_enum_name:ident {
$($Variant:ident($key:ident $(, $value:ty)?),)*
@ -567,8 +650,11 @@ macro_rules! options {
}
#[derive(Clone, Debug, Default)]
#[allow(non_snake_case)]
$enum_vis struct $options_name {
$($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
$(
$enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
)*
}
crate::fold::impl_fold! {
@ -577,6 +663,43 @@ macro_rules! options {
}
}
const _: () = {
#[derive(Clone, Debug)]
$enum_vis struct Iter($enum_vis $options_name);
impl IntoIterator for $options_name {
type Item = $option_enum_name;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
Iter(self)
}
}
impl Iterator for Iter {
type Item = $option_enum_name;
fn next(&mut self) -> Option<Self::Item> {
$(
if let Some(value) = self.0.$key.take() {
return Some($option_enum_name::$Variant(value));
}
)*
None
}
#[allow(unused_mut, unused_variables)]
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
$(
if let Some(value) = self.0.$key.take() {
init = f(init, $option_enum_name::$Variant(value));
}
)*
init
}
}
};
impl syn::parse::Parse for $options_name {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
#![allow(unused_mut, unused_variables, unreachable_code)]
@ -585,7 +708,7 @@ macro_rules! options {
let old_input = input.fork();
match input.parse::<$option_enum_name>()? {
$($option_enum_name::$Variant(v) => {
if retval.$key.replace(v).is_some() {
if retval.$key.replace(v).is_some() && !$allow_duplicates {
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
}
})*
@ -593,7 +716,7 @@ macro_rules! options {
if input.is_empty() {
break;
}
input.parse::<syn::Token![,]>()?;
input.parse::<$Punct>()?;
}
Ok(retval)
}
@ -602,7 +725,7 @@ macro_rules! options {
impl quote::ToTokens for $options_name {
#[allow(unused_mut, unused_variables, unused_assignments)]
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let mut separator: Option<syn::Token![,]> = None;
let mut separator: Option<$Punct> = None;
$(if let Some(v) = &self.$key {
separator.to_tokens(tokens);
separator = Some(Default::default());
@ -673,6 +796,20 @@ macro_rules! options {
}
}
}
impl $option_enum_name {
#[allow(dead_code)]
fn variant(&self) -> usize {
#[repr(usize)]
enum Variant {
$($Variant,)*
__Last, // so it doesn't complain about zero-variant enums
}
match *self {
$(Self::$Variant(..) => Variant::$Variant as usize,)*
}
}
}
};
}
@ -686,6 +823,15 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
.suffix(".tmp.rs")
.tempfile_in(out_dir)
.unwrap();
struct PrintOnPanic<'a>(&'a TokenStream);
impl Drop for PrintOnPanic<'_> {
fn drop(&mut self) {
if std::thread::panicking() {
println!("{}", self.0);
}
}
}
let _print_on_panic = PrintOnPanic(&contents);
let contents = prettyplease::unparse(&parse_quote! { #contents });
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
let hash = base16ct::HexDisplay(&hash[..5]);
@ -706,7 +852,7 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
}
}
pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let options = syn::parse2::<module::ConfigOptions>(attr)?;
let options = HdlAttr::from(options);
let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
@ -717,14 +863,14 @@ pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream>
Ok(contents)
}
pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
let item = syn::parse2::<Item>(item)?;
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?;
match item {
Item::Enum(item) => value_derive_enum::value_derive_enum(item),
Item::Struct(item) => value_derive_struct::value_derive_struct(item),
Item::Enum(item) => hdl_enum::hdl_enum(item),
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
_ => Err(syn::Error::new(
Span::call_site(),
"derive(Value) can only be used on structs or enums",
"top-level #[hdl] can only be used on structs or enums",
)),
}
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
is_hdl_attr,
hdl_type_common::{ParsedGenerics, SplitForImpl},
module::transform_body::{HdlLet, HdlLetKindIO},
options, Errors, HdlAttr, PairsIterExt,
};
@ -57,13 +57,6 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
}
}
fn retain_struct_attrs<F: FnMut(&Attribute) -> bool>(item: &mut ItemStruct, mut f: F) {
item.attrs.retain(&mut f);
for field in item.fields.iter_mut() {
field.attrs.retain(&mut f);
}
}
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
pub(crate) struct ModuleFn {
@ -74,7 +67,7 @@ pub(crate) struct ModuleFn {
sig: Signature,
block: Box<Block>,
io: Vec<ModuleIO>,
struct_generics: Generics,
struct_generics: ParsedGenerics,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
@ -220,8 +213,16 @@ impl Parse for ModuleFn {
"return type not allowed here",
));
}
let body_results = errors.ok(transform_body::transform_body(module_kind, block));
let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics }));
let body_results = struct_generics.as_ref().and_then(|struct_generics| {
errors.ok(transform_body::transform_body(
module_kind,
block,
struct_generics,
))
});
errors.finish()?;
let struct_generics = struct_generics.unwrap();
let (block, io) = body_results.unwrap();
Ok(Self {
attrs,
@ -273,19 +274,18 @@ impl ModuleFn {
});
name
}));
let module_kind_ty = match module_kind {
ModuleKind::Extern => quote! { ::fayalite::module::ExternModule },
ModuleKind::Normal => quote! { ::fayalite::module::NormalModule },
let module_kind_value = match module_kind {
ModuleKind::Extern => quote! { ::fayalite::module::ModuleKind::Extern },
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
};
let fn_name = &outer_sig.ident;
let (_struct_impl_generics, struct_type_generics, struct_where_clause) =
struct_generics.split_for_impl();
let struct_ty = quote! {#fn_name #struct_type_generics};
body_sig.ident = parse_quote! {__body};
body_sig.inputs.insert(
0,
parse_quote! {m: &mut ::fayalite::module::ModuleBuilder<#struct_ty, #module_kind_ty>},
);
body_sig
.inputs
.insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder });
let body_fn = ItemFn {
attrs: vec![],
vis: Visibility::Inherited,
@ -310,34 +310,21 @@ impl ModuleFn {
let body_turbofish_type_generics = body_type_generics.as_turbofish();
let block = parse_quote! {{
#body_fn
::fayalite::module::ModuleBuilder::run(#fn_name_str, |m| __body #body_turbofish_type_generics(m, #(#param_names,)*))
::fayalite::module::ModuleBuilder::run(
#fn_name_str,
#module_kind_value,
|m| __body #body_turbofish_type_generics(m, #(#param_names,)*),
)
}};
let static_type = io.iter().all(|io| io.kind.ty_expr.is_none());
let struct_options = if static_type {
quote! { #[hdl(static)] }
} else {
quote! {}
};
let the_struct: ItemStruct = parse_quote! {
#[derive(::fayalite::__std::clone::Clone,
::fayalite::__std::hash::Hash,
::fayalite::__std::cmp::PartialEq,
::fayalite::__std::cmp::Eq,
::fayalite::__std::fmt::Debug)]
#[allow(non_camel_case_types)]
#struct_options
#[hdl(no_runtime_generics, no_static)]
#vis struct #fn_name #struct_generics #struct_where_clause {
#(
#io_flips
#vis #io_names: #io_types,)*
}
};
let mut struct_without_hdl_attrs = the_struct.clone();
let mut struct_without_derives = the_struct;
retain_struct_attrs(&mut struct_without_hdl_attrs, |attr| !is_hdl_attr(attr));
retain_struct_attrs(&mut struct_without_derives, |attr| {
!attr.path().is_ident("derive")
});
let outer_fn = ItemFn {
attrs,
vis,
@ -345,10 +332,7 @@ impl ModuleFn {
block,
};
let mut retval = outer_fn.into_token_stream();
struct_without_hdl_attrs.to_tokens(&mut retval);
retval.extend(
crate::value_derive_struct::value_derive_struct(struct_without_derives).unwrap(),
);
retval.extend(crate::hdl_bundle::hdl_bundle(the_struct).unwrap());
retval
}
}

View file

@ -2,11 +2,14 @@
// See Notices.txt for copyright information
use crate::{
fold::{impl_fold, DoFold},
hdl_type_common::{
known_items, ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser,
},
is_hdl_attr, kw,
module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind},
options, Errors, HdlAttr,
};
use num_bigint::{BigInt, Sign};
use num_bigint::BigInt;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use std::{borrow::Borrow, convert::Infallible};
@ -87,9 +90,9 @@ macro_rules! with_debug_clone_and_fold {
pub(crate) use with_debug_clone_and_fold;
with_debug_clone_and_fold! {
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind> {
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind, Ty = ParsedType> {
pub(crate) colon_token: Token![:],
pub(crate) ty: Box<Type>,
pub(crate) ty: Box<Ty>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) kind: Kind,
@ -98,6 +101,32 @@ with_debug_clone_and_fold! {
}
}
impl<Kind: Clone, T: ParseTypes<I>, I> ParseTypes<HdlLetKindIO<Kind, I>> for HdlLetKindIO<Kind, T> {
fn parse_types(
input: &mut HdlLetKindIO<Kind, I>,
parser: &mut TypesParser<'_>,
) -> Result<Self, ParseFailed> {
let HdlLetKindIO {
colon_token,
ty,
m,
dot_token,
kind,
paren,
ty_expr,
} = input;
Ok(Self {
colon_token: *colon_token,
ty: ParseTypes::parse_types(ty, parser)?,
m: *m,
dot_token: *dot_token,
kind: kind.clone(),
paren: *paren,
ty_expr: ty_expr.clone(),
})
}
}
pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result<Box<Expr>> {
let retval = input.parse()?;
let _: Option<Token![,]> = input.parse()?;
@ -145,17 +174,19 @@ impl<Kind: ToTokens> HdlLetKindToTokens for HdlLetKindIO<Kind> {
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindInstance {
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) instance: kw::instance,
pub(crate) paren: Paren,
pub(crate) module: Box<Expr>,
}
impl ParseTypes<Self> for HdlLetKindInstance {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindInstance<> {
m: kw::m,
dot_token: Token![.],
instance: kw::instance,
paren: Paren,
module: Box<Expr>,
@ -167,14 +198,10 @@ impl HdlLetKindToTokens for HdlLetKindInstance {
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
m,
dot_token,
instance,
paren,
module,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
instance.to_tokens(tokens);
paren.surround(tokens, |tokens| module.to_tokens(tokens));
}
@ -381,19 +408,21 @@ make_builder_method_enum! {
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindRegBuilder {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) reg_builder: kw::reg_builder,
pub(crate) reg_builder_paren: Paren,
pub(crate) clock_domain: Option<RegBuilderClockDomain>,
pub(crate) reset: RegBuilderReset,
}
impl ParseTypes<Self> for HdlLetKindRegBuilder {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindRegBuilder<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
reg_builder: kw::reg_builder,
reg_builder_paren: Paren,
clock_domain: Option<RegBuilderClockDomain>,
@ -406,10 +435,10 @@ impl HdlLetKindRegBuilder {
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
m_dot: Option<(kw::m, Token![.])>,
reg_builder: kw::reg_builder,
) -> syn::Result<Self> {
check_empty_m_dot(m_dot, reg_builder)?;
let _reg_builder_paren_inner;
let reg_builder_paren = parenthesized!(_reg_builder_paren_inner in input);
let mut clock_domain = None;
@ -430,8 +459,6 @@ impl HdlLetKindRegBuilder {
}
Ok(Self {
ty: parsed_ty,
m,
dot_token,
reg_builder,
reg_builder_paren,
clock_domain,
@ -451,15 +478,11 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
reg_builder,
reg_builder_paren,
clock_domain,
reset,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
reg_builder.to_tokens(tokens);
reg_builder_paren.surround(tokens, |_tokens| {});
clock_domain.to_tokens(tokens);
@ -470,18 +493,20 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindWire {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) wire: kw::wire,
pub(crate) paren: Paren,
pub(crate) ty_expr: Option<Box<Expr>>,
}
impl ParseTypes<Self> for HdlLetKindWire {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindWire<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
wire: kw::wire,
paren: Paren,
ty_expr: Option<Box<Expr>>,
@ -499,14 +524,10 @@ impl HdlLetKindToTokens for HdlLetKindWire {
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
wire,
paren,
ty_expr,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
wire.to_tokens(tokens);
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
}
@ -627,16 +648,18 @@ impl ToTokens for MemoryFn {
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindMemory {
pub(crate) ty: Option<(Token![:], Box<Type>)>,
pub(crate) m: kw::m,
pub(crate) dot_token: Token![.],
pub(crate) memory_fn: MemoryFn,
}
impl ParseTypes<Self> for HdlLetKindMemory {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindMemory<> {
ty: Option<(Token![:], Box<Type>)>,
m: kw::m,
dot_token: Token![.],
memory_fn: MemoryFn,
}
}
@ -650,14 +673,7 @@ impl HdlLetKindToTokens for HdlLetKindMemory {
}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self {
ty: _,
m,
dot_token,
memory_fn,
} = self;
m.to_tokens(tokens);
dot_token.to_tokens(tokens);
let Self { ty: _, memory_fn } = self;
memory_fn.to_tokens(tokens);
}
}
@ -667,22 +683,20 @@ impl HdlLetKindMemory {
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
_after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
m_dot: Option<(kw::m, Token![.])>,
memory_fn_name: MemoryFnName,
) -> syn::Result<Self> {
check_empty_m_dot(m_dot, memory_fn_name)?;
Ok(Self {
ty: parsed_ty,
m,
dot_token,
memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?,
})
}
}
#[derive(Clone, Debug)]
pub(crate) enum HdlLetKind {
IO(HdlLetKindIO),
pub(crate) enum HdlLetKind<IOType = ParsedType> {
IO(HdlLetKindIO<ModuleIOKind, IOType>),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
@ -690,8 +704,8 @@ pub(crate) enum HdlLetKind {
}
impl_fold! {
enum HdlLetKind<> {
IO(HdlLetKindIO),
enum HdlLetKind<IOType,> {
IO(HdlLetKindIO<ModuleIOKind, IOType>),
Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire),
@ -699,6 +713,27 @@ impl_fold! {
}
}
impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
fn parse_types(
input: &mut HdlLetKind<I>,
parser: &mut TypesParser<'_>,
) -> Result<Self, ParseFailed> {
match input {
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
HdlLetKind::Instance(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance)
}
HdlLetKind::RegBuilder(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::RegBuilder)
}
HdlLetKind::Wire(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::Wire),
HdlLetKind::Memory(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Memory)
}
}
}
}
fn parsed_ty_or_err(
parsed_ty: Option<(Token![:], Box<Type>)>,
after_ty: Token![=],
@ -710,15 +745,15 @@ fn parsed_ty_or_err(
}
}
impl HdlLetKindIO {
impl HdlLetKindIO<ModuleIOKind, Type> {
fn rest_of_parse(
input: ParseStream,
parsed_ty: Option<(Token![:], Box<Type>)>,
after_ty: Token![=],
m: kw::m,
dot_token: Token![.],
m_dot: Option<(kw::m, Token![.])>,
kind: ModuleIOKind,
) -> syn::Result<Self> {
let (m, dot_token) = unwrap_m_dot(m_dot, kind)?;
let (colon_token, ty) = parsed_ty_or_err(parsed_ty, after_ty)?;
let paren_contents;
Ok(Self {
@ -733,7 +768,36 @@ impl HdlLetKindIO {
}
}
impl HdlLetKindParse for HdlLetKind {
fn check_empty_m_dot(m_dot: Option<(kw::m, Token![.])>, kind: impl ToTokens) -> syn::Result<()> {
if let Some((m, dot_token)) = m_dot {
Err(Error::new_spanned(
quote! {#m #dot_token #kind},
format_args!(
"{} is a free function, not a method of ModuleBuilder: try removing the `m.`",
kind.to_token_stream()
),
))
} else {
Ok(())
}
}
fn unwrap_m_dot(
m_dot: Option<(kw::m, Token![.])>,
kind: impl ToTokens,
) -> syn::Result<(kw::m, Token![.])> {
m_dot.ok_or_else(|| {
Error::new_spanned(
&kind,
format_args!(
"{} is a ModuleBuilder method, not a free function: try prefixing it with `m.`",
kind.to_token_stream()
),
)
})
}
impl HdlLetKindParse for HdlLetKind<Type> {
type ParsedTy = Option<(Token![:], Box<Type>)>;
fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy> {
@ -753,16 +817,20 @@ impl HdlLetKindParse for HdlLetKind {
after_ty: Token![=],
input: ParseStream,
) -> syn::Result<Self> {
let m = input.parse()?;
let dot_token = input.parse()?;
let m_dot = if input.peek(kw::m) && input.peek2(Token![.]) {
let m = input.parse()?;
let dot_token = input.parse()?;
Some((m, dot_token))
} else {
None
};
let kind: LetFnKind = input.parse()?;
match kind {
LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
ModuleIOKind::Input(input_token),
)
.map(Self::IO),
@ -770,8 +838,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
ModuleIOKind::Output(output),
)
.map(Self::IO),
@ -782,30 +849,23 @@ impl HdlLetKindParse for HdlLetKind {
"type annotation not allowed for instance",
));
}
check_empty_m_dot(m_dot, kind)?;
let paren_contents;
Ok(Self::Instance(HdlLetKindInstance {
m,
dot_token,
instance,
paren: parenthesized!(paren_contents in input),
module: paren_contents.call(parse_single_fn_arg)?,
}))
}
LetFnKind::RegBuilder((reg_builder,)) => HdlLetKindRegBuilder::rest_of_parse(
input,
parsed_ty,
after_ty,
m,
dot_token,
reg_builder,
)
.map(Self::RegBuilder),
LetFnKind::RegBuilder((reg_builder,)) => {
HdlLetKindRegBuilder::rest_of_parse(input, parsed_ty, after_ty, m_dot, reg_builder)
.map(Self::RegBuilder)
}
LetFnKind::Wire((wire,)) => {
check_empty_m_dot(m_dot, wire)?;
let paren_contents;
Ok(Self::Wire(HdlLetKindWire {
ty: parsed_ty,
m,
dot_token,
wire,
paren: parenthesized!(paren_contents in input),
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
@ -815,8 +875,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::Memory(fn_name),
)
.map(Self::Memory),
@ -824,8 +883,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::MemoryArray(fn_name),
)
.map(Self::Memory),
@ -833,8 +891,7 @@ impl HdlLetKindParse for HdlLetKind {
input,
parsed_ty,
after_ty,
m,
dot_token,
m_dot,
MemoryFnName::MemoryWithInit(fn_name),
)
.map(Self::Memory),
@ -878,6 +935,34 @@ with_debug_clone_and_fold! {
}
}
impl<T: ParseTypes<I>, I> ParseTypes<HdlLet<I>> for HdlLet<T> {
fn parse_types(
input: &mut HdlLet<I>,
parser: &mut TypesParser<'_>,
) -> Result<Self, ParseFailed> {
let HdlLet {
attrs,
hdl_attr,
let_token,
mut_token,
name,
eq_token,
kind,
semi_token,
} = input;
Ok(Self {
attrs: attrs.clone(),
hdl_attr: hdl_attr.clone(),
let_token: *let_token,
mut_token: *mut_token,
name: name.clone(),
eq_token: *eq_token,
kind: T::parse_types(kind, parser)?,
semi_token: *semi_token,
})
}
}
impl<Kind> HdlLet<Kind> {
pub(crate) fn try_map<Kind2, E>(
self,
@ -1006,7 +1091,7 @@ fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
fn unwrap_or_static_type(expr: Option<impl ToTokens>, span: Span) -> TokenStream {
expr.map(ToTokens::into_token_stream).unwrap_or_else(|| {
quote_spanned! {span=>
::fayalite::ty::StaticType::static_type()
::fayalite::ty::StaticType::TYPE
}
})
}
@ -1026,14 +1111,15 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
}
}
struct Visitor {
struct Visitor<'a> {
module_kind: ModuleKind,
errors: Errors,
io: Vec<ModuleIO>,
block_depth: usize,
parsed_generics: &'a ParsedGenerics,
}
impl Visitor {
impl Visitor<'_> {
fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> {
self.errors.unwrap_or(
HdlAttr::parse_and_take_attr(attrs),
@ -1086,7 +1172,7 @@ impl Visitor {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let mut __scope = ::fayalite::module::if_(#cond);
let _: () = #then_branch;
let mut __scope = __scope.else_();
let _: () = #else_expr;
@ -1096,7 +1182,7 @@ impl Visitor {
parse_quote_spanned! {if_token.span=>
#(#attrs)*
{
let mut __scope = m.if_(#cond);
let mut __scope = ::fayalite::module::if_(#cond);
let _: () = #then_branch;
}
}
@ -1157,8 +1243,6 @@ impl Visitor {
eq_token,
kind:
HdlLetKindInstance {
m,
dot_token,
instance,
paren,
module,
@ -1166,7 +1250,7 @@ impl Visitor {
semi_token,
} = hdl_let;
self.require_normal_module(instance);
let mut expr = quote! {#m #dot_token #instance};
let mut expr = instance.to_token_stream();
paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name: &name,
@ -1191,11 +1275,9 @@ impl Visitor {
}
fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let reg_builder = hdl_let.kind.reg_builder;
self.require_normal_module(reg_builder);
let mut expr = quote! {#m #dot #reg_builder};
let mut expr = reg_builder.to_token_stream();
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
@ -1244,12 +1326,10 @@ impl Visitor {
}
fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let wire = hdl_let.kind.wire;
self.require_normal_module(wire);
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span());
let mut expr = quote! {#m #dot #wire};
let mut expr = wire.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| {
let name_str = ImplicitName {
name,
@ -1279,18 +1359,19 @@ impl Visitor {
}
fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local {
let name = &hdl_let.name;
let m = hdl_let.kind.m;
let dot = hdl_let.kind.dot_token;
let memory_fn = hdl_let.kind.memory_fn;
let memory_fn_name = memory_fn.name();
self.require_normal_module(memory_fn_name);
let mut expr = quote! {#m #dot #memory_fn_name};
let mut expr = memory_fn_name.to_token_stream();
let (paren, arg) = match memory_fn {
MemoryFn::Memory {
memory,
paren,
ty_expr,
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())),
} => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
),
MemoryFn::MemoryArray {
memory_array,
paren,
@ -1377,16 +1458,17 @@ impl Visitor {
let value: BigInt = self
.errors
.ok(base10_digits.parse().map_err(|e| Error::new(span, e)))?;
let (negative, bytes) = match value.sign() {
Sign::Minus => (true, value.magnitude().to_bytes_le()),
Sign::NoSign => (false, vec![]),
Sign::Plus => (false, value.magnitude().to_bytes_le()),
let bytes = value.to_signed_bytes_le();
let path = if signed {
known_items::SInt(span).path
} else {
known_items::UInt(span).path
};
Some(parse_quote_spanned! {span=>
::fayalite::int::make_int_literal::<#signed, #width>(
#negative,
<#path<#width> as ::fayalite::int::BoolOrIntType>::le_bytes_to_expr_wrapping(
&[#(#bytes,)*],
).to_int_expr()
#width,
)
})
}
fn process_literal(&mut self, literal: ExprLit) -> Expr {
@ -1449,7 +1531,7 @@ fn empty_let() -> Local {
}
}
impl Fold for Visitor {
impl Fold for Visitor<'_> {
fn fold_item(&mut self, item: Item) -> Item {
// don't process item interiors
item
@ -1521,8 +1603,6 @@ impl Fold for Visitor {
Repeat => process_hdl_repeat,
Struct => process_hdl_struct,
Tuple => process_hdl_tuple,
Call => process_hdl_call,
Path => process_hdl_path,
}
}
}
@ -1536,11 +1616,16 @@ impl Fold for Visitor {
Some(None) => return fold_local(self, let_stmt),
Some(Some(HdlAttr { .. })) => {}
};
let hdl_let = syn::parse2::<HdlLet>(let_stmt.into_token_stream());
let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream());
let Some(hdl_let) = self.errors.ok(hdl_let) else {
return empty_let();
};
let hdl_let = hdl_let.do_fold(self);
let mut hdl_let = hdl_let.do_fold(self);
let Ok(hdl_let) =
TypesParser::run_with_errors(self.parsed_generics, &mut hdl_let, &mut self.errors)
else {
return empty_let();
};
self.process_hdl_let(hdl_let)
}
@ -1563,12 +1648,14 @@ impl Fold for Visitor {
pub(crate) fn transform_body(
module_kind: ModuleKind,
mut body: Box<Block>,
parsed_generics: &ParsedGenerics,
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
let mut visitor = Visitor {
module_kind,
errors: Errors::new(),
io: vec![],
block_depth: 0,
parsed_generics,
};
*body = syn::fold::fold_block(&mut visitor, *body);
visitor.errors.finish()?;

View file

@ -1,336 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{module::transform_body::Visitor, options, Errors, HdlAttr, PairsIterExt};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
use crate::{module::transform_body::Visitor, HdlAttr};
use quote::{format_ident, quote_spanned};
use syn::{
parse::Nothing,
parse_quote, parse_quote_spanned,
punctuated::{Pair, Punctuated},
spanned::Spanned,
token::{Brace, Paren},
Attribute, Expr, ExprArray, ExprCall, ExprGroup, ExprPath, ExprRepeat, ExprStruct, ExprTuple,
FieldValue, Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath,
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath,
};
options! {
#[options = AggregateLiteralOptions]
#[no_ident_fragment]
pub(crate) enum AggregateLiteralOption {
Struct(struct_),
Enum(enum_),
}
}
#[derive(Clone, Debug)]
pub(crate) struct StructOrEnumPath {
pub(crate) ty: TypePath,
pub(crate) variant: Option<(TypePath, Ident)>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct SingleSegmentVariant {
pub(crate) name: &'static str,
pub(crate) make_type_path: fn(Span, &PathArguments) -> Path,
}
impl StructOrEnumPath {
pub(crate) const SINGLE_SEGMENT_VARIANTS: &'static [SingleSegmentVariant] = {
fn make_option_type_path(span: Span, arguments: &PathArguments) -> Path {
let arguments = if arguments.is_none() {
quote_spanned! {span=>
<_>
}
} else {
arguments.to_token_stream()
};
parse_quote_spanned! {span=>
::fayalite::__std::option::Option #arguments
}
}
fn make_result_type_path(span: Span, arguments: &PathArguments) -> Path {
let arguments = if arguments.is_none() {
quote_spanned! {span=>
<_, _>
}
} else {
arguments.to_token_stream()
};
parse_quote_spanned! {span=>
::fayalite::__std::result::Result #arguments
}
}
&[
SingleSegmentVariant {
name: "Some",
make_type_path: make_option_type_path,
},
SingleSegmentVariant {
name: "None",
make_type_path: make_option_type_path,
},
SingleSegmentVariant {
name: "Ok",
make_type_path: make_result_type_path,
},
SingleSegmentVariant {
name: "Err",
make_type_path: make_result_type_path,
},
]
};
pub(crate) fn new(
errors: &mut Errors,
path: TypePath,
options: &AggregateLiteralOptions,
) -> Result<Self, ()> {
let Path {
leading_colon,
segments,
} = &path.path;
let qself_position = path.qself.as_ref().map(|qself| qself.position).unwrap_or(0);
let variant_name = if qself_position < segments.len() {
Some(segments.last().unwrap().ident.clone())
} else {
None
};
let enum_type = 'guess_enum_type: {
if options.enum_.is_some() {
if let Some((struct_,)) = options.struct_ {
errors.error(
struct_,
"can't specify both #[hdl(enum)] and #[hdl(struct)]",
);
}
break 'guess_enum_type Some(None);
}
if options.struct_.is_some() {
break 'guess_enum_type None;
}
if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 {
let PathSegment { ident, arguments } = &segments[0];
for &SingleSegmentVariant {
name,
make_type_path,
} in Self::SINGLE_SEGMENT_VARIANTS
{
if ident == name {
break 'guess_enum_type Some(Some(TypePath {
qself: None,
path: make_type_path(ident.span(), arguments),
}));
}
}
}
if segments.len() == qself_position + 2
&& segments[qself_position + 1].arguments.is_none()
&& (path.qself.is_some()
|| segments[qself_position].ident.to_string().as_bytes()[0]
.is_ascii_uppercase())
{
let mut ty = path.clone();
ty.path.segments.pop();
ty.path.segments.pop_punct();
break 'guess_enum_type Some(Some(ty));
}
None
};
if let Some(enum_type) = enum_type {
let ty = if let Some(enum_type) = enum_type {
enum_type
} else {
if qself_position >= segments.len() {
errors.error(path, "#[hdl]: can't figure out enum's type");
return Err(());
}
let mut ty = path.clone();
ty.path.segments.pop();
ty.path.segments.pop_punct();
ty
};
let Some(variant_name) = variant_name else {
errors.error(path, "#[hdl]: can't figure out enum's variant name");
return Err(());
};
Ok(Self {
ty,
variant: Some((path, variant_name)),
})
} else {
Ok(Self {
ty: path,
variant: None,
})
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum BraceOrParen {
Brace(Brace),
Paren(Paren),
}
impl BraceOrParen {
pub(crate) fn surround(self, tokens: &mut TokenStream, f: impl FnOnce(&mut TokenStream)) {
match self {
BraceOrParen::Brace(v) => v.surround(tokens, f),
BraceOrParen::Paren(v) => v.surround(tokens, f),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct StructOrEnumLiteralField {
pub(crate) attrs: Vec<Attribute>,
pub(crate) member: Member,
pub(crate) colon_token: Option<Token![:]>,
pub(crate) expr: Expr,
}
#[derive(Debug, Clone)]
pub(crate) struct StructOrEnumLiteral {
pub(crate) attrs: Vec<Attribute>,
pub(crate) path: TypePath,
pub(crate) brace_or_paren: BraceOrParen,
pub(crate) fields: Punctuated<StructOrEnumLiteralField, Token![,]>,
pub(crate) dot2_token: Option<Token![..]>,
pub(crate) rest: Option<Box<Expr>>,
}
impl StructOrEnumLiteral {
pub(crate) fn map_field_exprs(self, mut f: impl FnMut(Expr) -> Expr) -> Self {
self.map_fields(|mut field| {
field.expr = f(field.expr);
field
})
}
pub(crate) fn map_fields(
self,
f: impl FnMut(StructOrEnumLiteralField) -> StructOrEnumLiteralField,
) -> Self {
let Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
} = self;
let fields = fields.into_pairs().map_pair_value(f).collect();
Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
}
}
}
impl From<ExprStruct> for StructOrEnumLiteral {
fn from(value: ExprStruct) -> Self {
let ExprStruct {
attrs,
qself,
path,
brace_token,
fields,
dot2_token,
rest,
} = value;
Self {
attrs,
path: TypePath { qself, path },
brace_or_paren: BraceOrParen::Brace(brace_token),
fields: fields
.into_pairs()
.map_pair_value(
|FieldValue {
attrs,
member,
colon_token,
expr,
}| StructOrEnumLiteralField {
attrs,
member,
colon_token,
expr,
},
)
.collect(),
dot2_token,
rest,
}
}
}
fn expr_to_member(expr: &Expr) -> Option<Member> {
syn::parse2(expr.to_token_stream()).ok()
}
impl ToTokens for StructOrEnumLiteral {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
path,
brace_or_paren,
fields,
dot2_token,
rest,
} = self;
tokens.append_all(attrs);
path.to_tokens(tokens);
brace_or_paren.surround(tokens, |tokens| {
match brace_or_paren {
BraceOrParen::Brace(_) => {
for (
StructOrEnumLiteralField {
attrs,
member,
mut colon_token,
expr,
},
comma,
) in fields.pairs().map(|v| v.into_tuple())
{
tokens.append_all(attrs);
if Some(member) != expr_to_member(expr).as_ref() {
colon_token = Some(<Token![:]>::default());
}
member.to_tokens(tokens);
colon_token.to_tokens(tokens);
expr.to_tokens(tokens);
comma.to_tokens(tokens);
}
}
BraceOrParen::Paren(_) => {
for (
StructOrEnumLiteralField {
attrs,
member: _,
colon_token: _,
expr,
},
comma,
) in fields.pairs().map(|v| v.into_tuple())
{
tokens.append_all(attrs);
expr.to_tokens(tokens);
comma.to_tokens(tokens);
}
}
}
if let Some(rest) = rest {
dot2_token.unwrap_or_default().to_tokens(tokens);
rest.to_tokens(tokens);
}
});
}
}
impl Visitor {
impl Visitor<'_> {
pub(crate) fn process_hdl_array(
&mut self,
hdl_attr: HdlAttr<Nothing>,
@ -356,100 +33,61 @@ impl Visitor {
};
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)}
}
pub(crate) fn process_struct_enum(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
mut literal: StructOrEnumLiteral,
) -> Expr {
let span = hdl_attr.hdl.span;
if let Some(rest) = literal.rest.take() {
self.errors
.error(rest, "#[hdl] struct functional update syntax not supported");
}
let mut next_var = 0usize;
let mut new_var = || -> Ident {
let retval = format_ident!("__v{}", next_var, span = span);
next_var += 1;
retval
};
let infallible_var = new_var();
let retval_var = new_var();
let mut lets = vec![];
let mut build_steps = vec![];
let literal = literal.map_field_exprs(|expr| {
let field_var = new_var();
lets.push(quote_spanned! {span=>
let #field_var = ::fayalite::expr::ToExpr::to_expr(&#expr);
});
parse_quote! { #field_var }
});
let Ok(StructOrEnumPath { ty, variant }) =
StructOrEnumPath::new(&mut self.errors, literal.path.clone(), &hdl_attr.body)
else {
return parse_quote_spanned! {span=>
{}
};
};
for StructOrEnumLiteralField {
attrs: _,
member,
colon_token: _,
expr,
} in literal.fields.iter()
{
let field_fn = format_ident!("field_{}", member);
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.#field_fn(#expr);
});
}
let check_literal = literal.map_field_exprs(|expr| {
parse_quote_spanned! {span=>
::fayalite::expr::value_from_expr_type(#expr, #infallible_var)
}
});
let make_expr_fn = if let Some((_variant_path, variant_ident)) = &variant {
let variant_fn = format_ident!("variant_{}", variant_ident);
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.#variant_fn();
});
quote_spanned! {span=>
::fayalite::expr::make_enum_expr
}
} else {
build_steps.push(quote_spanned! {span=>
let #retval_var = #retval_var.build();
});
quote_spanned! {span=>
::fayalite::expr::make_bundle_expr
}
};
let variant_or_type =
variant.map_or_else(|| ty.clone(), |(variant_path, _variant_ident)| variant_path);
parse_quote_spanned! {span=>
{
#(#lets)*
#make_expr_fn::<#ty>(|#infallible_var| {
let #retval_var = #check_literal;
#[allow(unreachable_code)]
match #retval_var {
#variant_or_type { .. } => #retval_var,
#[allow(unreachable_patterns)]
_ => match #infallible_var {},
}
}, |#retval_var| {
#(#build_steps)*
#retval_var
})
}
}
}
pub(crate) fn process_hdl_struct(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
hdl_attr: HdlAttr<Nothing>,
expr_struct: ExprStruct,
) -> Expr {
self.require_normal_module(&hdl_attr);
self.process_struct_enum(hdl_attr, expr_struct.into())
let name_span = expr_struct.path.segments.last().unwrap().ident.span();
let builder_ident = format_ident!("__builder", span = name_span);
let empty_builder = if expr_struct.qself.is_some()
|| expr_struct
.path
.segments
.iter()
.any(|seg| !seg.arguments.is_none())
{
let ty = TypePath {
qself: expr_struct.qself,
path: expr_struct.path,
};
let builder_ty = quote_spanned! {name_span=>
<#ty as ::fayalite::bundle::BundleType>::Builder
};
quote_spanned! {name_span=>
<#builder_ty as ::fayalite::__std::default::Default>::default()
}
} else {
let path = ExprPath {
attrs: vec![],
qself: expr_struct.qself,
path: expr_struct.path,
};
quote_spanned! {name_span=>
#path::__bundle_builder()
}
};
let field_calls = Vec::from_iter(expr_struct.fields.iter().map(
|FieldValue {
attrs: _,
member,
colon_token: _,
expr,
}| {
let field_fn = format_ident!("field_{}", member);
quote_spanned! {member.span()=>
let #builder_ident = #builder_ident.#field_fn(#expr);
}
},
));
parse_quote_spanned! {name_span=>
{
let #builder_ident = #empty_builder;
#(#field_calls)*
::fayalite::expr::ToExpr::to_expr(&#builder_ident)
}
}
}
pub(crate) fn process_hdl_tuple(
&mut self,
@ -461,80 +99,4 @@ impl Visitor {
::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
}
}
pub(crate) fn process_hdl_path(
&mut self,
hdl_attr: HdlAttr<Nothing>,
expr_path: ExprPath,
) -> Expr {
self.require_normal_module(hdl_attr);
parse_quote_spanned! {expr_path.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_path)
}
}
pub(crate) fn process_hdl_call(
&mut self,
hdl_attr: HdlAttr<AggregateLiteralOptions>,
expr_call: ExprCall,
) -> Expr {
self.require_normal_module(&hdl_attr);
let ExprCall {
attrs: mut literal_attrs,
func,
paren_token,
args,
} = expr_call;
let mut path_expr = *func;
let path = loop {
break match path_expr {
Expr::Group(ExprGroup {
attrs,
group_token: _,
expr,
}) => {
literal_attrs.extend(attrs);
path_expr = *expr;
continue;
}
Expr::Path(ExprPath { attrs, qself, path }) => {
literal_attrs.extend(attrs);
TypePath { qself, path }
}
_ => {
self.errors.error(&path_expr, "missing tuple struct's name");
return parse_quote_spanned! {path_expr.span()=>
{}
};
}
};
};
let fields = args
.into_pairs()
.enumerate()
.map(|(index, p)| {
let (expr, comma) = p.into_tuple();
let mut index = Index::from(index);
index.span = hdl_attr.hdl.span;
Pair::new(
StructOrEnumLiteralField {
attrs: vec![],
member: Member::Unnamed(index),
colon_token: None,
expr,
},
comma,
)
})
.collect();
self.process_struct_enum(
hdl_attr,
StructOrEnumLiteral {
attrs: literal_attrs,
path,
brace_or_paren: BraceOrParen::Paren(paren_token),
fields,
dot2_token: None,
rest: None,
},
)
}
}

View file

@ -2,23 +2,20 @@
// See Notices.txt for copyright information
use crate::{
fold::impl_fold,
module::transform_body::{
expand_aggregate_literals::{AggregateLiteralOptions, StructOrEnumPath},
with_debug_clone_and_fold, Visitor,
},
module::transform_body::{with_debug_clone_and_fold, Visitor},
Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use quote::{format_ident, ToTokens, TokenStreamExt};
use syn::{
fold::{fold_arm, fold_expr_match, fold_pat, Fold},
parse::Nothing,
parse_quote_spanned,
punctuated::{Pair, Punctuated},
punctuated::Punctuated,
spanned::Spanned,
token::{Brace, Paren},
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Index, Member, Pat, PatIdent, PatOr,
PatParen, PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, Token, TypePath,
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen,
PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath,
};
with_debug_clone_and_fold! {
@ -81,7 +78,7 @@ impl ToTokens for MatchPatWild {
with_debug_clone_and_fold! {
struct MatchPatStructField<> {
member: Member,
field_name: Ident,
colon_token: Option<Token![:]>,
pat: MatchPatSimple,
}
@ -90,12 +87,19 @@ with_debug_clone_and_fold! {
impl ToTokens for MatchPatStructField {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
member,
field_name,
colon_token,
pat,
} = self;
member.to_tokens(tokens);
colon_token.to_tokens(tokens);
field_name.to_tokens(tokens);
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
if field_name == ident {
return;
}
}
colon_token
.unwrap_or_else(|| Token![:](field_name.span()))
.to_tokens(tokens);
pat.to_tokens(tokens);
}
}
@ -108,8 +112,16 @@ impl MatchPatStructField {
colon_token,
pat,
} = field_pat;
let field_name = if let Member::Named(field_name) = member {
field_name
} else {
state
.errors
.error(&member, "field name must not be a number");
format_ident!("_{}", member)
};
Ok(Self {
member,
field_name,
colon_token,
pat: MatchPatSimple::parse(state, *pat)?,
})
@ -118,7 +130,7 @@ impl MatchPatStructField {
with_debug_clone_and_fold! {
struct MatchPatStruct<> {
resolved_path: Path,
path: Path,
brace_token: Brace,
fields: Punctuated<MatchPatStructField, Token![,]>,
rest: Option<Token![..]>,
@ -128,12 +140,12 @@ with_debug_clone_and_fold! {
impl ToTokens for MatchPatStruct {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
resolved_path,
path,
brace_token,
fields,
rest,
} = self;
resolved_path.to_tokens(tokens);
path.to_tokens(tokens);
brace_token.surround(tokens, |tokens| {
fields.to_tokens(tokens);
rest.to_tokens(tokens);
@ -141,6 +153,30 @@ impl ToTokens for MatchPatStruct {
}
}
with_debug_clone_and_fold! {
struct MatchPatEnumVariant<> {
variant_path: Path,
enum_path: Path,
variant_name: Ident,
field: Option<(Paren, MatchPatSimple)>,
}
}
impl ToTokens for MatchPatEnumVariant {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
variant_path,
enum_path: _,
variant_name: _,
field,
} = self;
variant_path.to_tokens(tokens);
if let Some((paren_token, field)) = field {
paren_token.surround(tokens, |tokens| field.to_tokens(tokens));
}
}
}
#[derive(Debug, Clone)]
enum MatchPatSimple {
Paren(MatchPatParen<MatchPatSimple>),
@ -169,21 +205,70 @@ impl ToTokens for MatchPatSimple {
}
}
fn is_pat_ident_a_struct_or_enum_name(ident: &Ident) -> bool {
ident
.to_string()
.starts_with(|ch: char| ch.is_ascii_uppercase())
struct EnumPath {
variant_path: Path,
enum_path: Path,
variant_name: Ident,
}
fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> {
let TypePath {
qself: None,
path: variant_path,
} = variant_path
else {
return Err(variant_path);
};
if variant_path.is_ident("HdlNone") || variant_path.is_ident("HdlSome") {
let ident = variant_path.get_ident().unwrap();
return Ok(EnumPath {
enum_path: parse_quote_spanned! {ident.span()=>
::fayalite::enum_::HdlOption::<_>
},
variant_name: ident.clone(),
variant_path,
});
}
if variant_path.segments.len() < 2 {
return Err(TypePath {
qself: None,
path: variant_path,
});
}
let mut enum_path = variant_path.clone();
let PathSegment {
ident: variant_name,
arguments,
} = enum_path.segments.pop().unwrap().into_value();
if !arguments.is_none() {
return Err(TypePath {
qself: None,
path: variant_path,
});
}
enum_path.segments.pop_punct();
Ok(EnumPath {
variant_path,
enum_path,
variant_name,
})
}
fn parse_enum_ident(ident: Ident) -> Result<EnumPath, Ident> {
parse_enum_path(TypePath {
qself: None,
path: ident.into(),
})
.map_err(|p| p.path.segments.into_iter().next().unwrap().ident)
}
trait ParseMatchPat: Sized {
fn simple(v: MatchPatSimple) -> Self;
fn or(v: MatchPatOr<Self>) -> Self;
fn paren(v: MatchPatParen<Self>) -> Self;
fn struct_(
state: &mut HdlMatchParseState<'_>,
v: MatchPatStruct,
struct_error_spanned: &dyn ToTokens,
) -> Result<Self, ()>;
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>;
fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant)
-> Result<Self, ()>;
fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
match pat {
Pat::Ident(PatIdent {
@ -208,26 +293,23 @@ trait ParseMatchPat: Sized {
.errors
.error(at_token, "@ not allowed in #[hdl] patterns");
}
if is_pat_ident_a_struct_or_enum_name(&ident) {
let ident_span = ident.span();
let resolved_path = state.resolve_enum_struct_path(TypePath {
qself: None,
path: ident.clone().into(),
})?;
Self::struct_(
match parse_enum_ident(ident) {
Ok(EnumPath {
variant_path,
enum_path,
variant_name,
}) => Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace(ident_span),
fields: Punctuated::new(),
rest: None,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: None,
},
&ident,
)
} else {
Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
),
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
ident,
})))
}))),
}
}
Pat::Or(PatOr {
@ -254,18 +336,21 @@ trait ParseMatchPat: Sized {
qself,
path,
}) => {
let path = TypePath { qself, path };
let path_span = path.span();
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
Self::struct_(
let EnumPath {
variant_path,
enum_path,
variant_name,
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
state.errors.error(path, "unsupported enum variant path");
})?;
Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace(path_span),
fields: Punctuated::new(),
rest: None,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: None,
},
&path,
)
}
Pat::Struct(PatStruct {
@ -282,12 +367,16 @@ trait ParseMatchPat: Sized {
MatchPatStructField::parse(state, field_pat).ok()
})
.collect();
let path = TypePath { qself, path };
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
if qself.is_some() {
state
.errors
.error(TypePath { qself, path }, "unsupported struct path");
return Err(());
}
Self::struct_(
state,
MatchPatStruct {
resolved_path,
path,
brace_token,
fields,
rest: rest.map(
@ -297,7 +386,6 @@ trait ParseMatchPat: Sized {
}| dot2_token,
),
},
&path,
)
}
Pat::TupleStruct(PatTupleStruct {
@ -307,45 +395,44 @@ trait ParseMatchPat: Sized {
paren_token,
mut elems,
}) => {
let rest = if let Some(&Pat::Rest(PatRest {
attrs: _,
dot2_token,
})) = elems.last()
{
elems.pop();
Some(dot2_token)
} else {
None
};
let fields = elems
.into_pairs()
.enumerate()
.filter_map(|(index, pair)| {
let (pat, punct) = pair.into_tuple();
let pat = MatchPatSimple::parse(state, pat).ok()?;
let mut index = Index::from(index);
index.span = state.span;
let field = MatchPatStructField {
member: index.into(),
colon_token: Some(Token![:](state.span)),
pat,
};
Some(Pair::new(field, punct))
let EnumPath {
variant_path,
enum_path,
variant_name,
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
state.errors.error(path, "unsupported enum variant path");
})?;
if elems.is_empty() {
let mut tokens = TokenStream::new();
paren_token.surround(&mut tokens, |_| {});
state.errors.error(
tokens,
"field-less enum variants must not be matched using parenthesis",
);
}
if elems.len() != 1 {
state.errors.error(
variant_path,
"enum variant pattern must have exactly one field",
);
return Err(());
}
let field = elems.pop().unwrap().into_value();
let field = if let Pat::Rest(rest) = field {
MatchPatSimple::Wild(MatchPatWild {
underscore_token: Token![_](rest.dot2_token.span()),
})
.collect();
let path = TypePath { qself, path };
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
Self::struct_(
} else {
MatchPatSimple::parse(state, field)?
};
Self::enum_variant(
state,
MatchPatStruct {
resolved_path,
brace_token: Brace {
span: paren_token.span,
},
fields,
rest,
MatchPatEnumVariant {
variant_path,
enum_path,
variant_name,
field: Some((paren_token, field)),
},
&path,
)
}
Pat::Rest(_) => {
@ -387,14 +474,21 @@ impl ParseMatchPat for MatchPatSimple {
Self::Paren(v)
}
fn struct_(
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
state.errors.error(
v.path,
"matching structs is not yet implemented inside structs/enums in #[hdl] patterns",
);
Err(())
}
fn enum_variant(
state: &mut HdlMatchParseState<'_>,
_v: MatchPatStruct,
struct_error_spanned: &dyn ToTokens,
v: MatchPatEnumVariant,
) -> Result<Self, ()> {
state.errors.error(
struct_error_spanned,
"not yet implemented inside structs/enums in #[hdl] patterns",
v.variant_path,
"matching enum variants is not yet implemented inside structs/enums in #[hdl] patterns",
);
Err(())
}
@ -406,6 +500,7 @@ enum MatchPat {
Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct),
EnumVariant(MatchPatEnumVariant),
}
impl_fold! {
@ -414,6 +509,7 @@ impl_fold! {
Or(MatchPatOr<MatchPat>),
Paren(MatchPatParen<MatchPat>),
Struct(MatchPatStruct),
EnumVariant(MatchPatEnumVariant),
}
}
@ -430,13 +526,16 @@ impl ParseMatchPat for MatchPat {
Self::Paren(v)
}
fn struct_(
_state: &mut HdlMatchParseState<'_>,
v: MatchPatStruct,
_struct_error_spanned: &dyn ToTokens,
) -> Result<Self, ()> {
fn struct_(_state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
Ok(Self::Struct(v))
}
fn enum_variant(
_state: &mut HdlMatchParseState<'_>,
v: MatchPatEnumVariant,
) -> Result<Self, ()> {
Ok(Self::EnumVariant(v))
}
}
impl ToTokens for MatchPat {
@ -446,6 +545,7 @@ impl ToTokens for MatchPat {
Self::Or(v) => v.to_tokens(tokens),
Self::Paren(v) => v.to_tokens(tokens),
Self::Struct(v) => v.to_tokens(tokens),
Self::EnumVariant(v) => v.to_tokens(tokens),
}
}
}
@ -511,23 +611,58 @@ impl Fold for RewriteAsCheckMatch {
i.colon_token = Some(Token![:](i.member.span()));
i
}
fn fold_pat(&mut self, i: Pat) -> Pat {
match i {
Pat::Ident(PatIdent {
attrs,
by_ref,
mutability,
ident,
subpat: None,
}) if is_pat_ident_a_struct_or_enum_name(&ident) => {
parse_quote_spanned! {ident.span()=>
#(#attrs)*
#by_ref
#mutability
#ident {}
fn fold_pat(&mut self, pat: Pat) -> Pat {
match pat {
Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) {
Ok(EnumPath {
variant_path: _,
enum_path,
variant_name,
}) => parse_quote_spanned! {self.span=>
__MatchTy::<#enum_path>::#variant_name {}
},
Err(ident) => {
pat_ident.ident = ident;
Pat::Ident(self.fold_pat_ident(pat_ident))
}
}
_ => fold_pat(self, i),
},
Pat::TupleStruct(PatTupleStruct {
attrs,
qself,
path,
paren_token,
elems,
}) => match parse_enum_path(TypePath { qself, path }) {
Ok(EnumPath {
variant_path: _,
enum_path,
variant_name,
}) => {
let path = parse_quote_spanned! {self.span=>
__MatchTy::<#enum_path>::#variant_name
};
let elems = Punctuated::from_iter(
elems.into_pairs().map_pair_value(|p| fold_pat(self, p)),
);
Pat::TupleStruct(PatTupleStruct {
attrs,
qself: None,
path,
paren_token,
elems,
})
}
Err(TypePath { qself, path }) => {
Pat::TupleStruct(self.fold_pat_tuple_struct(PatTupleStruct {
attrs,
qself,
path,
paren_token,
elems,
}))
}
},
_ => fold_pat(self, pat),
}
}
fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent {
@ -556,26 +691,9 @@ impl Fold for RewriteAsCheckMatch {
struct HdlMatchParseState<'a> {
errors: &'a mut Errors,
span: Span,
}
impl HdlMatchParseState<'_> {
fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> {
let StructOrEnumPath { ty, variant } =
StructOrEnumPath::new(self.errors, path, &AggregateLiteralOptions::default())?;
Ok(if let Some((_variant_path, variant_name)) = variant {
parse_quote_spanned! {self.span=>
__MatchTy::<#ty>::#variant_name
}
} else {
parse_quote_spanned! {self.span=>
__MatchTy::<#ty>
}
})
}
}
impl Visitor {
impl Visitor<'_> {
pub(crate) fn process_hdl_match(
&mut self,
_hdl_attr: HdlAttr<Nothing>,
@ -593,22 +711,20 @@ impl Visitor {
self.require_normal_module(match_token);
let mut state = HdlMatchParseState {
errors: &mut self.errors,
span,
};
let arms = Vec::from_iter(
arms.into_iter()
.filter_map(|arm| MatchArm::parse(&mut state, arm).ok()),
);
parse_quote_spanned! {span=>
let expr = quote::quote_spanned! {span=>
{
type __MatchTy<V> =
<<V as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::MatchVariant;
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
#[allow(unused_variables)]
#check_match
});
for __match_variant in m.match_(__match_expr) {
for __match_variant in ::fayalite::module::match_(__match_expr) {
let (__match_variant, __scope) =
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
__match_variant,
@ -618,6 +734,8 @@ impl Visitor {
}
}
}
}
};
println!("{}", expr);
syn::parse2(expr).unwrap()
}
}

View file

@ -1,761 +0,0 @@
// 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<Type: ::fayalite::ty::Type<Value = #type_param>>});
static_type_predicates
.push(parse_quote! {#type_param: ::fayalite::ty::StaticValue});
}
}
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![,](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<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![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<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
{
}
}
}

View file

@ -1,969 +0,0 @@
// 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_field_names, get_target,
make_connect_impl, Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics,
},
value_derive_struct::{self, ParsedStruct, ParsedStructNames, StructOptions},
Errors, HdlAttr,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::{
parse_quote, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace,
Field, FieldMutability, Fields, FieldsNamed, Generics, Ident, Index, ItemEnum, ItemStruct,
Member, Path, Token, Type, Variant, Visibility,
};
crate::options! {
#[options = EnumOptions]
enum EnumOption {
OutlineGenerated(outline_generated),
ConnectInexact(connect_inexact),
Bounds(where_, Bounds),
Target(target, Path),
}
}
crate::options! {
#[options = VariantOptions]
enum VariantOption {}
}
crate::options! {
#[options = FieldOptions]
enum FieldOption {}
}
enum VariantValue {
None,
Direct {
value_type: Type,
},
Struct {
value_struct: ItemStruct,
parsed_struct: ParsedStruct,
},
}
impl VariantValue {
fn is_none(&self) -> bool {
matches!(self, Self::None)
}
fn value_ty(&self) -> Option<Type> {
match self {
VariantValue::None => None,
VariantValue::Direct { value_type } => Some(value_type.clone()),
VariantValue::Struct { value_struct, .. } => {
let (_, type_generics, _) = value_struct.generics.split_for_impl();
let ident = &value_struct.ident;
Some(parse_quote! { #ident #type_generics })
}
}
}
}
struct ParsedVariant {
options: HdlAttr<VariantOptions>,
ident: Ident,
fields_kind: FieldsKind,
fields: Vec<ParsedField<FieldOptions>>,
value: VariantValue,
}
impl ParsedVariant {
fn parse(
errors: &mut Errors,
variant: Variant,
enum_options: &EnumOptions,
enum_vis: &Visibility,
enum_ident: &Ident,
enum_generics: &Generics,
) -> Self {
let target = get_target(&enum_options.target, enum_ident);
let Variant {
mut attrs,
ident,
fields,
discriminant,
} = variant;
if let Some((eq, _)) = discriminant {
errors.error(eq, "#[derive(Value)]: discriminants not allowed");
}
let variant_options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
let (fields_kind, parsed_fields) =
ParsedField::parse_fields(errors, &mut fields.clone(), true);
let value = match (&fields_kind, &*parsed_fields) {
(FieldsKind::Unit, _) => VariantValue::None,
(
FieldsKind::Unnamed(_),
[ParsedField {
options,
vis: _,
name: Member::Unnamed(Index { index: 0, span: _ }),
ty,
}],
) => {
let FieldOptions {} = options.body;
VariantValue::Direct {
value_type: ty.clone(),
}
}
_ => {
let variant_value_struct_ident =
format_ident!("__{}__{}", enum_ident, ident, span = ident.span());
let variant_type_struct_ident =
format_ident!("__{}__{}__Type", enum_ident, ident, span = ident.span());
let mut value_struct_fields = fields.clone();
let (_, type_generics, _) = enum_generics.split_for_impl();
append_field(
&mut value_struct_fields,
Field {
attrs: vec![HdlAttr::from(value_derive_struct::FieldOptions {
flip: None,
skip: Some(Default::default()),
})
.to_attr()],
vis: enum_vis.clone(),
mutability: FieldMutability::None,
ident: Some(Ident::new("__phantom", ident.span())),
colon_token: None,
ty: parse_quote_spanned! {ident.span()=>
::fayalite::__std::marker::PhantomData<#target #type_generics>
},
},
);
let (value_struct_fields_kind, value_struct_parsed_fields) =
ParsedField::parse_fields(errors, &mut value_struct_fields, false);
let value_struct = ItemStruct {
attrs: vec![parse_quote! { #[allow(non_camel_case_types)] }],
vis: enum_vis.clone(),
struct_token: Token![struct](ident.span()),
ident: variant_value_struct_ident.clone(),
generics: enum_generics.clone(),
fields: value_struct_fields,
semi_token: None,
};
VariantValue::Struct {
value_struct,
parsed_struct: ParsedStruct {
options: StructOptions {
outline_generated: None,
static_: Some(Default::default()),
where_: Some((
Default::default(),
Default::default(),
ValueDeriveGenerics::get(
enum_generics.clone(),
&enum_options.where_,
)
.static_type_generics
.where_clause
.into(),
)),
target: None,
connect_inexact: enum_options.connect_inexact,
}
.into(),
vis: enum_vis.clone(),
struct_token: Default::default(),
generics: enum_generics.clone(),
fields_kind: value_struct_fields_kind,
fields: value_struct_parsed_fields,
semi_token: None, // it will fill in the semicolon if needed
skip_check_fields: true,
names: ParsedStructNames {
ident: variant_value_struct_ident.clone(),
type_struct_debug_ident: Some(format!("{enum_ident}::{ident}::Type")),
type_struct_ident: variant_type_struct_ident,
match_variant_ident: None,
builder_struct_ident: None,
mask_match_variant_ident: None,
mask_type_ident: None,
mask_type_debug_ident: Some(format!(
"AsMask<{enum_ident}::{ident}>::Type"
)),
mask_value_ident: None,
mask_value_debug_ident: Some(format!("AsMask<{enum_ident}::{ident}>")),
mask_builder_struct_ident: None,
},
},
}
}
};
ParsedVariant {
options: variant_options,
ident,
fields_kind,
fields: parsed_fields,
value,
}
}
}
struct ParsedEnum {
options: HdlAttr<EnumOptions>,
vis: Visibility,
enum_token: Token![enum],
ident: Ident,
generics: Generics,
brace_token: Brace,
variants: Vec<ParsedVariant>,
}
impl ParsedEnum {
fn parse(item: ItemEnum) -> syn::Result<Self> {
let ItemEnum {
mut attrs,
vis,
enum_token,
ident,
generics,
brace_token,
variants,
} = item;
let mut errors = Errors::new();
let enum_options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
let variants = variants
.into_iter()
.map(|variant| {
ParsedVariant::parse(
&mut errors,
variant,
&enum_options.body,
&vis,
&ident,
&generics,
)
})
.collect();
errors.finish()?;
Ok(ParsedEnum {
options: enum_options,
vis,
enum_token,
ident,
generics,
brace_token,
variants,
})
}
}
impl ToTokens for ParsedEnum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
options,
vis,
enum_token,
ident: enum_ident,
generics: enum_generics,
brace_token,
variants,
} = self;
let EnumOptions {
outline_generated: _,
connect_inexact,
where_,
target,
} = &options.body;
let target = get_target(target, enum_ident);
let ValueDeriveGenerics {
generics: _,
static_type_generics,
} = ValueDeriveGenerics::get(enum_generics.clone(), where_);
let (static_type_impl_generics, static_type_type_generics, static_type_where_clause) =
static_type_generics.split_for_impl();
let type_struct_ident = format_ident!("__{}__Type", enum_ident);
let mut field_checks = vec![];
let mut make_type_struct_variant_type = |variant: &ParsedVariant| {
let VariantOptions {} = variant.options.body;
let (value_struct, parsed_struct) = match &variant.value {
VariantValue::None => {
return None;
}
VariantValue::Direct { value_type } => {
field_checks.push(quote_spanned! {value_type.span()=>
__check_field::<#value_type>();
});
return Some(parse_quote! { <#value_type as ::fayalite::expr::ToExpr>::Type });
}
VariantValue::Struct {
value_struct,
parsed_struct,
} => (value_struct, parsed_struct),
};
value_struct.to_tokens(tokens);
parsed_struct.to_tokens(tokens);
let mut field_names = Vec::from_iter(get_field_names(&value_struct.fields));
derive_clone_hash_eq_partialeq_for_struct(
&value_struct.ident,
&static_type_generics,
&field_names,
)
.to_tokens(tokens);
field_names = Vec::from_iter(
field_names
.into_iter()
.zip(parsed_struct.fields.iter())
.filter_map(|(member, field)| {
field.options.body.skip.is_none().then_some(member)
}),
);
let field_name_strs =
Vec::from_iter(field_names.iter().map(|v| v.to_token_stream().to_string()));
let debug_ident = format!("{enum_ident}::{}", variant.ident);
let debug_body = match variant.fields_kind {
FieldsKind::Unit => quote! {
f.debug_struct(#debug_ident).finish()
},
FieldsKind::Named(_) => quote! {
f.debug_struct(#debug_ident)
#(.field(#field_name_strs, &self.#field_names))*
.finish()
},
FieldsKind::Unnamed(_) => quote! {
f.debug_tuple(#debug_ident)#(.field(&self.#field_names))*.finish()
},
};
let value_struct_ident = &value_struct.ident;
quote! {
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
for #value_struct_ident #static_type_type_generics
#static_type_where_clause
{
fn fmt(
&self,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
#debug_body
}
}
}
.to_tokens(tokens);
Some(parse_quote! {
<
#value_struct_ident #static_type_type_generics
as ::fayalite::expr::ToExpr
>::Type
})
};
let type_struct_variants = Punctuated::from_iter(variants.iter().filter_map(|variant| {
let VariantOptions {} = variant.options.body;
Some(Field {
attrs: vec![],
vis: vis.clone(),
mutability: FieldMutability::None,
ident: Some(variant.ident.clone()),
colon_token: None, // it will fill in the colon if needed
ty: make_type_struct_variant_type(variant)?,
})
}));
let type_struct = ItemStruct {
attrs: vec![
parse_quote! {#[allow(non_camel_case_types)]},
parse_quote! {#[allow(non_snake_case)]},
],
vis: vis.clone(),
struct_token: Token![struct](enum_token.span),
ident: type_struct_ident,
generics: static_type_generics.clone(),
fields: Fields::Named(FieldsNamed {
brace_token: *brace_token,
named: type_struct_variants,
}),
semi_token: None,
};
let type_struct_ident = &type_struct.ident;
let type_struct_debug_ident = format!("{enum_ident}::Type");
type_struct.to_tokens(tokens);
let non_empty_variant_names = Vec::from_iter(
variants
.iter()
.filter(|v| !v.value.is_none())
.map(|v| v.ident.clone()),
);
let non_empty_variant_name_strs =
Vec::from_iter(non_empty_variant_names.iter().map(|v| v.to_string()));
let debug_type_body = quote! {
f.debug_struct(#type_struct_debug_ident)
#(.field(#non_empty_variant_name_strs, &self.#non_empty_variant_names))*
.finish()
};
derive_clone_hash_eq_partialeq_for_struct(
type_struct_ident,
&static_type_generics,
&non_empty_variant_names,
)
.to_tokens(tokens);
let variant_names = Vec::from_iter(variants.iter().map(|v| &v.ident));
let variant_name_strs = Vec::from_iter(variant_names.iter().map(|v| v.to_string()));
let (variant_field_pats, variant_to_canonical_values): (Vec<_>, Vec<_>) = variants
.iter()
.map(|v| {
let field_names: Vec<_> = v.fields.iter().map(|field| &field.name).collect();
let var_names: Vec<_> = v.fields.iter().map(|field| field.var_name()).collect();
let field_pats = quote! {
#(#field_names: #var_names,)*
};
let to_canonical_value = match &v.value {
VariantValue::None => quote! { ::fayalite::__std::option::Option::None },
VariantValue::Direct { .. } => {
debug_assert_eq!(var_names.len(), 1);
quote! {
::fayalite::__std::option::Option::Some(
::fayalite::ty::DynValueTrait::to_canonical_dyn(#(#var_names)*),
)
}
}
VariantValue::Struct {
value_struct,
parsed_struct,
} => {
let value_struct_ident = &value_struct.ident;
let phantom_field_name = &parsed_struct
.fields
.last()
.expect("missing phantom field")
.name;
let type_generics = static_type_type_generics.as_turbofish();
quote! {
::fayalite::__std::option::Option::Some(
::fayalite::ty::DynValueTrait::to_canonical_dyn(
&#value_struct_ident #type_generics {
#(#field_names:
::fayalite::__std::clone::Clone::clone(#var_names),)*
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
},
),
)
}
}
};
(field_pats, to_canonical_value)
})
.unzip();
let mut match_enum_variants = Punctuated::new();
let mut match_enum_debug_arms = vec![];
let mut match_enum_arms = vec![];
let mut variant_vars = vec![];
let mut from_canonical_type_variant_lets = vec![];
let mut non_empty_variant_vars = vec![];
let mut enum_type_variants = vec![];
let mut enum_type_variants_hint = vec![];
let match_enum_ident = format_ident!("__{}__MatchEnum", enum_ident);
let mut builder = Builder::new(format_ident!("__{}__Builder", enum_ident), vis.clone());
for variant in variants.iter() {
for field in variant.fields.iter() {
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);
for (variant_index, variant) in variants.iter().enumerate() {
let variant_var = format_ident!("__v_{}", variant.ident);
let variant_name = &variant.ident;
let variant_name_str = variant.ident.to_string();
match_enum_variants.push(Variant {
attrs: vec![],
ident: variant.ident.clone(),
fields: variant.fields_kind.into_fields(variant.fields.iter().map(
|ParsedField {
options,
vis,
name,
ty,
}| {
let FieldOptions {} = options.body;
Field {
attrs: vec![],
vis: vis.clone(),
mutability: FieldMutability::None,
ident: if let Member::Named(name) = name {
Some(name.clone())
} else {
None
},
colon_token: None,
ty: parse_quote! { ::fayalite::expr::Expr<#ty> },
}
},
)),
discriminant: None,
});
let match_enum_field_names = Vec::from_iter(variant.fields.iter().map(
|ParsedField {
options,
vis: _,
name,
ty: _,
}| {
let FieldOptions {} = options.body;
name
},
));
let match_enum_field_name_strs = Vec::from_iter(variant.fields.iter().map(
|ParsedField {
options,
vis: _,
name,
ty: _,
}| {
let FieldOptions {} = options.body;
name.to_token_stream().to_string()
},
));
let match_enum_debug_vars = Vec::from_iter(variant.fields.iter().map(
|ParsedField {
options,
vis: _,
name,
ty: _,
}| {
let FieldOptions {} = options.body;
format_ident!("__v_{}", name)
},
));
match_enum_debug_arms.push(match variant.fields_kind {
FieldsKind::Unit | FieldsKind::Named(_) => quote! {
Self::#variant_name {
#(#match_enum_field_names: ref #match_enum_debug_vars,)*
} => f.debug_struct(#variant_name_str)
#(.field(#match_enum_field_name_strs, #match_enum_debug_vars))*
.finish(),
},
FieldsKind::Unnamed(_) => quote! {
Self::#variant_name(
#(ref #match_enum_debug_vars,)*
) => f.debug_tuple(#variant_name_str)
#(.field(#match_enum_debug_vars))*
.finish(),
},
});
if let Some(value_ty) = variant.value.value_ty() {
from_canonical_type_variant_lets.push(quote! {
let #variant_var =
#variant_var.from_canonical_type_helper_has_value(#variant_name_str);
});
non_empty_variant_vars.push(variant_var.clone());
enum_type_variants.push(quote! {
::fayalite::enum_::VariantType {
name: ::fayalite::intern::Intern::intern(#variant_name_str),
ty: ::fayalite::__std::option::Option::Some(
::fayalite::ty::DynType::canonical_dyn(&self.#variant_name),
),
}
});
enum_type_variants_hint.push(quote! {
::fayalite::enum_::VariantType {
name: ::fayalite::intern::Intern::intern(#variant_name_str),
ty: ::fayalite::__std::option::Option::Some(
::fayalite::bundle::TypeHint::<
<#value_ty as ::fayalite::expr::ToExpr>::Type,
>::intern_dyn(),
),
}
});
} else {
from_canonical_type_variant_lets.push(quote! {
#variant_var.from_canonical_type_helper_no_value(#variant_name_str);
});
enum_type_variants.push(quote! {
::fayalite::enum_::VariantType {
name: ::fayalite::intern::Intern::intern(#variant_name_str),
ty: ::fayalite::__std::option::Option::None,
}
});
enum_type_variants_hint.push(quote! {
::fayalite::enum_::VariantType {
name: ::fayalite::intern::Intern::intern(#variant_name_str),
ty: ::fayalite::__std::option::Option::None,
}
});
}
variant_vars.push(variant_var);
match_enum_arms.push(match &variant.value {
VariantValue::None => quote! {
#variant_index => #match_enum_ident::#variant_name,
},
VariantValue::Direct { value_type } => quote! {
#variant_index => #match_enum_ident::#variant_name {
#(#match_enum_field_names)*: ::fayalite::expr::ToExpr::to_expr(
&__variant_access.downcast_unchecked::<
<#value_type as ::fayalite::expr::ToExpr>::Type>(),
),
},
},
VariantValue::Struct {
value_struct: ItemStruct { ident, .. },
..
} => quote! {
#variant_index => {
let __variant_access = ::fayalite::expr::ToExpr::to_expr(
&__variant_access.downcast_unchecked::<<
#ident #static_type_type_generics
as ::fayalite::expr::ToExpr
>::Type>(),
);
#match_enum_ident::#variant_name {
#(#match_enum_field_names:
(*__variant_access).#match_enum_field_names,)*
}
},
},
});
let builder_field_and_types = Vec::from_iter(variant.fields.iter().map(
|ParsedField {
options,
vis: _,
name,
ty,
}| {
let FieldOptions {} = options.body;
(name, ty)
},
));
let builder_field_vars = Vec::from_iter(
builder_field_and_types
.iter()
.map(|(name, _)| &builder.get_field(name).unwrap().1.builder_field_name),
);
let build_body = match &variant.value {
VariantValue::None => parse_quote! {
{
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::<
#type_struct_ident #static_type_type_generics
>::new_unchecked(
::fayalite::__std::option::Option::None,
#variant_index,
::fayalite::ty::StaticType::static_type(),
),
)
}
},
VariantValue::Direct { value_type: _ } => parse_quote! {
{
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::<
#type_struct_ident #static_type_type_generics
>::new_unchecked(
::fayalite::__std::option::Option::Some(
#(#builder_field_vars)*.to_canonical_dyn(),
),
#variant_index,
::fayalite::ty::StaticType::static_type(),
),
)
}
},
VariantValue::Struct {
parsed_struct:
ParsedStruct {
names:
ParsedStructNames {
type_struct_ident: field_type_struct_ident,
..
},
..
},
..
} => parse_quote! {
{
let __builder = <
#field_type_struct_ident #static_type_type_generics
as ::fayalite::bundle::BundleType
>::builder();
#(let __builder = __builder.#builder_field_vars(#builder_field_vars);)*
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::EnumLiteral::<
#type_struct_ident #static_type_type_generics
>::new_unchecked(
::fayalite::__std::option::Option::Some(
__builder.build().to_canonical_dyn(),
),
#variant_index,
::fayalite::ty::StaticType::static_type(),
),
)
}
},
};
builder
.make_build_method(
&format_ident!("variant_{}", variant_name),
variant.fields.iter().map(
|ParsedField {
options,
vis: _,
name,
ty,
}| {
let FieldOptions {} = options.body;
(name.clone(), parse_quote! { ::fayalite::expr::Expr<#ty> })
},
),
&static_type_generics,
&parse_quote! {#type_struct_ident #static_type_type_generics},
&parse_quote! { ::fayalite::expr::Expr<#target #static_type_type_generics> },
build_body,
)
.to_tokens(tokens);
}
let match_enum = ItemEnum {
attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
vis: vis.clone(),
enum_token: *enum_token,
ident: match_enum_ident,
generics: static_type_generics.clone(),
brace_token: *brace_token,
variants: match_enum_variants,
};
let match_enum_ident = &match_enum.ident;
match_enum.to_tokens(tokens);
make_connect_impl(
*connect_inexact,
&static_type_generics,
type_struct_ident,
variants.iter().flat_map(|variant| {
variant.fields.iter().map(|field| {
let ty = &field.ty;
parse_quote_spanned! {field.name.span()=>
<#ty as ::fayalite::expr::ToExpr>::Type
}
})
}),
)
.to_tokens(tokens);
let variant_count = variants.len();
let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false);
quote! {
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
for #match_enum_ident #static_type_type_generics
#static_type_where_clause
{
fn fmt(
&self,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
match *self {
#(#match_enum_debug_arms)*
}
}
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::ty::StaticType
for #type_struct_ident #static_type_type_generics
#static_type_where_clause
{
fn static_type() -> Self {
Self {
#(#non_empty_variant_names: ::fayalite::ty::StaticType::static_type(),)*
}
}
}
fn __check_field<T: ::fayalite::ty::Value>()
where
<T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
{}
fn __check_fields #static_type_impl_generics(_: #target #static_type_type_generics)
#static_type_where_clause
{
#(#field_checks)*
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
for #type_struct_ident #static_type_type_generics
#static_type_where_clause
{
fn fmt(
&self,
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
) -> ::fayalite::__std::fmt::Result {
#debug_type_body
}
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::ty::Type
for #type_struct_ident #static_type_type_generics
#static_type_where_clause
{
type CanonicalType = ::fayalite::enum_::DynEnumType;
type Value = #target #static_type_type_generics;
type CanonicalValue = ::fayalite::enum_::DynEnum;
type MaskType = ::fayalite::int::UIntType<1>;
type MaskValue = ::fayalite::int::UInt<1>;
type MatchVariant = #match_enum_ident #static_type_type_generics;
type MatchActiveScope = ::fayalite::module::Scope;
type MatchVariantAndInactiveScope =
::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
fn match_variants<IO: ::fayalite::bundle::BundleValue>(
this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
module_builder: &mut ::fayalite::module::ModuleBuilder<
IO,
::fayalite::module::NormalModule,
>,
source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
where
<IO as ::fayalite::expr::ToExpr>::Type:
::fayalite::bundle::BundleType<Value = IO>,
{
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
::fayalite::int::UIntType::new()
}
fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType {
let variants = ::fayalite::enum_::EnumType::variants(self);
::fayalite::enum_::DynEnumType::new(variants)
}
fn source_location(&self) -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
fn type_enum(&self) -> ::fayalite::ty::TypeEnum {
::fayalite::ty::TypeEnum::EnumType(::fayalite::ty::Type::canonical(self))
}
#[allow(non_snake_case)]
fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self {
let [#(#variant_vars),*] = *::fayalite::enum_::EnumType::variants(&t) else {
::fayalite::__std::panic!("wrong number of variants");
};
#(#from_canonical_type_variant_lets)*
Self {
#(#non_empty_variant_names: #non_empty_variant_vars,)*
}
}
}
#[automatically_derived]
#[allow(clippy::init_numbered_fields)]
impl #static_type_impl_generics ::fayalite::enum_::EnumType
for #type_struct_ident #static_type_type_generics
#static_type_where_clause
{
type Builder = #empty_builder_ty;
fn match_activate_scope(
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
) -> (
<Self as ::fayalite::ty::Type>::MatchVariant,
<Self as ::fayalite::ty::Type>::MatchActiveScope,
) {
let (__variant_access, __scope) = v.activate();
(
match ::fayalite::expr::ops::VariantAccess::variant_index(
&*__variant_access,
) {
#(#match_enum_arms)*
#variant_count.. => ::fayalite::__std::panic!("invalid variant index"),
},
__scope,
)
}
fn builder() -> <Self as ::fayalite::enum_::EnumType>::Builder {
#empty_builder_ty::new()
}
fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::VariantType<
::fayalite::intern::Interned<dyn ::fayalite::ty::DynCanonicalType>,
>]> {
::fayalite::intern::Intern::intern(&[#(#enum_type_variants,)*][..])
}
fn variants_hint() -> ::fayalite::enum_::VariantsHint {
::fayalite::enum_::VariantsHint::new([#(#enum_type_variants_hint,)*], false)
}
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::expr::ToExpr
for #target #static_type_type_generics
#static_type_where_clause
{
type Type = #type_struct_ident #static_type_type_generics;
fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type {
::fayalite::ty::StaticType::static_type()
}
fn to_expr(&self) -> ::fayalite::expr::Expr<Self> {
::fayalite::expr::Expr::from_value(self)
}
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::ty::Value
for #target #static_type_type_generics
#static_type_where_clause
{
fn to_canonical(&self) -> <
<Self as ::fayalite::expr::ToExpr>::Type
as ::fayalite::ty::Type
>::CanonicalValue
{
let __ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self));
match self {
#(Self::#variant_names { #variant_field_pats } => {
::fayalite::enum_::DynEnum::new_by_name(
__ty,
::fayalite::intern::Intern::intern(#variant_name_strs),
#variant_to_canonical_values,
)
})*
}
}
}
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::enum_::EnumValue
for #target #static_type_type_generics
#static_type_where_clause
{
}
}
.to_tokens(tokens);
}
}
pub(crate) fn value_derive_enum(item: ItemEnum) -> syn::Result<TokenStream> {
let item = ParsedEnum::parse(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-enum-");
}
Ok(contents)
}

View file

@ -1,765 +0,0 @@
// 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),
Static(static_),
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<I, S> {
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<StructOptions>,
pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct],
pub(crate) generics: Generics,
pub(crate) fields_kind: FieldsKind,
pub(crate) fields: Vec<ParsedField<FieldOptions>>,
pub(crate) semi_token: Option<Token![;]>,
pub(crate) skip_check_fields: bool,
pub(crate) names: ParsedStructNames<Option<Ident>, Option<String>>,
}
impl ParsedStruct {
pub(crate) fn parse(item: &mut ItemStruct) -> syn::Result<Self> {
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: _,
static_,
connect_inexact,
} = &options.body;
let ValueDeriveGenerics {
generics,
static_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 static_.is_some() {
let (impl_generics, type_generics, where_clause) =
static_type_generics.split_for_impl();
quote! {
#[automatically_derived]
impl #impl_generics ::fayalite::ty::StaticType for #type_struct_ident #type_generics
#where_clause
{
fn static_type() -> Self {
Self {
#(#unskipped_field_names: ::fayalite::ty::StaticType::static_type(),)*
#(#phantom_data_field_name_slice:
::fayalite::__std::marker::PhantomData,)*
}
}
}
}
.to_tokens(tokens);
}
if !skip_check_fields {
quote! {
fn __check_field<T: ::fayalite::ty::Value>(_v: T)
where
<T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
{}
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<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
#[allow(unused_variables)]
fn match_variants<IO: ::fayalite::bundle::BundleValue>(
this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
module_builder: &mut ::fayalite::module::ModuleBuilder<
IO,
::fayalite::module::NormalModule,
>,
source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
where
<IO as ::fayalite::expr::ToExpr>::Type:
::fayalite::bundle::BundleType<Value = IO>,
{
::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) -> <Self as ::fayalite::ty::Type>::MaskType {
#mask_type_body
}
fn canonical(&self) -> <Self as ::fayalite::ty::Type>::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: <Self as ::fayalite::ty::Type>::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<<Self as ::fayalite::ty::Type>::Value>)
-> &<Self as ::fayalite::ty::Type>::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() -> <Self as ::fayalite::bundle::BundleType>::Builder {
#empty_builder_ty::new()
}
fn fields(&self) -> ::fayalite::intern::Interned<
[::fayalite::bundle::FieldType<::fayalite::intern::Interned<
dyn ::fayalite::ty::DynCanonicalType,
>>],
>
{
::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) -> <Self as ::fayalite::expr::ToExpr>::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<Self> {
::fayalite::expr::Expr::from_value(self)
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Value for #target #type_generics
#where_clause
{
fn to_canonical(&self) -> <
<Self as ::fayalite::expr::ToExpr>::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<TokenStream> {
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)
}