WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 13s
All checks were successful
/ test (push) Successful in 13s
This commit is contained in:
parent
cd99dbc849
commit
43da831d31
|
@ -7,13 +7,14 @@ jobs:
|
|||
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
|
||||
source "$HOME/.cargo/env"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
- run: cargo test
|
||||
- run: cargo test --features=unstable-doc
|
||||
- run: cargo doc --features=unstable-doc
|
||||
# FIXME: uncomment once the code works again
|
||||
# - run: |
|
||||
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
|
||||
# source "$HOME/.cargo/env"
|
||||
# echo "$PATH" >> "$GITHUB_PATH"
|
||||
# - uses: https://github.com/Swatinem/rust-cache@v2
|
||||
# with:
|
||||
# save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
# - run: cargo test
|
||||
# - run: cargo test --features=unstable-doc
|
||||
# - run: cargo doc --features=unstable-doc
|
||||
|
|
713
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
713
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
|
@ -0,0 +1,713 @@
|
|||
use crate::{
|
||||
hdl_type_common::{common_derives, get_target, TypeOptions, 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<TypeOptions>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) struct_token: Token![struct],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: Generics,
|
||||
pub(crate) fields: 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,
|
||||
generics,
|
||||
fields,
|
||||
semi_token,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
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));
|
||||
}
|
||||
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 (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: ::fayalite::expr::Expr<#ty>,
|
||||
) -> #filled_ty {
|
||||
let Self {
|
||||
#phantom_field_name: _,
|
||||
#(#pat_fields)*
|
||||
} = self;
|
||||
#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, &__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 TypeOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact,
|
||||
target,
|
||||
} = &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.clone(),
|
||||
fields: Fields::Named(fields.clone()),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
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.clone(),
|
||||
fields: fields.clone(),
|
||||
};
|
||||
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 = 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.clone(),
|
||||
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.clone(),
|
||||
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.clone(),
|
||||
fields: Fields::Named(mask_type_match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let mut match_variant_fields = 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.clone(),
|
||||
fields: Fields::Named(match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let match_variant_body_fields =
|
||||
Vec::from_iter(fields.named.iter().map(|Field { ident, .. }| {
|
||||
let ident: &Ident = 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.iter().map(|Field { ident, .. }| {
|
||||
let ident: &Ident = 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.iter().enumerate().zip(field_flips).map(
|
||||
|((index, Field { ident, .. }), flip)| {
|
||||
let ident: &Ident = 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.iter().zip(field_flips).map(
|
||||
|(Field { ident, .. }, flip)| {
|
||||
let ident: &Ident = 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.len();
|
||||
quote_spanned! {ident.span()=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
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>,
|
||||
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
__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)*][..])
|
||||
}
|
||||
}
|
||||
#[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 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>,
|
||||
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
__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)*][..])
|
||||
}
|
||||
}
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
551
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
551
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
|
@ -0,0 +1,551 @@
|
|||
use crate::{
|
||||
hdl_type_common::{common_derives, get_target, TypeOptions, 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 ParsedField {
|
||||
pub(crate) paren_token: Paren,
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<FieldOptions>,
|
||||
pub(crate) ty: 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<ParsedField>,
|
||||
}
|
||||
|
||||
impl ParsedVariant {
|
||||
fn parse(errors: &mut Errors, variant: Variant) -> 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) {
|
||||
// FIXME: use mutability as the spanned tokens,
|
||||
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||
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(ParsedField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options,
|
||||
ty,
|
||||
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<TypeOptions>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) enum_token: Token![enum],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: 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,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
let variants = Punctuated::from_iter(
|
||||
variants
|
||||
.into_pairs()
|
||||
.map_pair_value(|v| ParsedVariant::parse(&mut errors, v)),
|
||||
);
|
||||
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 TypeOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact,
|
||||
target,
|
||||
} = &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(ParsedField {
|
||||
paren_token,
|
||||
attrs: _,
|
||||
options,
|
||||
ty,
|
||||
comma_token: _,
|
||||
}) = field
|
||||
{
|
||||
let FieldOptions {} = options.body;
|
||||
colon_token = Token![:](paren_token.span.open());
|
||||
ty.clone()
|
||||
} 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.clone(),
|
||||
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();
|
||||
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.clone(),
|
||||
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(ParsedField {
|
||||
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(),
|
||||
},
|
||||
*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.clone(),
|
||||
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(ParsedField {
|
||||
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(ParsedField { 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(
|
||||
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(
|
||||
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 var = format_ident!("_variant_{}", ident);
|
||||
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_unchecked(
|
||||
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(ParsedField { 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 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>,
|
||||
module_builder: &mut ::fayalite::module::ModuleBuilder<::fayalite::module::NormalModule>,
|
||||
source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
module_builder.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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
415
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
415
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
|
@ -0,0 +1,415 @@
|
|||
use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use std::fmt;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::Punctuated,
|
||||
token::Paren,
|
||||
Attribute, ConstParam, GenericParam, Generics, Ident, ImplGenerics, LifetimeParam, Path,
|
||||
PredicateType, Token, Turbofish, Type, TypeGenerics, TypeParam, WhereClause, WherePredicate,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = TypeOptions]
|
||||
pub(crate) enum TypeOption {
|
||||
OutlineGenerated(outline_generated),
|
||||
ConnectInexact(connect_inexact),
|
||||
Target(target, Path),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedInConst<'a> {
|
||||
outer: &'a mut TokenStream,
|
||||
span: Span,
|
||||
inner: TokenStream,
|
||||
}
|
||||
|
||||
impl<'a> WrappedInConst<'a> {
|
||||
pub(crate) fn new(outer: &'a mut TokenStream, span: Span) -> Self {
|
||||
Self {
|
||||
outer,
|
||||
span,
|
||||
inner: TokenStream::new(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn inner(&mut self) -> &mut TokenStream {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WrappedInConst<'_> {
|
||||
fn drop(&mut self) {
|
||||
let inner = &self.inner;
|
||||
quote_spanned! {self.span=>
|
||||
const _: () = {
|
||||
#inner
|
||||
};
|
||||
}
|
||||
.to_tokens(self.outer);
|
||||
}
|
||||
}
|
||||
|
||||
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) fn common_derives(span: Span) -> Attribute {
|
||||
parse_quote_spanned! {span=>
|
||||
#[::fayalite::__std::prelude::v1::derive(
|
||||
::fayalite::__std::fmt::Debug,
|
||||
::fayalite::__std::cmp::Eq,
|
||||
::fayalite::__std::cmp::PartialEq,
|
||||
::fayalite::__std::hash::Hash,
|
||||
::fayalite::__std::marker::Copy,
|
||||
::fayalite::__std::clone::Clone,
|
||||
)]
|
||||
}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = LifetimeParamOptions]
|
||||
enum LifetimeParamOption {}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = TypeParamOptions]
|
||||
pub(crate) enum TypeParamOption {}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = ConstParamOptions]
|
||||
pub(crate) enum ConstParamOption {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ParsedType {}
|
||||
|
||||
impl ParsedType {
|
||||
pub(crate) fn parse(ty: &mut Type) -> syn::Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedTypeParam {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<TypeParamOptions>,
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) default: Option<(Token![=], ParsedType)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ParsedGenericParam {
|
||||
Type(ParsedTypeParam),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedGenerics {
|
||||
pub(crate) lt_token: Option<Token![<]>,
|
||||
pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>,
|
||||
pub(crate) gt_token: Option<Token![>]>,
|
||||
}
|
||||
|
||||
impl ParsedGenerics {
|
||||
pub(crate) fn parse(generics: &mut Generics) -> syn::Result<Self> {
|
||||
let Generics {
|
||||
lt_token,
|
||||
params,
|
||||
gt_token,
|
||||
where_clause,
|
||||
} = generics;
|
||||
let mut errors = Errors::new();
|
||||
let mut predicates: Vec<WherePredicate> = Vec::with_capacity(params.len());
|
||||
let params = Punctuated::from_iter(params.pairs_mut().filter_map_pair_value_mut(|param| {
|
||||
Some(match param {
|
||||
GenericParam::Lifetime(param) => {
|
||||
errors.unwrap_or_default(HdlAttr::<LifetimeParamOptions>::parse_and_take_attr(
|
||||
&mut param.attrs,
|
||||
));
|
||||
errors.error(param, "lifetime generics are not supported by #[hdl]");
|
||||
return None;
|
||||
}
|
||||
GenericParam::Type(TypeParam {
|
||||
attrs,
|
||||
ident,
|
||||
colon_token,
|
||||
bounds,
|
||||
eq_token,
|
||||
default,
|
||||
}) => {
|
||||
let span = ident.span();
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::<TypeParamOptions>::parse_and_take_attr(attrs))
|
||||
.unwrap_or_default();
|
||||
if !bounds.is_empty() {
|
||||
predicates.push(WherePredicate::Type(PredicateType {
|
||||
lifetimes: None,
|
||||
bounded_ty: parse_quote! { #ident },
|
||||
colon_token: colon_token.unwrap_or_else(|| Token![:](span)),
|
||||
bounds: bounds.clone(),
|
||||
}));
|
||||
}
|
||||
let default = default
|
||||
.as_mut()
|
||||
.and_then(|v| errors.ok(ParsedType::parse(v)));
|
||||
ParsedGenericParam::Type(ParsedTypeParam {
|
||||
attrs: attrs.clone(),
|
||||
options,
|
||||
ident: ident.clone(),
|
||||
default: default.map(|v| (eq_token.unwrap_or_else(|| Token![=](span)), v)),
|
||||
})
|
||||
}
|
||||
GenericParam::Const(ConstParam {
|
||||
attrs,
|
||||
const_token,
|
||||
ident,
|
||||
colon_token,
|
||||
ty,
|
||||
eq_token,
|
||||
default,
|
||||
}) => {
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr(attrs))
|
||||
.unwrap_or_default();
|
||||
todo!()
|
||||
}
|
||||
})
|
||||
}));
|
||||
if let Some(where_clause) = where_clause {
|
||||
for predicate in &mut where_clause.predicates {
|
||||
let WherePredicate::Type(PredicateType {
|
||||
lifetimes,
|
||||
bounded_ty,
|
||||
colon_token,
|
||||
bounds,
|
||||
}) = predicate
|
||||
else {
|
||||
errors.error(predicate, "unsupported where predicate kind");
|
||||
continue;
|
||||
};
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
lt_token: *lt_token,
|
||||
params,
|
||||
gt_token: *gt_token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedGenerics {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedGenericsImplGenerics<'a>(&'a ParsedGenerics);
|
||||
|
||||
impl ToTokens for ParsedGenericsImplGenerics<'_> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedGenericsTurbofish<'a>(&'a ParsedGenerics);
|
||||
|
||||
impl ToTokens for ParsedGenericsTurbofish<'_> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedGenericsTypeGenerics<'a>(&'a ParsedGenerics);
|
||||
|
||||
impl ToTokens for ParsedGenericsTypeGenerics<'_> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedGenericsWhereClause<'a>(&'a ParsedGenerics);
|
||||
|
||||
impl ToTokens for ParsedGenericsWhereClause<'_> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsTurbofish {
|
||||
type Turbofish<'a>: 'a + ToTokens + Clone + fmt::Debug
|
||||
where
|
||||
Self: 'a;
|
||||
fn as_turbofish(&self) -> Self::Turbofish<'_>;
|
||||
}
|
||||
|
||||
impl AsTurbofish for TypeGenerics<'_> {
|
||||
type Turbofish<'a> = Turbofish<'a> where Self: 'a;
|
||||
|
||||
fn as_turbofish(&self) -> Self::Turbofish<'_> {
|
||||
TypeGenerics::as_turbofish(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsTurbofish for ParsedGenericsTypeGenerics<'_> {
|
||||
type Turbofish<'a> = ParsedGenericsTurbofish<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn as_turbofish(&self) -> Self::Turbofish<'_> {
|
||||
ParsedGenericsTurbofish(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SplitForImpl {
|
||||
type ImplGenerics<'a>: 'a + ToTokens + Clone + fmt::Debug
|
||||
where
|
||||
Self: 'a;
|
||||
type TypeGenerics<'a>: 'a + AsTurbofish + ToTokens + Clone + fmt::Debug
|
||||
where
|
||||
Self: 'a;
|
||||
type WhereClause<'a>: 'a + ToTokens + Clone + fmt::Debug
|
||||
where
|
||||
Self: 'a;
|
||||
fn split_for_impl(
|
||||
&self,
|
||||
) -> (
|
||||
Self::ImplGenerics<'_>,
|
||||
Self::TypeGenerics<'_>,
|
||||
Self::WhereClause<'_>,
|
||||
);
|
||||
}
|
||||
|
||||
impl SplitForImpl for Generics {
|
||||
type ImplGenerics<'a> = ImplGenerics<'a>;
|
||||
type TypeGenerics<'a> = TypeGenerics<'a>;
|
||||
type WhereClause<'a> = Option<&'a WhereClause>;
|
||||
fn split_for_impl(
|
||||
&self,
|
||||
) -> (
|
||||
Self::ImplGenerics<'_>,
|
||||
Self::TypeGenerics<'_>,
|
||||
Self::WhereClause<'_>,
|
||||
) {
|
||||
Generics::split_for_impl(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl SplitForImpl for ParsedGenerics {
|
||||
type ImplGenerics<'a> = ParsedGenericsImplGenerics<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
type TypeGenerics<'a> = ParsedGenericsTypeGenerics<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
type WhereClause<'a> = ParsedGenericsWhereClause<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn split_for_impl(
|
||||
&self,
|
||||
) -> (
|
||||
Self::ImplGenerics<'_>,
|
||||
Self::TypeGenerics<'_>,
|
||||
Self::WhereClause<'_>,
|
||||
) {
|
||||
(
|
||||
ParsedGenericsImplGenerics(self),
|
||||
ParsedGenericsTypeGenerics(self),
|
||||
ParsedGenericsWhereClause(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum MaybeParsed<P, U> {
|
||||
Unrecognized(U),
|
||||
Parsed(P),
|
||||
}
|
||||
|
||||
impl<P: ToTokens, U: ToTokens> ToTokens for MaybeParsed<P, U> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
MaybeParsed::Unrecognized(v) => v.to_tokens(tokens),
|
||||
MaybeParsed::Parsed(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_token_stream(&self) -> TokenStream {
|
||||
match self {
|
||||
MaybeParsed::Unrecognized(v) => v.to_token_stream(),
|
||||
MaybeParsed::Parsed(v) => v.to_token_stream(),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_token_stream(self) -> TokenStream {
|
||||
match self {
|
||||
MaybeParsed::Unrecognized(v) => v.into_token_stream(),
|
||||
MaybeParsed::Parsed(v) => v.into_token_stream(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsTurbofish, U: AsTurbofish> AsTurbofish for MaybeParsed<P, U> {
|
||||
type Turbofish<'a> = MaybeParsed<P::Turbofish<'a>, U::Turbofish<'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn as_turbofish(&self) -> Self::Turbofish<'_> {
|
||||
match self {
|
||||
MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.as_turbofish()),
|
||||
MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.as_turbofish()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: SplitForImpl, U: SplitForImpl> SplitForImpl for MaybeParsed<P, U> {
|
||||
type ImplGenerics<'a> = MaybeParsed<P::ImplGenerics<'a>, U::ImplGenerics<'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
type TypeGenerics<'a> = MaybeParsed<P::TypeGenerics<'a>, U::TypeGenerics<'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
type WhereClause<'a> = MaybeParsed<P::WhereClause<'a>, U::WhereClause<'a>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn split_for_impl(
|
||||
&self,
|
||||
) -> (
|
||||
Self::ImplGenerics<'_>,
|
||||
Self::TypeGenerics<'_>,
|
||||
Self::WhereClause<'_>,
|
||||
) {
|
||||
match self {
|
||||
MaybeParsed::Unrecognized(v) => {
|
||||
let (i, t, w) = v.split_for_impl();
|
||||
(
|
||||
MaybeParsed::Unrecognized(i),
|
||||
MaybeParsed::Unrecognized(t),
|
||||
MaybeParsed::Unrecognized(w),
|
||||
)
|
||||
}
|
||||
MaybeParsed::Parsed(v) => {
|
||||
let (i, t, w) = v.split_for_impl();
|
||||
(
|
||||
MaybeParsed::Parsed(i),
|
||||
MaybeParsed::Parsed(t),
|
||||
MaybeParsed::Parsed(w),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ use syn::{
|
|||
};
|
||||
|
||||
mod fold;
|
||||
mod hdl_bundle;
|
||||
mod hdl_enum;
|
||||
mod hdl_type_common;
|
||||
mod module;
|
||||
mod value_derive_common;
|
||||
mod value_derive_enum;
|
||||
|
@ -728,3 +731,15 @@ pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
|
|||
)),
|
||||
}
|
||||
}
|
||||
|
||||
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) => hdl_enum::hdl_enum(item),
|
||||
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
|
||||
_ => Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"top-level #[hdl] can only be used on structs or enums",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1290,7 +1290,10 @@ impl Visitor {
|
|||
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,
|
||||
|
|
|
@ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
// intentionally not documented here, see `fayalite::hdl` for docs
|
||||
#[proc_macro_attribute]
|
||||
pub fn hdl(
|
||||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) {
|
||||
Ok(retval) => retval.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,671 +1,185 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
bundle::{BundleType, BundleValue},
|
||||
expr::{
|
||||
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
intern::{Intern, Interned, InternedCompare, Memoize},
|
||||
module::{
|
||||
transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
ModuleBuilder, NormalModule,
|
||||
},
|
||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
||||
int::{DynSize, KnownSize, Size},
|
||||
intern::{Intern, Interned},
|
||||
module::{ModuleBuilder, NormalModule},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
|
||||
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
|
||||
StaticValue, Type, TypeEnum, Value, ValueEnum,
|
||||
CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
|
||||
},
|
||||
util::{ConstBool, GenericConstBool, MakeMutSlice},
|
||||
};
|
||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
fmt,
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
ops::IndexMut,
|
||||
sync::Arc,
|
||||
util::ConstUsize,
|
||||
};
|
||||
use std::ops::Index;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct ArrayImpl<T: Type, Len: Size> {
|
||||
element: T,
|
||||
len: Len::SizeType,
|
||||
type_properties: TypeProperties,
|
||||
}
|
||||
|
||||
pub trait ValueArrayOrSlice:
|
||||
sealed::Sealed
|
||||
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ Hash
|
||||
+ fmt::Debug
|
||||
+ Eq
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
|
||||
+ ToOwned
|
||||
+ InternedCompare
|
||||
{
|
||||
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
|
||||
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
|
||||
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
|
||||
type Match: 'static
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ fmt::Debug
|
||||
+ Hash
|
||||
+ Send
|
||||
+ Sync
|
||||
+ BorrowMut<[Expr<Self::Element>]>;
|
||||
type MaskVA: ValueArrayOrSlice<
|
||||
Element = <Self::ElementType as Type>::MaskValue,
|
||||
ElementType = <Self::ElementType as Type>::MaskType,
|
||||
LenType = Self::LenType,
|
||||
MaskVA = Self::MaskVA,
|
||||
> + ?Sized;
|
||||
type IsStaticLen: GenericConstBool;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType>;
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
|
||||
fn len_from_len_type(v: Self::LenType) -> usize;
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
|
||||
fn len_type(&self) -> Self::LenType;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn iter(&self) -> std::slice::Iter<Self::Element> {
|
||||
Borrow::<[_]>::borrow(self).iter()
|
||||
}
|
||||
fn clone_to_arc(&self) -> Arc<Self>;
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||
impl_: Interned<ArrayImpl<T, Len>>,
|
||||
}
|
||||
|
||||
impl<T> sealed::Sealed for [T] {}
|
||||
|
||||
impl<V: Value> ValueArrayOrSlice for [V]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = usize;
|
||||
type Match = Box<[Expr<V>]>;
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue];
|
||||
type IsStaticLen = ConstBool<false>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
|
||||
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
(0..array.canonical_type().len())
|
||||
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn len_from_len_type(v: Self::LenType) -> usize {
|
||||
v
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn len_type(&self) -> Self::LenType {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
<[_]>::len(self)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
<[_]>::is_empty(self)
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::from(self)
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
MakeMutSlice::make_mut_slice(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ArrayImpl {
|
||||
element, len: _, ..
|
||||
} = *self.impl_;
|
||||
write!(f, "Array<{element:?}, {}>", self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> sealed::Sealed for [T; N] {}
|
||||
pub type Array<
|
||||
T = CanonicalType,
|
||||
const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
|
||||
> = ArrayType<T, ConstUsize<LEN>>;
|
||||
|
||||
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = ();
|
||||
type Match = [Expr<V>; N];
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
|
||||
type IsStaticLen = ConstBool<true>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
std::array::from_fn(|index| {
|
||||
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
|
||||
})
|
||||
}
|
||||
|
||||
fn len_from_len_type(_v: Self::LenType) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
if v == N {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn len_type(&self) -> Self::LenType {}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
N == 0
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
Arc::make_mut(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element: VA::ElementType,
|
||||
len: VA::LenType,
|
||||
bit_width: usize,
|
||||
}
|
||||
|
||||
pub trait ArrayTypeTrait:
|
||||
Type<
|
||||
CanonicalType = ArrayType<[DynCanonicalValue]>,
|
||||
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
|
||||
CanonicalValue = Array<[DynCanonicalValue]>,
|
||||
MaskType = ArrayType<
|
||||
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
|
||||
>,
|
||||
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ sealed::Sealed
|
||||
+ Connect<Self>
|
||||
{
|
||||
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
|
||||
+ ?Sized;
|
||||
type Element: Value<Type = Self::ElementType>;
|
||||
type ElementType: Type<Value = Self::Element>;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
|
||||
type ValueArrayOrSlice = VA;
|
||||
type Element = VA::Element;
|
||||
type ElementType = VA::ElementType;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element: self.element.clone(),
|
||||
len: self.len,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
|
||||
pub fn element(&self) -> &VA::ElementType {
|
||||
&self.element
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.len)
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn bit_width(&self) -> usize {
|
||||
self.bit_width
|
||||
}
|
||||
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
|
||||
ArrayType {
|
||||
len: self.len(),
|
||||
element: self.element,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
|
||||
Self::new_with_len_type(
|
||||
element,
|
||||
VA::try_len_type_from_len(len).expect("length should match"),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
|
||||
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
|
||||
panic!("array is too big: bit-width overflowed");
|
||||
impl<T: Type, Len: Size> ArrayType<T, Len> {
|
||||
pub fn new(element: T, len: Len::SizeType) -> Self {
|
||||
let element_props = element.canonical().type_properties();
|
||||
let type_properties = TypeProperties {
|
||||
is_passive: element_props.is_passive,
|
||||
is_storable: element_props.is_storable,
|
||||
is_castable_from_bits: element_props.is_castable_from_bits,
|
||||
bit_width: element_props
|
||||
.bit_width
|
||||
.checked_mul(Len::as_usize(len))
|
||||
.expect("array too big"),
|
||||
};
|
||||
ArrayType {
|
||||
element,
|
||||
len,
|
||||
bit_width,
|
||||
Self {
|
||||
impl_: ArrayImpl {
|
||||
element,
|
||||
len,
|
||||
type_properties,
|
||||
}
|
||||
.intern_sized(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Fold<State>,
|
||||
{
|
||||
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
state.fold_array_type(self)
|
||||
pub fn element(&self) -> &T {
|
||||
&self.impl_.element
|
||||
}
|
||||
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
|
||||
pub fn len(self) -> usize {
|
||||
Len::as_usize(self.impl_.len)
|
||||
}
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
self.impl_.type_properties
|
||||
}
|
||||
pub fn as_dyn_array(self) -> Array {
|
||||
Array::new_dyn(self.element().canonical(), self.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Visit<State>,
|
||||
{
|
||||
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
state.visit_array_type(self)
|
||||
}
|
||||
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
self.element.visit(state)
|
||||
impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
|
||||
pub fn new_static(element: T) -> Self {
|
||||
Self::new(element, Len::SizeType::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
|
||||
pub fn new_array(element: V::Type) -> Self {
|
||||
ArrayType::new_with_len_type(element, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
|
||||
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
||||
fn static_type() -> Self {
|
||||
Self::new_array(StaticType::static_type())
|
||||
Self::new_static(T::static_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> {
|
||||
pub fn new_slice(element: V::Type, len: usize) -> Self {
|
||||
ArrayType::new_with_len_type(element, len)
|
||||
impl<T: Type> Array<T> {
|
||||
pub fn new_dyn(element: T, len: usize) -> Self {
|
||||
Self::new(element, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
|
||||
type CanonicalType = ArrayType<[DynCanonicalValue]>;
|
||||
type Value = Array<VA>;
|
||||
type CanonicalValue = Array<[DynCanonicalValue]>;
|
||||
type MaskType = ArrayType<VA::MaskVA>;
|
||||
type MaskValue = Array<VA::MaskVA>;
|
||||
type MatchVariant = VA::Match;
|
||||
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||
type MaskType = ArrayType<T::MaskType, Len>;
|
||||
type MatchVariant = Len::ArrayMatch<T>;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
|
||||
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
|
||||
|
||||
fn match_variants<IO: BundleValue>(
|
||||
this: Expr<Self::Value>,
|
||||
module_builder: &mut ModuleBuilder<IO, NormalModule>,
|
||||
fn match_variants(
|
||||
this: Expr<Self>,
|
||||
module_builder: &mut ModuleBuilder<NormalModule>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter
|
||||
where
|
||||
IO::Type: BundleType<Value = IO>,
|
||||
{
|
||||
) -> Self::MatchVariantsIter {
|
||||
let base = Expr::as_dyn_array(this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let _ = module_builder;
|
||||
let _ = source_location;
|
||||
std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
|
||||
let retval = Vec::from_iter(
|
||||
(0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()),
|
||||
);
|
||||
std::iter::once(MatchVariantWithoutScope(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
|
||||
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
|
||||
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
|
||||
type Input = ArrayType<T::ValueArrayOrSlice>;
|
||||
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
|
||||
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
|
||||
}
|
||||
}
|
||||
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
|
||||
ArrayType::new(self.element().mask_type(), self.impl_.len)
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
ArrayType {
|
||||
element: self.element.canonical_dyn(),
|
||||
len: self.len(),
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
#[track_caller]
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Array(array) = canonical_type else {
|
||||
panic!("expected array");
|
||||
};
|
||||
Self::new(
|
||||
T::from_canonical(*array.element()),
|
||||
Len::from_usize(array.len()),
|
||||
)
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::ArrayType(self.canonical())
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
Self {
|
||||
element: VA::ElementType::from_dyn_canonical_type(t.element),
|
||||
len: VA::try_len_type_from_len(t.len).expect("length should match"),
|
||||
bit_width: t.bit_width,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
|
||||
this,
|
||||
)?)
|
||||
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
||||
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
||||
let base = Expr::as_dyn_array(*this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let retval = Vec::from_iter(
|
||||
(0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()),
|
||||
);
|
||||
Interned::<_>::into_inner(Intern::intern_sized(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
|
||||
for ArrayType<Lhs>
|
||||
{
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||
pub struct ArrayWithoutGenerics;
|
||||
|
||||
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
|
||||
}
|
||||
impl<T: Type> Index<T> for ArrayWithoutGenerics {
|
||||
type Output = ArrayWithoutLen<T>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element_ty: VA::ElementType,
|
||||
value: Arc<VA>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element_ty: self.element_ty.clone(),
|
||||
value: self.value.clone(),
|
||||
}
|
||||
fn index(&self, element: T) -> &Self::Output {
|
||||
Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
|
||||
type Type = ArrayType<VA>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ArrayWithoutLen<T: Type> {
|
||||
element: T,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
Array {
|
||||
element_ty: self.element_ty.canonical_dyn(),
|
||||
value: AsRef::<[_]>::as_ref(&*self.value)
|
||||
.iter()
|
||||
.map(|v| v.to_canonical_dyn())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
|
||||
type Input = Array<VA>;
|
||||
type InputOwned = Array<VA>;
|
||||
type Output = Interned<BitSlice>;
|
||||
impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
|
||||
type Output = Array<T>;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
let mut bits = BitVec::with_capacity(input.ty().bit_width());
|
||||
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
|
||||
bits.extend_from_bitslice(&element.to_bits());
|
||||
}
|
||||
Intern::intern_owned(bits)
|
||||
}
|
||||
}
|
||||
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Array<[DynCanonicalValue]> {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Array(this.clone())
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
Value::to_bits_impl(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
|
||||
pub fn element_ty(&self) -> &VA::ElementType {
|
||||
&self.element_ty
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.value.len_type())
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn value(&self) -> &Arc<VA> {
|
||||
&self.value
|
||||
}
|
||||
pub fn set_element(&mut self, index: usize, element: VA::Element) {
|
||||
assert_eq!(self.element_ty, element.ty());
|
||||
VA::arc_make_mut(&mut self.value)[index] = element;
|
||||
}
|
||||
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
|
||||
for element in value.iter() {
|
||||
assert_eq!(element_ty, element.ty());
|
||||
}
|
||||
Self { element_ty, value }
|
||||
}
|
||||
pub fn into_slice(self) -> Array<[VA::Element]> {
|
||||
Array {
|
||||
element_ty: self.element_ty,
|
||||
value: self.value.arc_to_arc_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
|
||||
where
|
||||
VA::Element: StaticValue,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(StaticType::static_type(), value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
<[E]>::ty(self)
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
<[E]>::to_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
|
||||
type Type = ArrayType<[T::Value; N]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), ())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
|
||||
array: Arc<VA>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
|
||||
type Item = VA::Element;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next()?].clone())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next_back()?].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Array<VA> {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
|
||||
type Item = &'a VA::Element;
|
||||
type IntoIter = std::slice::Iter<'a, VA::Element>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
|
||||
type Item = VA::Element;
|
||||
type IntoIter = ArrayIntoIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ArrayIntoIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
|
||||
array: Expr<Array<VA>>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
|
||||
type Item = Expr<VA::Element>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
|
||||
pub fn len(self) -> usize {
|
||||
self.canonical_type().len()
|
||||
}
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.canonical_type().is_empty()
|
||||
}
|
||||
pub fn iter(self) -> ArrayExprIter<VA> {
|
||||
ArrayExprIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self,
|
||||
}
|
||||
fn index(&self, len: usize) -> &Self::Output {
|
||||
Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,150 +1,58 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use crate::{
|
||||
bundle::{BundleValue, TypeHintTrait},
|
||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
||||
int::{UInt, UIntType},
|
||||
intern::{Intern, Interned, MemoizeGeneric},
|
||||
hdl,
|
||||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
module::{
|
||||
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
|
||||
NormalModule, Scope,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
|
||||
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties},
|
||||
};
|
||||
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
||||
use hashbrown::HashMap;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter::FusedIterator,
|
||||
marker::PhantomData,
|
||||
};
|
||||
use std::iter::FusedIterator;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct VariantType<T> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct EnumVariant {
|
||||
pub name: Interned<str>,
|
||||
pub ty: Option<T>,
|
||||
}
|
||||
|
||||
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let VariantType { name, ref ty } = *self.0;
|
||||
if let Some(ty) = ty {
|
||||
write!(f, "{name}({ty:?})")
|
||||
} else {
|
||||
write!(f, "{name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VariantType<T> {
|
||||
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
|
||||
let Self { name, ty } = self;
|
||||
VariantType { name, ty: f(ty) }
|
||||
}
|
||||
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
|
||||
let Self { name, ty } = self;
|
||||
VariantType {
|
||||
name,
|
||||
ty: ty.map(f),
|
||||
}
|
||||
}
|
||||
pub fn as_ref_ty(&self) -> VariantType<&T> {
|
||||
VariantType {
|
||||
name: self.name,
|
||||
ty: self.ty.as_ref(),
|
||||
}
|
||||
}
|
||||
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
|
||||
FmtDebugInEnum(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> VariantType<T> {
|
||||
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
|
||||
self.as_ref_ty().map_ty(T::canonical)
|
||||
}
|
||||
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
|
||||
self.as_ref_ty().map_ty(T::to_dyn)
|
||||
}
|
||||
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
|
||||
self.as_ref_ty().map_ty(T::canonical_dyn)
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantType<Interned<dyn DynCanonicalType>> {
|
||||
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
|
||||
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
|
||||
let Some(ty) = self.ty else {
|
||||
panic!("variant {expected_name} has no value but a value is expected");
|
||||
};
|
||||
T::from_dyn_canonical_type(ty)
|
||||
}
|
||||
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
|
||||
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
|
||||
assert!(
|
||||
self.ty.is_none(),
|
||||
"variant {expected_name} has a value but is expected to have no value"
|
||||
);
|
||||
}
|
||||
pub ty: Option<CanonicalType>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq)]
|
||||
struct DynEnumTypeImpl {
|
||||
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
|
||||
struct EnumImpl {
|
||||
variants: Interned<[EnumVariant]>,
|
||||
name_indexes: HashMap<Interned<str>, usize>,
|
||||
bit_width: usize,
|
||||
is_storable: bool,
|
||||
is_castable_from_bits: bool,
|
||||
type_properties: TypeProperties,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DynEnumTypeImpl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "DynEnumType ")?;
|
||||
f.debug_set()
|
||||
.entries(
|
||||
self.variants
|
||||
.iter()
|
||||
.map(|variant| variant.fmt_debug_in_enum()),
|
||||
)
|
||||
impl std::fmt::Debug for EnumImpl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Enum")
|
||||
.field("variants", &self.variants)
|
||||
.field("name_indexes", &self.name_indexes)
|
||||
.field("type_properties", &self.type_properties)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for DynEnumTypeImpl {
|
||||
impl std::hash::Hash for EnumImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.variants.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EnumImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.variants == other.variants
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for DynEnumTypeImpl {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.variants.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
|
||||
|
||||
impl fmt::Debug for DynEnumType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Enum(Interned<EnumImpl>);
|
||||
|
||||
fn discriminant_bit_width_impl(variant_count: usize) -> usize {
|
||||
variant_count
|
||||
|
@ -153,38 +61,47 @@ fn discriminant_bit_width_impl(variant_count: usize) -> usize {
|
|||
.unwrap_or(0) as usize
|
||||
}
|
||||
|
||||
impl DynEnumType {
|
||||
impl Enum {
|
||||
#[track_caller]
|
||||
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
|
||||
pub fn new(variants: Interned<[EnumVariant]>) -> Self {
|
||||
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
|
||||
let mut name_indexes = HashMap::with_capacity(variants.len());
|
||||
let mut body_bit_width = 0usize;
|
||||
let mut is_storable = true;
|
||||
let mut is_castable_from_bits = true;
|
||||
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
|
||||
if let Some(old_index) = name_indexes.insert(name, index) {
|
||||
let mut type_properties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: true,
|
||||
is_castable_from_bits: true,
|
||||
bit_width: 0,
|
||||
};
|
||||
for (index, EnumVariant { name, ty }) in variants.iter().enumerate() {
|
||||
if let Some(old_index) = name_indexes.insert(*name, index) {
|
||||
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
|
||||
}
|
||||
if let Some(ty) = ty {
|
||||
assert!(
|
||||
ty.is_passive(),
|
||||
"variant type must be a passive type: {ty:?}"
|
||||
);
|
||||
body_bit_width = body_bit_width.max(ty.bit_width());
|
||||
is_storable &= ty.is_storable();
|
||||
is_castable_from_bits &= ty.is_castable_from_bits();
|
||||
let TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
} = ty.type_properties();
|
||||
assert!(is_passive, "variant type must be a passive type: {ty:?}");
|
||||
type_properties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: type_properties.is_storable & is_storable,
|
||||
is_castable_from_bits: type_properties.is_castable_from_bits
|
||||
& is_castable_from_bits,
|
||||
bit_width: type_properties.bit_width.max(bit_width),
|
||||
};
|
||||
}
|
||||
}
|
||||
let bit_width = body_bit_width
|
||||
type_properties.bit_width = type_properties
|
||||
.bit_width
|
||||
.checked_add(discriminant_bit_width_impl(variants.len()))
|
||||
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
|
||||
Self(
|
||||
DynEnumTypeImpl {
|
||||
EnumImpl {
|
||||
variants,
|
||||
name_indexes,
|
||||
bit_width,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
type_properties,
|
||||
}
|
||||
.intern_sized(),
|
||||
)
|
||||
|
@ -192,233 +109,31 @@ impl DynEnumType {
|
|||
pub fn discriminant_bit_width(self) -> usize {
|
||||
discriminant_bit_width_impl(self.variants().len())
|
||||
}
|
||||
pub fn is_passive(self) -> bool {
|
||||
true
|
||||
}
|
||||
pub fn is_storable(self) -> bool {
|
||||
self.0.is_storable
|
||||
}
|
||||
pub fn is_castable_from_bits(self) -> bool {
|
||||
self.0.is_castable_from_bits
|
||||
}
|
||||
pub fn bit_width(self) -> usize {
|
||||
self.0.bit_width
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
self.0.type_properties
|
||||
}
|
||||
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
|
||||
&self.0.name_indexes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct DynEnum {
|
||||
ty: DynEnumType,
|
||||
variant_index: usize,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
}
|
||||
|
||||
impl DynEnum {
|
||||
#[track_caller]
|
||||
pub fn new_by_index(
|
||||
ty: DynEnumType,
|
||||
variant_index: usize,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
) -> Self {
|
||||
let variant = ty.variants()[variant_index];
|
||||
assert_eq!(
|
||||
variant_value.as_ref().map(|v| v.ty()),
|
||||
variant.ty,
|
||||
"variant value doesn't match type"
|
||||
);
|
||||
Self {
|
||||
ty,
|
||||
variant_index,
|
||||
variant_value,
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_by_name(
|
||||
ty: DynEnumType,
|
||||
variant_name: Interned<str>,
|
||||
variant_value: Option<DynCanonicalValue>,
|
||||
) -> Self {
|
||||
let variant_index = ty.name_indexes()[&variant_name];
|
||||
Self::new_by_index(ty, variant_index, variant_value)
|
||||
}
|
||||
pub fn variant_index(&self) -> usize {
|
||||
self.variant_index
|
||||
}
|
||||
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
|
||||
&self.variant_value
|
||||
}
|
||||
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
|
||||
self.ty.variants()[self.variant_index]
|
||||
}
|
||||
pub fn variant_name(&self) -> Interned<str> {
|
||||
self.variant_with_type().name
|
||||
}
|
||||
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
|
||||
self.variant_with_type().ty
|
||||
}
|
||||
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
|
||||
self.variant_with_type()
|
||||
.map_opt_ty(|_| self.variant_value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct VariantsHint {
|
||||
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
|
||||
pub more_variants: bool,
|
||||
}
|
||||
|
||||
impl VariantsHint {
|
||||
pub fn new(
|
||||
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
|
||||
more_variants: bool,
|
||||
) -> Self {
|
||||
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
|
||||
Self {
|
||||
known_variants,
|
||||
more_variants,
|
||||
}
|
||||
}
|
||||
pub fn check_variant(
|
||||
self,
|
||||
index: usize,
|
||||
variant: VariantType<&dyn DynType>,
|
||||
) -> Result<(), String> {
|
||||
let Some(&known_variant) = self.known_variants.get(index) else {
|
||||
return if self.more_variants {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"too many variants: name={:?} index={index}",
|
||||
variant.name
|
||||
))
|
||||
};
|
||||
};
|
||||
let VariantType {
|
||||
name: known_name,
|
||||
ty: type_hint,
|
||||
} = known_variant;
|
||||
let VariantType { name, ty } = variant;
|
||||
if name != known_name {
|
||||
Err(format!(
|
||||
"wrong variant name {name:?}, expected {known_name:?}"
|
||||
))
|
||||
} else {
|
||||
match (ty, type_hint) {
|
||||
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
|
||||
(None, None) => Ok(()),
|
||||
(None, Some(_)) => Err(format!(
|
||||
"expected variant {name:?} to have type, no type provided"
|
||||
)),
|
||||
(Some(_), None) => Err(format!(
|
||||
"expected variant {name:?} to have no type, but a type was provided"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumType:
|
||||
Type<
|
||||
CanonicalType = DynEnumType,
|
||||
CanonicalValue = DynEnum,
|
||||
MaskType = UIntType<1>,
|
||||
MaskValue = UInt<1>,
|
||||
MatchActiveScope = Scope,
|
||||
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
|
||||
MatchVariantsIter = EnumMatchVariantsIter<Self>,
|
||||
> + Connect<Self>
|
||||
where
|
||||
Self::Value: EnumValue + ToExpr<Type = Self>,
|
||||
MaskType = Bool,
|
||||
MatchActiveScope = Scope,
|
||||
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
|
||||
MatchVariantsIter = EnumMatchVariantsIter<Self>,
|
||||
>
|
||||
{
|
||||
type Builder;
|
||||
fn variants(&self) -> Interned<[EnumVariant]>;
|
||||
fn match_activate_scope(
|
||||
v: Self::MatchVariantAndInactiveScope,
|
||||
) -> (Self::MatchVariant, Self::MatchActiveScope);
|
||||
fn builder() -> Self::Builder;
|
||||
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
|
||||
fn variants_hint() -> VariantsHint;
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
|
||||
&self,
|
||||
variant_index: usize,
|
||||
variant_value: Option<&VariantValue>,
|
||||
) -> Result<Interned<BitSlice>, ()> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
|
||||
impl<E, V> Clone for VariantToBitsMemoize<E, V> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
|
||||
impl<
|
||||
E: EnumType<Value: EnumValue<Type = E>>,
|
||||
V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
|
||||
> MemoizeGeneric for VariantToBitsMemoize<E, V>
|
||||
{
|
||||
type InputRef<'a> = (&'a E, usize, Option<&'a V>);
|
||||
type InputOwned = (E, usize, Option<V>);
|
||||
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
|
||||
type Output = Result<Interned<BitSlice>, ()>;
|
||||
|
||||
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
|
||||
(&input.0, input.1, input.2.as_ref())
|
||||
}
|
||||
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
|
||||
a == b
|
||||
}
|
||||
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
|
||||
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
|
||||
}
|
||||
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
|
||||
(&input.0, input.1, input.2.as_deref())
|
||||
}
|
||||
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
|
||||
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
|
||||
}
|
||||
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
|
||||
(Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
|
||||
}
|
||||
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
|
||||
let (ty, variant_index, variant_value) = input;
|
||||
let ty = ty.canonical();
|
||||
let mut bits = BitVec::with_capacity(ty.bit_width());
|
||||
bits.extend_from_bitslice(
|
||||
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
|
||||
);
|
||||
if let Some(variant_value) = variant_value {
|
||||
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
|
||||
}
|
||||
bits.resize(ty.bit_width(), false);
|
||||
Ok(Intern::intern_owned(bits))
|
||||
}
|
||||
}
|
||||
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
|
||||
self,
|
||||
variant_index,
|
||||
variant_value,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumValue: Value
|
||||
where
|
||||
<Self as ToExpr>::Type: EnumType<Value = Self>,
|
||||
{
|
||||
}
|
||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>);
|
||||
|
||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
|
||||
where
|
||||
T::Value: EnumValue<Type = T>;
|
||||
|
||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> {
|
||||
type MatchVariant = T::MatchVariant;
|
||||
type MatchActiveScope = Scope;
|
||||
|
||||
|
@ -427,36 +142,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
|
||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> {
|
||||
pub fn variant_access(&self) -> Interned<VariantAccess> {
|
||||
self.0.variant_access()
|
||||
}
|
||||
pub fn activate(
|
||||
self,
|
||||
) -> (
|
||||
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
|
||||
Scope,
|
||||
) {
|
||||
pub fn activate(self) -> (Interned<VariantAccess>, Scope) {
|
||||
self.0.activate()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EnumMatchVariantsIter<T: EnumType>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
pub struct EnumMatchVariantsIter<T: EnumType> {
|
||||
pub(crate) inner: EnumMatchVariantsIterImpl<T>,
|
||||
pub(crate) variant_index: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> {
|
||||
type Item = EnumMatchVariantAndInactiveScope<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -470,21 +171,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> {
|
||||
fn len(&self) -> usize {
|
||||
self.variant_index.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {}
|
||||
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {}
|
||||
|
||||
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T>
|
||||
where
|
||||
T::Value: EnumValue<Type = T>,
|
||||
{
|
||||
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.variant_index.next_back().map(|variant_index| {
|
||||
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
|
||||
|
@ -492,129 +187,55 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Type for DynEnumType {
|
||||
type CanonicalType = DynEnumType;
|
||||
type Value = DynEnum;
|
||||
type CanonicalValue = DynEnum;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
type MatchVariant = Option<Expr<DynCanonicalValue>>;
|
||||
type MatchActiveScope = Scope;
|
||||
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants<IO: BundleValue>(
|
||||
this: Expr<Self::Value>,
|
||||
module_builder: &mut ModuleBuilder<IO, NormalModule>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter
|
||||
where
|
||||
IO::Type: crate::bundle::BundleType<Value = IO>,
|
||||
{
|
||||
module_builder.enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::EnumType(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for DynEnumType {}
|
||||
|
||||
pub struct NoBuilder;
|
||||
|
||||
impl EnumType for DynEnumType {
|
||||
type Builder = NoBuilder;
|
||||
|
||||
impl EnumType for Enum {
|
||||
fn match_activate_scope(
|
||||
v: Self::MatchVariantAndInactiveScope,
|
||||
) -> (Self::MatchVariant, Self::MatchActiveScope) {
|
||||
let (expr, scope) = v.0.activate();
|
||||
(expr.variant_type().ty.map(|_| expr.to_expr()), scope)
|
||||
(expr.variant_type().map(|_| expr.to_expr()), scope)
|
||||
}
|
||||
|
||||
fn builder() -> Self::Builder {
|
||||
NoBuilder
|
||||
}
|
||||
|
||||
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
|
||||
fn variants(&self) -> Interned<[EnumVariant]> {
|
||||
self.0.variants
|
||||
}
|
||||
}
|
||||
|
||||
fn variants_hint() -> VariantsHint {
|
||||
VariantsHint {
|
||||
known_variants: [][..].intern(),
|
||||
more_variants: true,
|
||||
}
|
||||
impl Type for Enum {
|
||||
type MaskType = Bool;
|
||||
type MatchVariant = Option<Expr<CanonicalType>>;
|
||||
type MatchActiveScope = Scope;
|
||||
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants(
|
||||
this: Expr<Self>,
|
||||
module_builder: &mut ModuleBuilder<NormalModule>,
|
||||
source_location: crate::source_location::SourceLocation,
|
||||
) -> Self::MatchVariantsIter {
|
||||
module_builder.enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
Bool
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Enum(*self)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Enum(retval) = canonical_type else {
|
||||
panic!("expected enum");
|
||||
};
|
||||
retval
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalType for DynEnumType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType;
|
||||
}
|
||||
|
||||
impl ToExpr for DynEnum {
|
||||
type Type = DynEnumType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for DynEnum {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
self.clone()
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
this.ty
|
||||
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumValue for DynEnum {}
|
||||
|
||||
impl CanonicalValue for DynEnum {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Enum(this.clone())
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
this.ty
|
||||
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod impl_option {
|
||||
#[allow(dead_code)]
|
||||
#[derive(crate::ty::Value)]
|
||||
#[hdl(target(std::option::Option), connect_inexact, outline_generated)]
|
||||
pub enum Option<T> {
|
||||
None,
|
||||
Some(T),
|
||||
}
|
||||
#[hdl]
|
||||
pub enum HdlOption<T: Type> {
|
||||
None,
|
||||
Some(T),
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,6 @@ extern crate self as fayalite;
|
|||
pub use std as __std;
|
||||
|
||||
#[doc(inline)]
|
||||
#[doc(alias = "hdl")]
|
||||
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
|
||||
/// a [`Module`][`::fayalite::module::Module`] when called.
|
||||
/// In the function body it will implicitly create a
|
||||
|
@ -21,25 +20,82 @@ pub use std as __std;
|
|||
/// See [Fayalite Modules][crate::_docs::modules]
|
||||
pub use fayalite_proc_macros::hdl_module;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use fayalite_proc_macros::hdl;
|
||||
|
||||
#[cfg(feature = "unstable-doc")]
|
||||
pub mod _docs;
|
||||
|
||||
pub mod annotations;
|
||||
// FIXME: finish
|
||||
//pub mod annotations;
|
||||
pub mod array;
|
||||
pub mod bundle;
|
||||
pub mod cli;
|
||||
pub mod clock;
|
||||
//pub mod cli;
|
||||
//pub mod clock;
|
||||
pub mod enum_;
|
||||
pub mod expr;
|
||||
pub mod firrtl;
|
||||
//pub mod firrtl;
|
||||
pub mod int;
|
||||
pub mod intern;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod reg;
|
||||
pub mod reset;
|
||||
//pub mod memory;
|
||||
//pub mod module;
|
||||
//pub mod reg;
|
||||
//pub mod reset;
|
||||
pub mod source_location;
|
||||
pub mod ty;
|
||||
pub mod util;
|
||||
pub mod valueless;
|
||||
pub mod wire;
|
||||
//pub mod valueless;
|
||||
//pub mod wire;
|
||||
|
||||
// FIXME: switch to real module mod
|
||||
pub mod module {
|
||||
use crate::{
|
||||
enum_::{EnumMatchVariantsIter, EnumType},
|
||||
expr::{ops::VariantAccess, Expr},
|
||||
intern::Interned,
|
||||
source_location::SourceLocation,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct NormalModule;
|
||||
|
||||
pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>);
|
||||
|
||||
impl ModuleBuilder<NormalModule> {
|
||||
pub fn enum_match_variants_helper<Enum: EnumType>(
|
||||
&mut self,
|
||||
_base: Expr<Enum>,
|
||||
_source_location: SourceLocation,
|
||||
) -> EnumMatchVariantsIter<Enum> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Scope(());
|
||||
|
||||
pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> {
|
||||
variant_access: Interned<VariantAccess>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> {
|
||||
pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) {
|
||||
(self.variant_access, Scope(()))
|
||||
}
|
||||
pub(crate) fn variant_access(&self) -> Interned<VariantAccess> {
|
||||
self.variant_access
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>);
|
||||
|
||||
impl<T: EnumType> EnumMatchVariantsIterImpl<T> {
|
||||
pub(crate) fn for_variant_index(
|
||||
&self,
|
||||
_variant_index: usize,
|
||||
) -> EnumMatchVariantAndInactiveScopeImpl<T> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,10 +3,13 @@
|
|||
|
||||
mod const_bool;
|
||||
mod const_cmp;
|
||||
mod const_usize;
|
||||
mod misc;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||
#[doc(inline)]
|
||||
pub use const_usize::{ConstUsize, GenericConstUsize};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_cmp::{
|
||||
|
|
29
crates/fayalite/src/util/const_usize.rs
Normal file
29
crates/fayalite/src/util/const_usize.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// the only implementation is `ConstUsize<Self::VALUE>`
|
||||
pub trait GenericConstUsize:
|
||||
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
|
||||
{
|
||||
const VALUE: usize;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct ConstUsize<const VALUE: usize>;
|
||||
|
||||
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
|
||||
|
||||
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
|
||||
const VALUE: usize = VALUE;
|
||||
}
|
|
@ -1,32 +1,21 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
int::UInt,
|
||||
ty::{StaticValue, Value},
|
||||
};
|
||||
use fayalite::{hdl, int::UInt, ty::Type};
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S<T> {
|
||||
pub struct S<T: Type> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub enum E<T> {
|
||||
pub enum E<T: Type> {
|
||||
A,
|
||||
B {},
|
||||
C(),
|
||||
D(UInt<3>),
|
||||
E { a: UInt<3> },
|
||||
F(UInt<3>, UInt<3>),
|
||||
G(T),
|
||||
H(T, UInt<1>),
|
||||
B(UInt<3>),
|
||||
C(T),
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated, static, where(T: StaticValue))]
|
||||
pub struct S2<T> {
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S2<T: Type> {
|
||||
pub v: E<T>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue