860 lines
36 KiB
Rust
860 lines
36 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
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, kw::hdl>,
|
|
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, kw::hdl>>>,
|
|
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, kw::hdl>> {
|
|
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()));
|
|
errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs))
|
|
}
|
|
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, kw::hdl>::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, non_snake_case, 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 span = ident.span();
|
|
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(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! {span=>
|
|
#ident: #expr,
|
|
}
|
|
})
|
|
.collect();
|
|
parse_quote_spanned! {span=>
|
|
#target {
|
|
#(#fields)*
|
|
}
|
|
}
|
|
})
|
|
}
|
|
let mut wrapped_in_const = WrappedInConst::new(tokens, 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 { ty, .. } in &mut mask_type_fields.named {
|
|
*ty = parse_quote_spanned! {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(span),
|
|
parse_quote_spanned! {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 { ty, .. } in &mut mask_type_match_variant_fields.named {
|
|
*ty = parse_quote_spanned! {span=>
|
|
::fayalite::expr::Expr<#ty>
|
|
};
|
|
}
|
|
ItemStruct {
|
|
attrs: vec![
|
|
common_derives(span),
|
|
parse_quote_spanned! {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 { ty, .. } in &mut match_variant_fields.named {
|
|
*ty = parse_quote_spanned! {span=>
|
|
::fayalite::expr::Expr<#ty>
|
|
};
|
|
}
|
|
ItemStruct {
|
|
attrs: vec![
|
|
common_derives(span),
|
|
parse_quote_spanned! {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 this_token = Ident::new("__this", span);
|
|
let fields_token = Ident::new("__fields", span);
|
|
let self_token = Token![self](span);
|
|
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! {span=>
|
|
#ident: ::fayalite::expr::Expr::field(#this_token, #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! {span=>
|
|
#ident: ::fayalite::ty::Type::mask_type(&#self_token.#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 not_flipped = flip.is_none().then(|| Token![!](span));
|
|
quote_spanned! {span=>
|
|
#ident: {
|
|
let ::fayalite::bundle::BundleField {
|
|
name: __name,
|
|
flipped: __flipped,
|
|
ty: __ty,
|
|
} = #fields_token[#index];
|
|
::fayalite::__std::assert_eq!(&*__name, #ident_str);
|
|
::fayalite::__std::assert!(#not_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! {span=>
|
|
::fayalite::bundle::BundleField {
|
|
name: ::fayalite::intern::Intern::intern(#ident_str),
|
|
flipped: #flipped,
|
|
ty: ::fayalite::ty::Type::canonical(&#self_token.#ident),
|
|
},
|
|
}
|
|
},
|
|
));
|
|
let fields_len = fields.named().into_iter().len();
|
|
quote_spanned! {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_token: ::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_token) -> <Self as ::fayalite::ty::Type>::MaskType {
|
|
*#self_token
|
|
}
|
|
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
|
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token)))
|
|
}
|
|
#[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_token = ::fayalite::bundle::BundleType::fields(&__bundle);
|
|
::fayalite::__std::assert_eq!(#fields_token.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_token) -> ::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_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
|
let #this_token = *#this_token;
|
|
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_token: ::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_token) -> <Self as ::fayalite::ty::Type>::MaskType {
|
|
#mask_type_ident {
|
|
#(#mask_type_body_fields)*
|
|
}
|
|
}
|
|
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
|
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token)))
|
|
}
|
|
#[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_token = ::fayalite::bundle::BundleType::fields(&__bundle);
|
|
::fayalite::__std::assert_eq!(#fields_token.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_token) -> ::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_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
|
let #this_token = *#this_token;
|
|
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! {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! {span=>
|
|
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
|
|
}
|
|
}));
|
|
let type_properties = format_ident!("__type_properties", span = span);
|
|
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
|
|
let flipped = field_flip.is_some();
|
|
let ty = field.ty();
|
|
quote_spanned! {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 flipped = field_flip.is_some();
|
|
let ty = field.ty();
|
|
quote_spanned! {span=>
|
|
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
|
|
}
|
|
}));
|
|
quote_spanned! {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)
|
|
}
|