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

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 6a18f94750
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
31 changed files with 10896 additions and 8598 deletions

View file

@ -7,13 +7,14 @@ jobs:
- uses: https://code.forgejo.org/actions/checkout@v3 - uses: https://code.forgejo.org/actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: | # FIXME: uncomment once the code works again
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 # - run: |
source "$HOME/.cargo/env" # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
echo "$PATH" >> "$GITHUB_PATH" # source "$HOME/.cargo/env"
- uses: https://github.com/Swatinem/rust-cache@v2 # echo "$PATH" >> "$GITHUB_PATH"
with: # - uses: https://github.com/Swatinem/rust-cache@v2
save-if: ${{ github.ref == 'refs/heads/master' }} # with:
- run: cargo test # save-if: ${{ github.ref == 'refs/heads/master' }}
- run: cargo test --features=unstable-doc # - run: cargo test
- run: cargo doc --features=unstable-doc # - run: cargo test --features=unstable-doc
# - run: cargo doc --features=unstable-doc

View file

@ -0,0 +1,847 @@
use crate::{
hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField,
ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst,
},
kw, Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens};
use syn::{
parse_quote, parse_quote_spanned,
punctuated::{Pair, Punctuated},
spanned::Spanned,
token::Brace,
AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed,
GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility,
};
#[derive(Clone, Debug)]
pub(crate) struct ParsedBundle {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct],
pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>,
pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) match_variant_ident: Ident,
pub(crate) builder_ident: Ident,
pub(crate) mask_type_builder_ident: Ident,
}
impl ParsedBundle {
fn parse_field(
errors: &mut Errors,
field: &mut Field,
index: usize,
) -> Option<HdlAttr<kw::flip>> {
let Field {
attrs,
vis: _,
mutability,
ident,
colon_token,
ty,
} = field;
let ident = ident.get_or_insert_with(|| format_ident!("_{}", index, span = ty.span()));
if !matches!(mutability, FieldMutability::None) {
// FIXME: use mutability as the spanned tokens,
// blocked on https://github.com/dtolnay/syn/issues/1717
errors.error(&ident, "field mutability is not supported");
*mutability = FieldMutability::None;
}
*mutability = FieldMutability::None;
colon_token.get_or_insert(Token![:](ident.span()));
let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs));
options
}
fn parse(item: ItemStruct) -> syn::Result<Self> {
let ItemStruct {
mut attrs,
vis,
struct_token,
ident,
mut generics,
fields,
semi_token,
} = item;
let mut errors = Errors::new();
let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
errors.ok(options.body.validate());
let ItemOptions {
outline_generated: _,
connect_inexact: _,
target: _,
custom_bounds,
no_static: _,
} = options.body;
let mut fields = match fields {
syn::Fields::Named(fields) => fields,
syn::Fields::Unnamed(fields) => {
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
FieldsNamed {
brace_token: Brace(fields.paren_token.span),
named: fields.unnamed,
}
}
syn::Fields::Unit => {
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
FieldsNamed {
brace_token: Brace(semi_token.unwrap_or_default().span),
named: Punctuated::default(),
}
}
};
let mut field_flips = Vec::with_capacity(fields.named.len());
for (index, field) in fields.named.iter_mut().enumerate() {
field_flips.push(Self::parse_field(&mut errors, field, index));
}
let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
MaybeParsed::Parsed(generics)
} else {
MaybeParsed::Unrecognized(generics)
};
let fields = TypesParser::maybe_run(generics.as_ref(), fields, &mut errors);
errors.finish()?;
Ok(Self {
attrs,
options,
vis,
struct_token,
generics,
fields,
field_flips,
mask_type_ident: format_ident!("__{}__MaskType", ident),
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident),
builder_ident: format_ident!("__{}__Builder", ident),
ident,
})
}
}
#[derive(Clone, Debug)]
struct Builder {
vis: Visibility,
struct_token: Token![struct],
ident: Ident,
target: Path,
generics: Generics,
fields: FieldsNamed,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum BuilderFieldState {
Unfilled,
Generic,
Filled,
}
impl Builder {
fn phantom_field_name(&self) -> Ident {
format_ident!("__phantom", span = self.ident.span())
}
fn phantom_field(&self) -> Field {
let target = &self.target;
let type_generics = self.generics.split_for_impl().1;
Field {
attrs: vec![],
vis: Visibility::Inherited,
mutability: FieldMutability::None,
ident: Some(self.phantom_field_name()),
colon_token: Some(Token![:](self.ident.span())),
ty: parse_quote_spanned! {self.ident.span()=>
::fayalite::__std::marker::PhantomData<#target #type_generics>
},
}
}
fn builder_struct_generics(
&self,
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
) -> Generics {
let mut retval = self.generics.clone();
for param in retval.params.iter_mut() {
match param {
GenericParam::Lifetime(_) => {}
GenericParam::Type(param) => param.default = None,
GenericParam::Const(param) => param.default = None,
}
}
for (field_index, field) in self.fields.named.iter().enumerate() {
match get_field_state(field_index) {
BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue,
BuilderFieldState::Generic => {}
}
if !retval.params.empty_or_trailing() {
retval.params.push_punct(Token![,](self.ident.span()));
}
retval.params.push_value(GenericParam::Type(
type_var_for_field_name(field.ident.as_ref().unwrap()).into(),
));
}
retval
}
fn builder_struct_ty(
&self,
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
) -> Type {
let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() {
parse_quote_spanned! {self.ident.span()=>
<>
}
} else {
let builder_type_generics = self.generics.split_for_impl().1;
parse_quote! { #builder_type_generics }
};
for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() {
let ident = ident.as_ref().unwrap();
if !ty_arguments.args.empty_or_trailing() {
ty_arguments.args.push_punct(Token![,](self.ident.span()));
}
ty_arguments
.args
.push_value(match get_field_state(field_index) {
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
::fayalite::bundle::Unfilled<#ty>
},
BuilderFieldState::Generic => {
let type_var = type_var_for_field_name(ident);
parse_quote_spanned! {self.ident.span()=>
#type_var
}
}
BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=>
::fayalite::expr::Expr<#ty>
},
});
}
let ident = &self.ident;
parse_quote_spanned! {ident.span()=>
#ident #ty_arguments
}
}
}
fn type_var_for_field_name(ident: &Ident) -> Ident {
format_ident!("__T_{}", ident)
}
fn field_fn_for_field_name(ident: &Ident) -> Ident {
format_ident!("field_{}", ident)
}
impl ToTokens for Builder {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
vis,
struct_token,
ident,
target,
generics: _,
fields,
} = self;
let phantom_field_name = self.phantom_field_name();
let builder_struct = ItemStruct {
attrs: vec![parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)]
}],
vis: vis.clone(),
struct_token: *struct_token,
ident: ident.clone(),
generics: self.builder_struct_generics(|_| BuilderFieldState::Generic),
fields: Fields::Named(FieldsNamed {
brace_token: fields.brace_token,
named: Punctuated::from_iter(
[Pair::Punctuated(
self.phantom_field(),
Token![,](self.ident.span()),
)]
.into_iter()
.chain(fields.named.pairs().map_pair_value_ref(|field| {
let ident = field.ident.as_ref().unwrap();
let type_var = type_var_for_field_name(ident);
Field {
vis: Visibility::Inherited,
ty: parse_quote_spanned! {ident.span()=>
#type_var
},
..field.clone()
}
})),
),
}),
semi_token: None,
};
builder_struct.to_tokens(tokens);
let field_idents = Vec::from_iter(
self.fields
.named
.iter()
.map(|field| field.ident.as_ref().unwrap()),
);
for (
field_index,
Field {
vis,
ident: field_ident,
ty,
..
},
) in self.fields.named.iter().enumerate()
{
let field_ident = field_ident.as_ref().unwrap();
let fn_ident = field_fn_for_field_name(field_ident);
let fn_generics = self.builder_struct_generics(|i| {
if i == field_index {
BuilderFieldState::Unfilled
} else {
BuilderFieldState::Generic
}
});
let (impl_generics, _, where_clause) = fn_generics.split_for_impl();
let unfilled_ty = self.builder_struct_ty(|i| {
if i == field_index {
BuilderFieldState::Unfilled
} else {
BuilderFieldState::Generic
}
});
let filled_ty = self.builder_struct_ty(|i| {
if i == field_index {
BuilderFieldState::Filled
} else {
BuilderFieldState::Generic
}
});
let pat_fields =
Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| {
let field_ident = field.ident.as_ref().unwrap();
if field_index == i {
quote_spanned! {self.ident.span()=>
#field_ident: _,
}
} else {
quote_spanned! {self.ident.span()=>
#field_ident,
}
}
}));
quote_spanned! {self.ident.span()=>
#[automatically_derived]
#[allow(non_camel_case_types, dead_code)]
impl #impl_generics #unfilled_ty
#where_clause
{
#vis fn #fn_ident(
self,
#field_ident: impl ::fayalite::expr::ToExpr<Type = #ty>,
) -> #filled_ty {
let Self {
#phantom_field_name: _,
#(#pat_fields)*
} = self;
let #field_ident = ::fayalite::expr::ToExpr::to_expr(&#field_ident);
#ident {
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
#(#field_idents,)*
}
}
}
}
.to_tokens(tokens);
}
let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled);
let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled);
let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl();
quote_spanned! {self.ident.span()=>
#[automatically_derived]
#[allow(non_camel_case_types, dead_code)]
impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty
#unfilled_where_clause
{
fn default() -> Self {
#ident {
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
}
}
}
}
.to_tokens(tokens);
let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled);
let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled);
let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl();
let type_generics = self.generics.split_for_impl().1;
quote_spanned! {self.ident.span()=>
#[automatically_derived]
#[allow(non_camel_case_types, dead_code)]
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
#filled_where_clause
{
type Type = #target #type_generics;
fn to_expr(
&self,
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
let __ty = #target {
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
};
let __field_values = [
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
];
::fayalite::expr::ToExpr::to_expr(
&::fayalite::expr::ops::BundleLiteral::new(
__ty,
::fayalite::intern::Intern::intern(&__field_values[..]),
),
)
}
}
}
.to_tokens(tokens);
}
}
impl ToTokens for ParsedBundle {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
options,
vis,
struct_token,
ident,
generics,
fields,
field_flips,
mask_type_ident,
mask_type_match_variant_ident,
match_variant_ident,
builder_ident,
mask_type_builder_ident,
} = self;
let ItemOptions {
outline_generated: _,
connect_inexact,
target,
custom_bounds: _,
no_static,
} = &options.body;
let target = get_target(target, ident);
let mut item_attrs = attrs.clone();
item_attrs.push(common_derives(ident.span()));
ItemStruct {
attrs: item_attrs,
vis: vis.clone(),
struct_token: *struct_token,
ident: ident.clone(),
generics: generics.into(),
fields: Fields::Named(fields.clone().into()),
semi_token: None,
}
.to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields)) = (generics, fields) {
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
let fields: Vec<_> = fields
.named
.iter()
.map(|ParsedField { ident, ty, .. }| {
let ident = ident.as_ref().unwrap();
let expr = ty.make_hdl_type_expr(context);
quote_spanned! {ident.span()=>
#ident: #expr,
}
})
.collect();
parse_quote_spanned! {ident.span()=>
#target {
#(#fields)*
}
}
})
}
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner();
let builder = Builder {
vis: vis.clone(),
struct_token: *struct_token,
ident: builder_ident.clone(),
target: target.clone(),
generics: generics.into(),
fields: fields.clone().into(),
};
builder.to_tokens(tokens);
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled);
let mut mask_type_fields = FieldsNamed::from(fields.clone());
for Field { ident, ty, .. } in &mut mask_type_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
<#ty as ::fayalite::ty::Type>::MaskType
};
}
let mask_type_builder = Builder {
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_builder_ident.clone(),
target: mask_type_ident.clone().into(),
generics: generics.into(),
fields: mask_type_fields.clone(),
};
mask_type_builder.to_tokens(tokens);
let unfilled_mask_type_builder_ty =
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
let filled_mask_type_builder_ty =
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
ItemStruct {
attrs: vec![
common_derives(ident.span()),
parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)]
},
],
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_ident.clone(),
generics: generics.into(),
fields: Fields::Named(mask_type_fields.clone()),
semi_token: None,
}
.to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields;
for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
attrs: vec![
common_derives(ident.span()),
parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)]
},
],
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_match_variant_ident.clone(),
generics: generics.into(),
fields: Fields::Named(mask_type_match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
let mut match_variant_fields = FieldsNamed::from(fields.clone());
for Field { ident, ty, .. } in &mut match_variant_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
attrs: vec![
common_derives(ident.span()),
parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types, dead_code)]
},
],
vis: vis.clone(),
struct_token: *struct_token,
ident: match_variant_ident.clone(),
generics: generics.into(),
fields: Fields::Named(match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ident_str = ident.to_string();
quote_spanned! {ident.span()=>
#ident: ::fayalite::expr::Expr::field(__this, #ident_str),
}
}));
let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {ident.span()=>
#ident: ::fayalite::ty::Type::mask_type(&self.#ident),
}
}));
let from_canonical_body_fields =
Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map(
|((index, field), flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ident_str = ident.to_string();
let flipped = flip.is_some();
quote_spanned! {ident.span()=>
#ident: {
let ::fayalite::bundle::BundleField {
name: __name,
flipped: __flipped,
ty: __ty,
} = __fields[#index];
::fayalite::__std::assert_eq!(&*__name, #ident_str);
::fayalite::__std::assert_eq!(__flipped, #flipped);
::fayalite::ty::Type::from_canonical(__ty)
},
}
},
));
let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(
|(field, flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ident_str = ident.to_string();
let flipped = flip.is_some();
quote_spanned! {ident.span()=>
::fayalite::bundle::BundleField {
name: ::fayalite::intern::Intern::intern(#ident_str),
flipped: #flipped,
ty: ::fayalite::ty::Type::canonical(&self.#ident),
},
}
},
));
let fields_len = fields.named().into_iter().len();
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
#where_clause
{
type BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #mask_type_match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
__this: ::fayalite::expr::Expr<Self>,
__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 BaseType = ::fayalite::bundle::Bundle;
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
__this: ::fayalite::expr::Expr<Self>,
__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);
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
let static_generics = generics.clone().for_static_type();
let (static_impl_generics, static_type_generics, static_where_clause) =
static_generics.split_for_impl();
let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ty = field.ty();
quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::TYPE,
}
}));
let static_mask_type_body_fields =
Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
let ty = field.ty();
quote_spanned! {ident.span()=>
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
}
}));
let type_properties = format_ident!("__type_properties", span = ident.span());
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some();
let ty = field.ty();
quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES);
}
}));
let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
let ident: &Ident = field.ident().as_ref().unwrap();
let flipped = field_flip.is_some();
let ty = field.ty();
quote_spanned! {ident.span()=>
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
}
}));
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
#static_where_clause
{
const TYPE: Self = Self {
#(#static_mask_type_body_fields)*
};
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = Self {
#(#static_mask_type_body_fields)*
};
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
#static_where_clause
{
const TYPE: Self = Self {
#(#static_type_body_fields)*
};
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = #mask_type_ident {
#(#static_mask_type_body_fields)*
};
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_fields)*
#type_properties.finish()
};
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
#(#type_properties_mask_fields)*
#type_properties.finish()
};
}
}
.to_tokens(tokens);
}
}
}
pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> {
let item = ParsedBundle::parse(item)?;
let outline_generated = item.options.body.outline_generated;
let mut contents = item.to_token_stream();
if outline_generated.is_some() {
contents = crate::outline_generated(contents, "hdl-bundle-");
}
Ok(contents)
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,10 @@
#![cfg_attr(test, recursion_limit = "512")] #![cfg_attr(test, recursion_limit = "512")]
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use std::io::{ErrorKind, Write}; use std::{
convert::Infallible,
io::{ErrorKind, Write},
};
use syn::{ use syn::{
bracketed, parenthesized, bracketed, parenthesized,
parse::{Parse, ParseStream, Parser}, parse::{Parse, ParseStream, Parser},
@ -13,6 +16,9 @@ use syn::{
}; };
mod fold; mod fold;
mod hdl_bundle;
mod hdl_enum;
mod hdl_type_common;
mod module; mod module;
mod value_derive_common; mod value_derive_common;
mod value_derive_enum; mod value_derive_enum;
@ -43,6 +49,7 @@ mod kw {
custom_keyword!(clock_domain); custom_keyword!(clock_domain);
custom_keyword!(connect_inexact); custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
custom_keyword!(flip); custom_keyword!(flip);
custom_keyword!(hdl); custom_keyword!(hdl);
custom_keyword!(input); custom_keyword!(input);
@ -52,6 +59,7 @@ mod kw {
custom_keyword!(memory_array); custom_keyword!(memory_array);
custom_keyword!(memory_with_init); custom_keyword!(memory_with_init);
custom_keyword!(no_reset); custom_keyword!(no_reset);
custom_keyword!(no_static);
custom_keyword!(outline_generated); custom_keyword!(outline_generated);
custom_keyword!(output); custom_keyword!(output);
custom_keyword!(reg_builder); custom_keyword!(reg_builder);
@ -460,6 +468,15 @@ impl Errors {
self.push(Error::new_spanned(tokens, message)); self.push(Error::new_spanned(tokens, message));
self self
} }
pub(crate) fn fatal_error(
mut self,
tokens: impl ToTokens,
message: impl std::fmt::Display,
) -> syn::Result<Infallible> {
self.push(Error::new_spanned(tokens, message));
self.finish()?;
unreachable!()
}
pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> { pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> {
match v { match v {
Ok(v) => Some(v), Ok(v) => Some(v),
@ -519,6 +536,26 @@ macro_rules! impl_extra_traits_for_options {
) => { ) => {
impl Copy for $option_enum_name {} impl Copy for $option_enum_name {}
impl PartialEq for $option_enum_name {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl Eq for $option_enum_name {}
impl PartialOrd for $option_enum_name {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for $option_enum_name {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.variant().cmp(&other.variant())
}
}
impl quote::IdentFragment for $option_enum_name { impl quote::IdentFragment for $option_enum_name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let _ = f; let _ = f;
@ -554,6 +591,66 @@ pub(crate) use impl_extra_traits_for_options;
macro_rules! options { macro_rules! options {
( (
#[options = $options_name:ident] #[options = $options_name:ident]
$($tt:tt)*
) => {
crate::options! {
#[options = $options_name, punct = syn::Token![,], allow_duplicates = false]
$($tt)*
}
};
(
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true]
$(#[$($enum_meta:tt)*])*
$enum_vis:vis enum $option_enum_name:ident {
$($Variant:ident($key:ident $(, $value:ty)?),)*
}
) => {
crate::options! {
#[options = $options_name, punct = $Punct, allow_duplicates = (true)]
$(#[$($enum_meta)*])*
$enum_vis enum $option_enum_name {
$($Variant($key $(, $value)?),)*
}
}
impl Extend<$option_enum_name> for $options_name {
fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) {
iter.into_iter().for_each(|v| match v {
$($option_enum_name::$Variant(v) => {
self.$key = Some(v);
})*
});
}
}
impl FromIterator<$option_enum_name> for $options_name {
fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self {
let mut retval = Self::default();
retval.extend(iter);
retval
}
}
impl Extend<$options_name> for $options_name {
fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) {
iter.into_iter().for_each(|v| {
$(if let Some(v) = v.$key {
self.$key = Some(v);
})*
});
}
}
impl FromIterator<$options_name> for $options_name {
fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self {
let mut retval = Self::default();
retval.extend(iter);
retval
}
}
};
(
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr]
$(#[$($enum_meta:tt)*])* $(#[$($enum_meta:tt)*])*
$enum_vis:vis enum $option_enum_name:ident { $enum_vis:vis enum $option_enum_name:ident {
$($Variant:ident($key:ident $(, $value:ty)?),)* $($Variant:ident($key:ident $(, $value:ty)?),)*
@ -567,8 +664,11 @@ macro_rules! options {
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[allow(non_snake_case)]
$enum_vis struct $options_name { $enum_vis struct $options_name {
$($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)* $(
$enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
)*
} }
crate::fold::impl_fold! { crate::fold::impl_fold! {
@ -577,6 +677,43 @@ macro_rules! options {
} }
} }
const _: () = {
#[derive(Clone, Debug)]
$enum_vis struct Iter($enum_vis $options_name);
impl IntoIterator for $options_name {
type Item = $option_enum_name;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
Iter(self)
}
}
impl Iterator for Iter {
type Item = $option_enum_name;
fn next(&mut self) -> Option<Self::Item> {
$(
if let Some(value) = self.0.$key.take() {
return Some($option_enum_name::$Variant(value));
}
)*
None
}
#[allow(unused_mut, unused_variables)]
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
$(
if let Some(value) = self.0.$key.take() {
init = f(init, $option_enum_name::$Variant(value));
}
)*
init
}
}
};
impl syn::parse::Parse for $options_name { impl syn::parse::Parse for $options_name {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
#![allow(unused_mut, unused_variables, unreachable_code)] #![allow(unused_mut, unused_variables, unreachable_code)]
@ -585,7 +722,7 @@ macro_rules! options {
let old_input = input.fork(); let old_input = input.fork();
match input.parse::<$option_enum_name>()? { match input.parse::<$option_enum_name>()? {
$($option_enum_name::$Variant(v) => { $($option_enum_name::$Variant(v) => {
if retval.$key.replace(v).is_some() { if retval.$key.replace(v).is_some() && !$allow_duplicates {
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option"))); return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
} }
})* })*
@ -593,7 +730,7 @@ macro_rules! options {
if input.is_empty() { if input.is_empty() {
break; break;
} }
input.parse::<syn::Token![,]>()?; input.parse::<$Punct>()?;
} }
Ok(retval) Ok(retval)
} }
@ -602,7 +739,7 @@ macro_rules! options {
impl quote::ToTokens for $options_name { impl quote::ToTokens for $options_name {
#[allow(unused_mut, unused_variables, unused_assignments)] #[allow(unused_mut, unused_variables, unused_assignments)]
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let mut separator: Option<syn::Token![,]> = None; let mut separator: Option<$Punct> = None;
$(if let Some(v) = &self.$key { $(if let Some(v) = &self.$key {
separator.to_tokens(tokens); separator.to_tokens(tokens);
separator = Some(Default::default()); separator = Some(Default::default());
@ -673,6 +810,20 @@ macro_rules! options {
} }
} }
} }
impl $option_enum_name {
#[allow(dead_code)]
fn variant(&self) -> usize {
#[repr(usize)]
enum Variant {
$($Variant,)*
__Last, // so it doesn't complain about zero-variant enums
}
match *self {
$(Self::$Variant(..) => Variant::$Variant as usize,)*
}
}
}
}; };
} }
@ -686,6 +837,15 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
.suffix(".tmp.rs") .suffix(".tmp.rs")
.tempfile_in(out_dir) .tempfile_in(out_dir)
.unwrap(); .unwrap();
struct PrintOnPanic<'a>(&'a TokenStream);
impl Drop for PrintOnPanic<'_> {
fn drop(&mut self) {
if std::thread::panicking() {
println!("{}", self.0);
}
}
}
let _print_on_panic = PrintOnPanic(&contents);
let contents = prettyplease::unparse(&parse_quote! { #contents }); let contents = prettyplease::unparse(&parse_quote! { #contents });
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents); let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
let hash = base16ct::HexDisplay(&hash[..5]); let hash = base16ct::HexDisplay(&hash[..5]);
@ -728,3 +888,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",
)),
}
}

View file

@ -1290,7 +1290,10 @@ impl Visitor {
memory, memory,
paren, paren,
ty_expr, 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 { MemoryFn::MemoryArray {
memory_array, memory_array,
paren, paren,

View file

@ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
Err(err) => err.into_compile_error().into(), 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(),
}
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::Target, expr::target::Target,
intern::{Intern, Interned}, intern::{Intern, Interned},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -1,671 +1,236 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
bundle::{BundleType, BundleValue}, expr::{ops::ArrayIndex, Expr, ToExpr},
expr::{ int::{DynSize, KnownSize, Size},
ops::{ArrayIndex, ArrayLiteral, ExprIndex}, intern::{Intern, Interned, LazyInterned},
Expr, ToExpr,
},
intern::{Intern, Interned, InternedCompare, Memoize},
module::{ module::{
transform::visit::{Fold, Folder, Visit, Visitor}, transform::visit::{Fold, Folder, Visit, Visitor},
ModuleBuilder, NormalModule, ModuleBuilder, NormalModule,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
StaticValue, Type, TypeEnum, Value, ValueEnum,
}, },
util::{ConstBool, GenericConstBool, MakeMutSlice}, util::ConstUsize,
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use std::ops::Index;
use std::{
any::Any, #[derive(Copy, Clone, PartialEq, Eq, Hash)]
borrow::{Borrow, BorrowMut}, pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
fmt, element: LazyInterned<T>,
hash::Hash, len: Len::SizeType,
marker::PhantomData, type_properties: TypeProperties,
ops::IndexMut, }
sync::Arc,
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Array<{:?}, {}>", self.element, self.len())
}
}
pub type Array<
T = CanonicalType,
const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
> = ArrayType<T, ConstUsize<LEN>>;
#[allow(non_upper_case_globals)]
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
#[allow(non_upper_case_globals)]
pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;
impl<T: Type, Len: Size> ArrayType<T, Len> {
const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties {
let TypeProperties {
is_passive,
is_storable,
is_castable_from_bits,
bit_width,
} = element;
let Some(bit_width) = bit_width.checked_mul(len) else {
panic!("array too big");
}; };
TypeProperties {
mod sealed { is_passive,
pub trait Sealed {} is_storable,
} is_castable_from_bits,
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]>;
}
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, const N: usize> sealed::Sealed for [T; N] {}
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(());
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");
};
ArrayType {
element,
len,
bit_width, bit_width,
} }
} }
pub fn new(element: T, len: Len::SizeType) -> Self {
let type_properties =
Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len));
Self {
element: LazyInterned::Interned(element.intern_sized()),
len,
type_properties,
}
}
pub fn element(&self) -> T {
*self.element
}
pub fn len(self) -> usize {
Len::as_usize(self.len)
}
pub fn type_properties(self) -> TypeProperties {
self.type_properties
}
pub fn as_dyn_array(self) -> Array {
Array::new_dyn(self.element().canonical(), self.len())
}
pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool {
self.len() == rhs.len()
&& self
.element()
.canonical()
.can_connect(rhs.element().canonical())
}
} }
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA> impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
where pub fn new_static(element: T) -> Self {
VA::ElementType: Fold<State>, Self::new(element, Len::SizeType::default())
{ }
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self {
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
len: Len::SIZE,
type_properties: Self::TYPE_PROPERTIES,
};
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
len: Len::SIZE,
type_properties: Self::MASK_TYPE_PROPERTIES,
};
const TYPE_PROPERTIES: TypeProperties =
Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE);
const MASK_TYPE_PROPERTIES: TypeProperties =
Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE);
}
impl<T: Type> Array<T> {
pub fn new_dyn(element: T, len: usize) -> Self {
Self::new(element, len)
}
}
impl<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> { fn fold(self, state: &mut State) -> Result<Self, State::Error> {
state.fold_array_type(self) state.fold_array_type(self)
} }
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> {
Ok(ArrayType::new(self.element().fold(state)?, self.len))
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
where for ArrayType<T, Len>
VA::ElementType: Visit<State>,
{ {
fn visit(&self, state: &mut State) -> Result<(), State::Error> { fn visit(&self, state: &mut State) -> Result<(), State::Error> {
state.visit_array_type(self) state.visit_array_type(self)
} }
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
self.element.visit(state) self.element().visit(state)
} }
} }
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> { impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
pub fn new_array(element: V::Type) -> Self { type BaseType = Array;
ArrayType::new_with_len_type(element, ()) type MaskType = ArrayType<T::MaskType, Len>;
} type MatchVariant = Len::ArrayMatch<T>;
}
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
fn static_type() -> Self {
Self::new_array(StaticType::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<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;
type MatchActiveScope = (); type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>; type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>( fn match_variants(
this: Expr<Self::Value>, this: Expr<Self>,
module_builder: &mut ModuleBuilder<IO, NormalModule>, module_builder: &mut ModuleBuilder<NormalModule>,
source_location: SourceLocation, source_location: SourceLocation,
) -> Self::MatchVariantsIter ) -> Self::MatchVariantsIter {
where let base = Expr::as_dyn_array(this);
IO::Type: BundleType<Value = IO>, let base_ty = Expr::ty(base);
{
let _ = module_builder; let _ = module_builder;
let _ = source_location; let _ = source_location;
std::iter::once(MatchVariantWithoutScope(VA::make_match(this))) let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
std::iter::once(MatchVariantWithoutScope(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
} }
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
#[derive(Clone, Hash, Eq, PartialEq)] ArrayType::new(self.element().mask_type(), self.len)
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)
} }
fn canonical(&self) -> Self::CanonicalType { fn canonical(&self) -> CanonicalType {
ArrayType { CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
element: self.element.canonical_dyn(),
len: self.len(),
bit_width: self.bit_width,
}
} }
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() SourceLocation::builtin()
} }
fn type_enum(&self) -> TypeEnum {
TypeEnum::ArrayType(self.canonical())
} }
fn from_canonical_type(t: Self::CanonicalType) -> Self { impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
Self { fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
element: VA::ElementType::from_dyn_canonical_type(t.element), let base = Expr::as_dyn_array(*this);
len: VA::try_len_type_from_len(t.len).expect("length should match"), let base_ty = Expr::ty(base);
bit_width: t.bit_width, let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
Interned::<_>::into_inner(Intern::intern_sized(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
} }
} }
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>( pub struct ArrayWithoutGenerics;
this,
)?) impl<T: Type> Index<T> for ArrayWithoutGenerics {
type Output = ArrayWithoutLen<T>;
fn index(&self, element: T) -> &Self::Output {
Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
} }
} }
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
for ArrayType<Lhs> pub struct ArrayWithoutLen<T: Type> {
{ element: T,
} }
impl CanonicalType for ArrayType<[DynCanonicalValue]> { impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; type Output = Array<T>;
}
#[derive(Debug, PartialEq, Eq, Hash)] fn index(&self, len: usize) -> &Self::Output {
pub struct Array<VA: ValueArrayOrSlice + ?Sized> { Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
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(),
}
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> { impl<T: Type, const LEN: usize> Index<ConstUsize<LEN>> for ArrayWithoutLen<T>
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)
}
}
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>;
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 where
VA::Element: StaticValue, ConstUsize<LEN>: KnownSize,
{ {
fn from(value: T) -> Self { type Output = Array<T, LEN>;
Self::new(StaticType::static_type(), value.into())
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] { fn index(&self, len: ConstUsize<LEN>) -> &Self::Output {
type Type = ArrayType<[T::Value]>; Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len)))
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,
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,111 +2,61 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, ToExpr}, expr::{Expr, ToExpr},
int::{UInt, UIntType}, hdl,
intern::Interned, int::Bool,
reset::Reset, reset::Reset,
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
},
util::interned_bit,
}; };
use bitvec::slice::BitSlice;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct ClockType; pub struct Clock;
impl ClockType { impl Type for Clock {
pub const fn new() -> Self { type BaseType = Clock;
Self type MaskType = Bool;
}
}
impl Type for ClockType { impl_match_variant_as_self!();
type Value = Clock;
type CanonicalType = ClockType;
type CanonicalValue = Clock;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
UIntType::new() Bool
} }
fn type_enum(&self) -> TypeEnum { fn canonical(&self) -> CanonicalType {
TypeEnum::Clock(*self) CanonicalType::Clock(*self)
} }
fn from_canonical_type(t: Self::CanonicalType) -> Self { fn source_location() -> SourceLocation {
t
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin() SourceLocation::builtin()
} }
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { fn from_canonical(canonical_type: CanonicalType) -> Self {
Some(this) let CanonicalType::Clock(retval) = canonical_type else {
panic!("expected Clock");
};
retval
} }
} }
impl Connect<Self> for ClockType {} impl Clock {
pub fn type_properties(self) -> TypeProperties {
impl CanonicalType for ClockType { Self::TYPE_PROPERTIES
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock;
} }
pub fn can_connect(self, _rhs: Self) -> bool {
impl StaticType for ClockType { true
fn static_type() -> Self {
Self
} }
} }
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] impl StaticType for Clock {
pub struct Clock(pub bool); const TYPE: Self = Self;
const MASK_TYPE: Self::MaskType = Bool;
impl ToExpr for Clock { const TYPE_PROPERTIES: TypeProperties = TypeProperties {
type Type = ClockType; is_passive: true,
is_storable: false,
fn ty(&self) -> Self::Type { is_castable_from_bits: true,
ClockType bit_width: 1,
} };
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for Clock {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for Clock {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Clock(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)]
#[hdl(static, outline_generated)]
pub struct ClockDomain {
pub clk: Clock,
pub rst: Reset,
} }
pub trait ToClock { pub trait ToClock {
@ -137,10 +87,10 @@ impl ToClock for Expr<Clock> {
} }
} }
impl ToClock for Clock { #[hdl]
fn to_clock(&self) -> Expr<Clock> { pub struct ClockDomain {
self.to_expr() pub clk: Clock,
} pub rst: Reset,
} }
impl ToClock for bool { impl ToClock for bool {
@ -148,9 +98,3 @@ impl ToClock for bool {
self.to_expr().to_clock() self.to_expr().to_clock()
} }
} }
impl ToClock for UInt<1> {
fn to_clock(&self) -> Expr<Clock> {
self.to_expr().to_clock()
}
}

View file

@ -1,190 +1,155 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{ use crate::{
bundle::{BundleValue, TypeHintTrait},
expr::{ops::VariantAccess, Expr, ToExpr}, expr::{ops::VariantAccess, Expr, ToExpr},
int::{UInt, UIntType}, hdl,
intern::{Intern, Interned, MemoizeGeneric}, int::Bool,
intern::{Intern, Interned},
module::{ module::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
NormalModule, Scope, NormalModule, Scope,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties},
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
},
}; };
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{ use std::iter::FusedIterator;
borrow::Cow,
fmt,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantType<T> { pub struct EnumVariant {
pub name: Interned<str>, pub name: Interned<str>,
pub ty: Option<T>, pub ty: Option<CanonicalType>,
}
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"
);
}
} }
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
struct DynEnumTypeImpl { struct EnumImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, variants: Interned<[EnumVariant]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize, type_properties: TypeProperties,
is_storable: bool,
is_castable_from_bits: bool,
} }
impl fmt::Debug for DynEnumTypeImpl { impl std::fmt::Debug for EnumImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DynEnumType ")?; f.debug_struct("Enum")
f.debug_set() .field("variants", &self.variants)
.entries( .field("name_indexes", &self.name_indexes)
self.variants .field("type_properties", &self.type_properties)
.iter()
.map(|variant| variant.fmt_debug_in_enum()),
)
.finish() .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 { fn eq(&self, other: &Self) -> bool {
self.variants == other.variants self.variants == other.variants
} }
} }
impl Hash for DynEnumTypeImpl { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
fn hash<H: Hasher>(&self, state: &mut H) { pub struct Enum(Interned<EnumImpl>);
self.variants.hash(state);
const fn discriminant_bit_width_impl(variant_count: usize) -> usize {
match variant_count.next_power_of_two().checked_ilog2() {
Some(x) => x as usize,
None => 0,
} }
} }
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Clone)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>); pub struct EnumTypePropertiesBuilder {
type_properties: TypeProperties,
impl fmt::Debug for DynEnumType { variant_count: usize,
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
} }
fn discriminant_bit_width_impl(variant_count: usize) -> usize { impl EnumTypePropertiesBuilder {
variant_count #[must_use]
.next_power_of_two() pub const fn new() -> Self {
.checked_ilog2() Self {
.unwrap_or(0) as usize type_properties: TypeProperties {
} is_passive: true,
is_storable: true,
impl DynEnumType { is_castable_from_bits: true,
#[track_caller] bit_width: 0,
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { },
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); variant_count: 0,
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) {
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 bit_width = body_bit_width pub const fn clone(&self) -> Self {
.checked_add(discriminant_bit_width_impl(variants.len())) Self { ..*self }
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); }
Self( #[must_use]
DynEnumTypeImpl { pub const fn variant(self, field_props: Option<TypeProperties>) -> Self {
variants, let Self {
name_indexes, mut type_properties,
bit_width, variant_count,
} = self;
if let Some(TypeProperties {
is_passive,
is_storable, is_storable,
is_castable_from_bits, is_castable_from_bits,
bit_width,
}) = field_props
{
assert!(is_passive, "variant type must be a passive type");
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: if type_properties.bit_width < bit_width {
bit_width
} else {
type_properties.bit_width
},
};
}
Self {
type_properties,
variant_count: variant_count + 1,
}
}
pub const fn finish(self) -> TypeProperties {
assert!(
self.variant_count != 0,
"zero-variant enums aren't yet supported: \
https://github.com/chipsalliance/firrtl-spec/issues/208",
);
let Some(bit_width) = self
.type_properties
.bit_width
.checked_add(discriminant_bit_width_impl(self.variant_count))
else {
panic!("enum is too big: bit-width overflowed");
};
TypeProperties {
bit_width,
..self.type_properties
}
}
}
impl Enum {
#[track_caller]
pub fn new(variants: Interned<[EnumVariant]>) -> Self {
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut type_props_builder = EnumTypePropertiesBuilder::new();
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}");
}
type_props_builder = type_props_builder.variant(ty.map(CanonicalType::type_properties));
}
Self(
EnumImpl {
variants,
name_indexes,
type_properties: type_props_builder.finish(),
} }
.intern_sized(), .intern_sized(),
) )
@ -192,233 +157,62 @@ impl DynEnumType {
pub fn discriminant_bit_width(self) -> usize { pub fn discriminant_bit_width(self) -> usize {
discriminant_bit_width_impl(self.variants().len()) discriminant_bit_width_impl(self.variants().len())
} }
pub fn is_passive(self) -> bool { pub fn type_properties(self) -> TypeProperties {
true self.0.type_properties
}
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 name_indexes(&self) -> &HashMap<Interned<str>, usize> { pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes &self.0.name_indexes
} }
pub fn can_connect(self, rhs: Self) -> bool {
if self.0.variants.len() != rhs.0.variants.len() {
return false;
} }
for (
#[derive(Debug, Clone, Hash, PartialEq, Eq)] &EnumVariant {
pub struct DynEnum { name: lhs_name,
ty: DynEnumType, ty: lhs_ty,
variant_index: usize, },
variant_value: Option<DynCanonicalValue>, &EnumVariant {
name: rhs_name,
ty: rhs_ty,
},
) in self.0.variants.iter().zip(rhs.0.variants.iter())
{
if lhs_name != rhs_name {
return false;
} }
match (lhs_ty, rhs_ty) {
impl DynEnum { (None, None) => {}
#[track_caller] (None, Some(_)) | (Some(_), None) => return false,
pub fn new_by_index( (Some(lhs_ty), Some(rhs_ty)) => {
ty: DynEnumType, if !lhs_ty.can_connect(rhs_ty) {
variant_index: usize, return false;
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"
)),
} }
} }
true
} }
} }
pub trait EnumType: pub trait EnumType:
Type< Type<
CanonicalType = DynEnumType, BaseType = Enum,
CanonicalValue = DynEnum, MaskType = Bool,
MaskType = UIntType<1>,
MaskValue = UInt<1>,
MatchActiveScope = Scope, MatchActiveScope = Scope,
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MatchVariantsIter = EnumMatchVariantsIter<Self>, MatchVariantsIter = EnumMatchVariantsIter<Self>,
> + Connect<Self> >
where
Self::Value: EnumValue + ToExpr<Type = Self>,
{ {
type Builder; fn variants(&self) -> Interned<[EnumVariant]>;
fn match_activate_scope( fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope, v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope); ) -> (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 pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>);
where
<Self as ToExpr>::Type: EnumType<Value = Self>,
{
}
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>) impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> {
where
T::Value: EnumValue<Type = T>;
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
type MatchVariant = T::MatchVariant; type MatchVariant = T::MatchVariant;
type MatchActiveScope = Scope; type MatchActiveScope = Scope;
@ -427,36 +221,22 @@ where
} }
} }
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> {
where pub fn variant_access(&self) -> VariantAccess {
T::Value: EnumValue<Type = T>,
{
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
self.0.variant_access() self.0.variant_access()
} }
pub fn activate( pub fn activate(self) -> (VariantAccess, Scope) {
self,
) -> (
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
Scope,
) {
self.0.activate() self.0.activate()
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct EnumMatchVariantsIter<T: EnumType> pub struct EnumMatchVariantsIter<T: EnumType> {
where
T::Value: EnumValue<Type = T>,
{
pub(crate) inner: EnumMatchVariantsIterImpl<T>, pub(crate) inner: EnumMatchVariantsIterImpl<T>,
pub(crate) variant_index: std::ops::Range<usize>, pub(crate) variant_index: std::ops::Range<usize>,
} }
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
type Item = EnumMatchVariantAndInactiveScope<T>; type Item = EnumMatchVariantAndInactiveScope<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -470,21 +250,15 @@ where
} }
} }
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
fn len(&self) -> usize { fn len(&self) -> usize {
self.variant_index.len() 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> impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.variant_index.next_back().map(|variant_index| { self.variant_index.next_back().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
@ -492,129 +266,56 @@ where
} }
} }
impl Type for DynEnumType { impl EnumType for Enum {
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;
fn match_activate_scope( fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope, v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope) { ) -> (Self::MatchVariant, Self::MatchActiveScope) {
let (expr, scope) = v.0.activate(); let (expr, scope) = v.0.activate();
(expr.variant_type().ty.map(|_| expr.to_expr()), scope) (expr.variant_type().map(|_| expr.to_expr()), scope)
} }
fn variants(&self) -> Interned<[EnumVariant]> {
fn builder() -> Self::Builder {
NoBuilder
}
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
self.0.variants self.0.variants
} }
fn variants_hint() -> VariantsHint {
VariantsHint {
known_variants: [][..].intern(),
more_variants: true,
} }
impl Type for Enum {
type BaseType = 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 { #[hdl]
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType; pub enum HdlOption<T: Type> {
}
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, None,
Some(T), Some(T),
} }
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,432 @@
use crate::{
array::Array,
bundle::{Bundle, BundleField},
expr::Flow,
intern::{Intern, Interned},
memory::{DynPortType, MemPort},
module::{Instance, ModuleIO, TargetName},
reg::Reg,
source_location::SourceLocation,
ty::{CanonicalType, Type},
wire::Wire,
};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathBundleField {
pub name: Interned<str>,
}
impl fmt::Display for TargetPathBundleField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ".{}", self.name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathArrayElement {
pub index: usize,
}
impl fmt::Display for TargetPathArrayElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}]", self.index)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TargetPathDynArrayElement {}
impl fmt::Display for TargetPathDynArrayElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[<dyn>]")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TargetPathElement {
BundleField(TargetPathBundleField),
ArrayElement(TargetPathArrayElement),
DynArrayElement(TargetPathDynArrayElement),
}
impl From<TargetPathBundleField> for TargetPathElement {
fn from(value: TargetPathBundleField) -> Self {
Self::BundleField(value)
}
}
impl From<TargetPathArrayElement> for TargetPathElement {
fn from(value: TargetPathArrayElement) -> Self {
Self::ArrayElement(value)
}
}
impl From<TargetPathDynArrayElement> for TargetPathElement {
fn from(value: TargetPathDynArrayElement) -> Self {
Self::DynArrayElement(value)
}
}
impl fmt::Display for TargetPathElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BundleField(v) => v.fmt(f),
Self::ArrayElement(v) => v.fmt(f),
Self::DynArrayElement(v) => v.fmt(f),
}
}
}
impl TargetPathElement {
pub fn canonical_ty(&self, parent: Interned<Target>) -> CanonicalType {
match self {
&Self::BundleField(TargetPathBundleField { name }) => {
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
let field = parent_ty
.field_by_name(name)
.expect("field name is known to be a valid field of parent type");
field.ty
}
&Self::ArrayElement(TargetPathArrayElement { index }) => {
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
assert!(index < parent_ty.len());
parent_ty.element()
}
Self::DynArrayElement(_) => {
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
parent_ty.element()
}
}
}
pub fn flow(&self, parent: Interned<Target>) -> Flow {
match self {
Self::BundleField(v) => {
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
let field = parent_ty
.field_by_name(v.name)
.expect("field name is known to be a valid field of parent type");
parent.flow().flip_if(field.flipped)
}
Self::ArrayElement(_) => parent.flow(),
Self::DynArrayElement(_) => parent.flow(),
}
}
pub fn is_static(&self) -> bool {
match self {
Self::BundleField(_) | Self::ArrayElement(_) => true,
Self::DynArrayElement(_) => false,
}
}
}
macro_rules! impl_target_base {
(
$(#[$enum_meta:meta])*
$enum_vis:vis enum $TargetBase:ident {
$(
#[is = $is_fn:ident]
#[to = $to_fn:ident]
$(#[$variant_meta:meta])*
$Variant:ident($VariantTy:ty),
)*
}
) => {
$(#[$enum_meta])*
$enum_vis enum $TargetBase {
$(
$(#[$variant_meta])*
$Variant($VariantTy),
)*
}
impl fmt::Debug for $TargetBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(Self::$Variant(v) => v.fmt(f),)*
}
}
}
$(
impl From<$VariantTy> for $TargetBase {
fn from(value: $VariantTy) -> Self {
Self::$Variant(value)
}
}
impl From<$VariantTy> for Target {
fn from(value: $VariantTy) -> Self {
$TargetBase::$Variant(value).into()
}
}
)*
impl $TargetBase {
$(
$enum_vis fn $is_fn(&self) -> bool {
self.$to_fn().is_some()
}
$enum_vis fn $to_fn(&self) -> Option<&$VariantTy> {
if let Self::$Variant(retval) = self {
Some(retval)
} else {
None
}
}
)*
$enum_vis fn must_connect_to(&self) -> bool {
match self {
$(Self::$Variant(v) => v.must_connect_to(),)*
}
}
$enum_vis fn flow(&self) -> Flow {
match self {
$(Self::$Variant(v) => v.flow(),)*
}
}
$enum_vis fn source_location(&self) -> SourceLocation {
match self {
$(Self::$Variant(v) => v.source_location(),)*
}
}
}
};
}
impl_target_base! {
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum TargetBase {
#[is = is_module_io]
#[to = module_io]
ModuleIO(ModuleIO<CanonicalType>),
#[is = is_mem_port]
#[to = mem_port]
MemPort(MemPort<DynPortType>),
#[is = is_reg]
#[to = reg]
Reg(Reg<CanonicalType>),
#[is = is_wire]
#[to = wire]
Wire(Wire<CanonicalType>),
#[is = is_instance]
#[to = instance]
Instance(Instance<Bundle>),
}
}
impl fmt::Display for TargetBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.target_name())
}
}
impl TargetBase {
pub fn target_name(&self) -> TargetName {
match self {
TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None),
TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())),
TargetBase::Reg(v) => TargetName(v.scoped_name(), None),
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
}
}
pub fn canonical_ty(&self) -> CanonicalType {
match self {
TargetBase::ModuleIO(v) => v.ty(),
TargetBase::MemPort(v) => v.ty().canonical(),
TargetBase::Reg(v) => v.ty(),
TargetBase::Wire(v) => v.ty(),
TargetBase::Instance(v) => v.ty().canonical(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct TargetChild {
parent: Interned<Target>,
path_element: Interned<TargetPathElement>,
canonical_ty: CanonicalType,
flow: Flow,
}
impl fmt::Debug for TargetChild {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
parent,
path_element,
canonical_ty: _,
flow: _,
} = self;
parent.fmt(f)?;
fmt::Display::fmt(path_element, f)
}
}
impl fmt::Display for TargetChild {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
parent,
path_element,
canonical_ty: _,
flow: _,
} = self;
parent.fmt(f)?;
path_element.fmt(f)
}
}
impl TargetChild {
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
Self {
parent,
path_element,
canonical_ty: path_element.canonical_ty(parent),
flow: path_element.flow(parent),
}
}
pub fn parent(self) -> Interned<Target> {
self.parent
}
pub fn path_element(self) -> Interned<TargetPathElement> {
self.path_element
}
pub fn canonical_ty(self) -> CanonicalType {
self.canonical_ty
}
pub fn flow(self) -> Flow {
self.flow
}
pub fn bundle_field(self) -> Option<BundleField> {
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
Some(
parent_ty
.field_by_name(name)
.expect("field name known to be a valid field of parent"),
)
} else {
None
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Target {
Base(Interned<TargetBase>),
Child(TargetChild),
}
impl From<TargetBase> for Target {
fn from(value: TargetBase) -> Self {
Self::Base(Intern::intern_sized(value))
}
}
impl From<TargetChild> for Target {
fn from(value: TargetChild) -> Self {
Self::Child(value)
}
}
impl From<Interned<TargetBase>> for Target {
fn from(value: Interned<TargetBase>) -> Self {
Self::Base(value)
}
}
impl Target {
pub fn base(&self) -> Interned<TargetBase> {
let mut target = self;
loop {
match target {
Self::Base(v) => break *v,
Self::Child(v) => target = &v.parent,
}
}
}
pub fn child(&self) -> Option<TargetChild> {
match *self {
Target::Base(_) => None,
Target::Child(v) => Some(v),
}
}
pub fn is_static(&self) -> bool {
let mut target = self;
loop {
match target {
Self::Base(_) => return true,
Self::Child(v) if !v.path_element().is_static() => return false,
Self::Child(v) => target = &v.parent,
}
}
}
#[must_use]
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
TargetChild::new(self.intern(), path_element).into()
}
pub fn flow(&self) -> Flow {
match self {
Self::Base(v) => v.flow(),
Self::Child(v) => v.flow(),
}
}
pub fn canonical_ty(&self) -> CanonicalType {
match self {
Target::Base(v) => v.canonical_ty(),
Target::Child(v) => v.canonical_ty(),
}
}
}
impl fmt::Display for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base(v) => v.fmt(f),
Self::Child(v) => v.fmt(f),
}
}
}
impl fmt::Debug for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base(v) => v.fmt(f),
Self::Child(v) => v.fmt(f),
}
}
}
pub trait GetTarget {
fn target(&self) -> Option<Interned<Target>>;
}
impl GetTarget for bool {
fn target(&self) -> Option<Interned<Target>> {
None
}
}
impl<T: ?Sized + GetTarget + Send + Sync + 'static> GetTarget for Interned<T> {
fn target(&self) -> Option<Interned<Target>> {
T::target(self)
}
}
impl<T: ?Sized + GetTarget> GetTarget for &'_ T {
fn target(&self) -> Option<Interned<Target>> {
T::target(self)
}
}
impl<T: ?Sized + GetTarget> GetTarget for &'_ mut T {
fn target(&self) -> Option<Interned<Target>> {
T::target(self)
}
}
impl<T: ?Sized + GetTarget> GetTarget for Box<T> {
fn target(&self) -> Option<Interned<Target>> {
T::target(self)
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,179 @@ use std::{
pub mod type_map; pub mod type_map;
pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
fn get(&self) -> Interned<T>;
}
impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
LazyInternedTrait<T> for F
{
fn get(&self) -> Interned<T> {
self()
}
}
#[repr(transparent)]
pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.get_ptr_eq_with_type_id().hash(state);
}
}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
fn eq(&self, other: &Self) -> bool {
self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id()
}
}
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
Interned(Interned<T>),
Lazy(LazyInternedFn<T>),
}
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
Self::Lazy(LazyInternedFn(v))
}
}
impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
Interned::into_inner(self.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
where
Interned<T>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.interned() == other.interned()
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
where
Interned<T>: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
self.interned().cmp(&other.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
where
Interned<T>: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.interned().partial_cmp(&other.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
where
Interned<T>: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.interned().hash(state);
}
}
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
pub fn interned(self) -> Interned<T>
where
T: Intern,
{
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
type InputRef<'a> = LazyInternedFn<T>;
type InputOwned = LazyInternedFn<T>;
type InputCow<'a> = LazyInternedFn<T>;
type Output = Interned<T>;
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
*input
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
input
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
*input
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
input
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
input
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
input.0.get()
}
}
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval),
}
}
}
pub trait InternedCompare { pub trait InternedCompare {
type InternedCompareKey: Ord + Hash; type InternedCompareKey: Ord + Hash;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
@ -471,6 +644,12 @@ pub struct Interned<T: ?Sized + 'static + Send + Sync, C: InternContext = Global
macro_rules! forward_fmt_trait { macro_rules! forward_fmt_trait {
($Tr:ident) => { ($Tr:ident) => {
impl<T: ?Sized + Intern + fmt::$Tr> fmt::$Tr for LazyInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::fmt(&**self, f)
}
}
impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr
for Interned<T, C> for Interned<T, C>
{ {

View file

@ -12,7 +12,6 @@ extern crate self as fayalite;
pub use std as __std; pub use std as __std;
#[doc(inline)] #[doc(inline)]
#[doc(alias = "hdl")]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called. /// a [`Module`][`::fayalite::module::Module`] when called.
/// In the function body it will implicitly create a /// In the function body it will implicitly create a
@ -21,17 +20,25 @@ pub use std as __std;
/// See [Fayalite Modules][crate::_docs::modules] /// See [Fayalite Modules][crate::_docs::modules]
pub use fayalite_proc_macros::hdl_module; pub use fayalite_proc_macros::hdl_module;
#[doc(inline)]
pub use fayalite_proc_macros::hdl;
/// struct used as a placeholder when applying defaults
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct __;
#[cfg(feature = "unstable-doc")] #[cfg(feature = "unstable-doc")]
pub mod _docs; pub mod _docs;
// FIXME: finish
pub mod annotations; pub mod annotations;
pub mod array; pub mod array;
pub mod bundle; pub mod bundle;
pub mod cli; //pub mod cli;
pub mod clock; pub mod clock;
pub mod enum_; pub mod enum_;
pub mod expr; pub mod expr;
pub mod firrtl; //pub mod firrtl;
pub mod int; pub mod int;
pub mod intern; pub mod intern;
pub mod memory; pub mod memory;
@ -41,5 +48,5 @@ pub mod reset;
pub mod source_location; pub mod source_location;
pub mod ty; pub mod ty;
pub mod util; pub mod util;
pub mod valueless; //pub mod valueless;
pub mod wire; pub mod wire;

View file

@ -4,15 +4,16 @@
#![allow(clippy::multiple_bound_locations)] #![allow(clippy::multiple_bound_locations)]
use crate::{ use crate::{
annotations::{Annotation, IntoAnnotations, TargetedAnnotation}, annotations::{Annotation, IntoAnnotations, TargetedAnnotation},
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, array::{Array, ArrayType},
bundle::{BundleType, BundleValue, DynBundle, DynBundleType}, bundle::{Bundle, BundleType},
clock::{Clock, ClockType}, clock::Clock,
expr::{Expr, ExprEnum, ExprTrait, Flow, ToExpr}, expr::{Expr, Flow, ToExpr, ToLiteralBits},
int::{DynUInt, DynUIntType, UInt, UIntType}, hdl,
int::{Bool, DynSize, Size, UInt, UIntType},
intern::{Intern, Interned}, intern::{Intern, Interned},
module::ScopedNameId, module::ScopedNameId,
source_location::SourceLocation, source_location::SourceLocation,
ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value}, ty::{AsMask, CanonicalType, Type},
util::DebugAsDisplay, util::DebugAsDisplay,
}; };
use bitvec::slice::BitSlice; use bitvec::slice::BitSlice;
@ -20,37 +21,37 @@ use std::{
cell::RefCell, cell::RefCell,
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData,
num::NonZeroU32, num::NonZeroU32,
rc::Rc, rc::Rc,
}; };
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] #[hdl]
pub struct ReadStruct<Element> { pub struct ReadStruct<Element, AddrWidth: Size> {
pub addr: DynUInt, pub addr: UIntType<AddrWidth>,
pub en: UInt<1>, pub en: Bool,
pub clk: Clock, pub clk: Clock,
#[hdl(flip)] #[hdl(flip)]
pub data: Element, pub data: Element,
} }
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] #[hdl]
pub struct WriteStruct<Element: Value> { pub struct WriteStruct<Element, AddrWidth: Size> {
pub addr: DynUInt, pub addr: UIntType<AddrWidth>,
pub en: UInt<1>, pub en: Bool,
pub clk: Clock, pub clk: Clock,
pub data: Element, pub data: Element,
pub mask: AsMask<Element>, pub mask: AsMask<Element>,
} }
#[allow(clippy::multiple_bound_locations)] #[hdl]
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] pub struct ReadWriteStruct<Element, AddrWidth: Size> {
pub struct ReadWriteStruct<Element: Value> { pub addr: UIntType<AddrWidth>,
pub addr: DynUInt, pub en: Bool,
pub en: UInt<1>,
pub clk: Clock, pub clk: Clock,
#[hdl(flip)] #[hdl(flip)]
pub rdata: Element, pub rdata: Element,
pub wmode: UInt<1>, pub wmode: Bool,
pub wdata: Element, pub wdata: Element,
pub wmask: AsMask<Element>, pub wmask: AsMask<Element>,
} }
@ -63,11 +64,10 @@ pub trait PortType:
sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static
{ {
type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static; type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static;
type PortType: BundleType<Value = Self::PortValue>; type Port: BundleType;
type PortValue: BundleValue<Type = Self::PortType>;
fn port_kind(port_kind: Self::PortKindTy) -> PortKind; fn port_kind(port_kind: Self::PortKindTy) -> PortKind;
fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy; fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy;
fn port_ty(port: &MemPort<Self>) -> Self::PortType; fn port_ty(port: &MemPort<Self>) -> Self::Port;
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -79,8 +79,7 @@ impl sealed::Sealed for DynPortType {}
impl PortType for DynPortType { impl PortType for DynPortType {
type PortKindTy = PortKind; type PortKindTy = PortKind;
type PortType = DynBundleType; type Port = Bundle;
type PortValue = DynBundle;
fn port_kind(port_kind: Self::PortKindTy) -> PortKind { fn port_kind(port_kind: Self::PortKindTy) -> PortKind {
port_kind port_kind
@ -90,41 +89,38 @@ impl PortType for DynPortType {
port_kind port_kind
} }
fn port_ty(port: &MemPort<Self>) -> Self::PortType { fn port_ty(port: &MemPort<Self>) -> Self::Port {
match port.port_kind { Bundle::new(match port.port_kind {
PortKind::ReadOnly => MemPort::<ReadStruct<DynCanonicalValue>>::from_canonical(*port) PortKind::ReadOnly => {
MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port)
.ty() .ty()
.canonical(), .fields()
PortKind::WriteOnly => MemPort::<WriteStruct<DynCanonicalValue>>::from_canonical(*port) }
PortKind::WriteOnly => {
MemPort::<WriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
.ty() .ty()
.canonical(), .fields()
}
PortKind::ReadWrite => { PortKind::ReadWrite => {
MemPort::<ReadWriteStruct<DynCanonicalValue>>::from_canonical(*port) MemPort::<ReadWriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
.ty() .ty()
.canonical() .fields()
}
} }
})
} }
} }
pub trait PortStruct: pub trait PortStruct: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> {
BundleValue type Element: Type;
+ sealed::Sealed
+ PortType<PortKindTy = (), PortType = <Self as ToExpr>::Type, PortValue = Self>
where
Self::Type: BundleType<Value = Self>,
{
type Element: Value<Type = Self::ElementType>;
type ElementType: Type<Value = Self::Element>;
const PORT_KIND: PortKind; const PORT_KIND: PortKind;
fn addr(this: Expr<Self>) -> Expr<DynUInt>; fn addr(this: Expr<Self>) -> Expr<UInt>;
fn en(this: Expr<Self>) -> Expr<UInt<1>>; fn en(this: Expr<Self>) -> Expr<Bool>;
fn clk(this: Expr<Self>) -> Expr<Clock>; fn clk(this: Expr<Self>) -> Expr<Clock>;
fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>>; fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>>;
fn wmode(this: Expr<Self>) -> Expr<UInt<1>>; fn wmode(this: Expr<Self>) -> Expr<Bool>;
} }
macro_rules! impl_port_struct { macro_rules! impl_port_struct {
@ -137,19 +133,11 @@ macro_rules! impl_port_struct {
$($body:tt)* $($body:tt)*
} }
) => { ) => {
impl<$Element: Value> sealed::Sealed for $Struct<$Element> impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {}
where
$Element::Type: Type<Value = $Element>,
{
}
impl<$Element: Value> PortType for $Struct<$Element> impl<$Element: Type> PortType for $Struct<$Element, DynSize> {
where
$Element::Type: Type<Value = $Element>,
{
type PortKindTy = (); type PortKindTy = ();
type PortType = <Self as ToExpr>::Type; type Port = Self;
type PortValue = Self;
fn port_kind(_port_kind: Self::PortKindTy) -> PortKind { fn port_kind(_port_kind: Self::PortKindTy) -> PortKind {
Self::PORT_KIND Self::PORT_KIND
@ -164,18 +152,14 @@ macro_rules! impl_port_struct {
} }
} }
impl<$Element: Value> PortStruct for $Struct<$Element> impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> {
where
$Element::Type: Type<Value = $Element>,
{
type Element = $Element; type Element = $Element;
type ElementType = $Element::Type;
fn addr(this: Expr<Self>) -> Expr<DynUInt> { fn addr(this: Expr<Self>) -> Expr<UInt> {
this.addr this.addr
} }
fn en(this: Expr<Self>) -> Expr<UInt<1>> { fn en(this: Expr<Self>) -> Expr<Bool> {
this.en this.en
} }
@ -190,14 +174,9 @@ macro_rules! impl_port_struct {
impl_port_struct! { impl_port_struct! {
impl<Element> _ for ReadStruct { impl<Element> _ for ReadStruct {
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { fn port_ty(port: &MemPort<Self>) -> Self {
type T<V> = <V as ToExpr>::Type; let element = Element::from_canonical(port.mem_element_type);
T::<Self> { ReadStruct[element][port.addr_type.width()]
addr: port.addr_type,
en: UIntType::new(),
clk: ClockType,
data: Element::Type::from_dyn_canonical_type(port.mem_element_type),
}
} }
const PORT_KIND: PortKind = PortKind::ReadOnly; const PORT_KIND: PortKind = PortKind::ReadOnly;
@ -214,7 +193,7 @@ impl_port_struct! {
None None
} }
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { fn wmode(_this: Expr<Self>) -> Expr<Bool> {
false.to_expr() false.to_expr()
} }
} }
@ -222,16 +201,9 @@ impl_port_struct! {
impl_port_struct! { impl_port_struct! {
impl<Element> _ for WriteStruct { impl<Element> _ for WriteStruct {
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { fn port_ty(port: &MemPort<Self>) -> Self {
type T<V> = <V as ToExpr>::Type; let element = Element::from_canonical(port.mem_element_type);
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); WriteStruct[element][port.addr_type.width()]
T::<Self> {
addr: port.addr_type,
en: UIntType::new(),
clk: ClockType,
mask: element_ty.mask_type(),
data: element_ty,
}
} }
const PORT_KIND: PortKind = PortKind::WriteOnly; const PORT_KIND: PortKind = PortKind::WriteOnly;
@ -248,7 +220,7 @@ impl_port_struct! {
Some(this.mask) Some(this.mask)
} }
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { fn wmode(_this: Expr<Self>) -> Expr<Bool> {
true.to_expr() true.to_expr()
} }
} }
@ -256,18 +228,9 @@ impl_port_struct! {
impl_port_struct! { impl_port_struct! {
impl<Element> _ for ReadWriteStruct { impl<Element> _ for ReadWriteStruct {
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { fn port_ty(port: &MemPort<Self>) -> Self {
type T<V> = <V as ToExpr>::Type; let element = Element::from_canonical(port.mem_element_type);
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); ReadWriteStruct[element][port.addr_type.width()]
T::<Self> {
addr: port.addr_type,
en: UIntType::new(),
clk: ClockType,
rdata: element_ty.clone(),
wmode: UIntType::new(),
wmask: element_ty.mask_type(),
wdata: element_ty,
}
} }
const PORT_KIND: PortKind = PortKind::ReadWrite; const PORT_KIND: PortKind = PortKind::ReadWrite;
@ -284,7 +247,7 @@ impl_port_struct! {
Some(this.wmask) Some(this.wmask)
} }
fn wmode(this: Expr<Self>) -> Expr<UInt<1>> { fn wmode(this: Expr<Self>) -> Expr<Bool> {
this.wmode this.wmode
} }
} }
@ -382,8 +345,8 @@ pub struct MemPort<T: PortType> {
source_location: SourceLocation, source_location: SourceLocation,
port_kind: T::PortKindTy, port_kind: T::PortKindTy,
port_index: usize, port_index: usize,
addr_type: DynUIntType, addr_type: UInt,
mem_element_type: Interned<dyn DynCanonicalType>, mem_element_type: CanonicalType,
} }
impl<T: PortType> fmt::Debug for MemPort<T> { impl<T: PortType> fmt::Debug for MemPort<T> {
@ -405,19 +368,10 @@ impl<T: PortType> fmt::Debug for MemPort<T> {
} }
} }
impl<T: PortType> ToExpr for MemPort<T> { impl<T: PortType> MemPort<T> {
type Type = T::PortType; pub fn ty(&self) -> T::Port {
fn ty(&self) -> Self::Type {
T::port_ty(self) T::port_ty(self)
} }
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::new_unchecked(self.expr_enum())
}
}
impl<T: PortType> MemPort<T> {
pub fn source_location(&self) -> SourceLocation { pub fn source_location(&self) -> SourceLocation {
self.source_location self.source_location
} }
@ -436,10 +390,10 @@ impl<T: PortType> MemPort<T> {
index: self.port_index, index: self.port_index,
} }
} }
pub fn mem_element_type(&self) -> Interned<dyn DynCanonicalType> { pub fn mem_element_type(&self) -> CanonicalType {
self.mem_element_type self.mem_element_type
} }
pub fn addr_type(&self) -> DynUIntType { pub fn addr_type(&self) -> UInt {
self.addr_type self.addr_type
} }
pub fn canonical(&self) -> MemPort<DynPortType> { pub fn canonical(&self) -> MemPort<DynPortType> {
@ -463,7 +417,6 @@ impl<T: PortType> MemPort<T> {
pub fn from_canonical(port: MemPort<DynPortType>) -> Self pub fn from_canonical(port: MemPort<DynPortType>) -> Self
where where
T: PortStruct, T: PortStruct,
T::Type: BundleType<Value = T>,
{ {
let MemPort { let MemPort {
mem_name, mem_name,
@ -488,8 +441,8 @@ impl<T: PortType> MemPort<T> {
mem_name: ScopedNameId, mem_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
port_name: PortName, port_name: PortName,
addr_type: DynUIntType, addr_type: UInt,
mem_element_type: Interned<dyn DynCanonicalType>, mem_element_type: CanonicalType,
) -> Self { ) -> Self {
assert!( assert!(
mem_element_type.is_storable(), mem_element_type.is_storable(),
@ -520,10 +473,10 @@ pub enum ReadUnderWrite {
} }
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MemImpl<T: ArrayTypeTrait, P> { struct MemImpl<Element: Type, Len: Size, P> {
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
array_type: T, array_type: ArrayType<Element, Len>,
initial_value: Option<Interned<BitSlice>>, initial_value: Option<Interned<BitSlice>>,
ports: P, ports: P,
read_latency: usize, read_latency: usize,
@ -533,11 +486,11 @@ struct MemImpl<T: ArrayTypeTrait, P> {
mem_annotations: Interned<[Annotation]>, mem_annotations: Interned<[Annotation]>,
} }
pub struct Mem<VA: ValueArrayOrSlice + ?Sized>( pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>(
Interned<MemImpl<ArrayType<VA>, Interned<[Interned<MemPort<DynPortType>>]>>>, Interned<MemImpl<Element, Len, Interned<[MemPort<DynPortType>]>>>,
); );
struct PortsDebug<'a>(&'a [Interned<MemPort<DynPortType>>]); struct PortsDebug<'a>(&'a [MemPort<DynPortType>]);
impl fmt::Debug for PortsDebug<'_> { impl fmt::Debug for PortsDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -551,7 +504,7 @@ impl fmt::Debug for PortsDebug<'_> {
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> { impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let MemImpl { let MemImpl {
scoped_name, scoped_name,
@ -579,37 +532,37 @@ impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> {
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Hash for Mem<VA> { impl<Element: Type, Len: Size> Hash for Mem<Element, Len> {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state); self.0.hash(state);
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Eq for Mem<VA> {} impl<Element: Type, Len: Size> Eq for Mem<Element, Len> {}
impl<VA: ValueArrayOrSlice + ?Sized> PartialEq for Mem<VA> { impl<Element: Type, Len: Size> PartialEq for Mem<Element, Len> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0 == other.0 self.0 == other.0
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Copy for Mem<VA> {} impl<Element: Type, Len: Size> Copy for Mem<Element, Len> {}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Mem<VA> { impl<Element: Type, Len: Size> Clone for Mem<Element, Len> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { impl<Element: Type, Len: Size> Mem<Element, Len> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[track_caller] #[track_caller]
pub fn new_unchecked( pub fn new_unchecked(
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
array_type: ArrayType<VA>, array_type: ArrayType<Element, Len>,
initial_value: Option<Interned<BitSlice>>, initial_value: Option<Interned<BitSlice>>,
ports: Interned<[Interned<MemPort<DynPortType>>]>, ports: Interned<[MemPort<DynPortType>]>,
read_latency: usize, read_latency: usize,
write_latency: NonZeroU32, write_latency: NonZeroU32,
read_under_write: ReadUnderWrite, read_under_write: ReadUnderWrite,
@ -617,14 +570,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
mem_annotations: Interned<[Annotation]>, mem_annotations: Interned<[Annotation]>,
) -> Self { ) -> Self {
if let Some(initial_value) = initial_value { if let Some(initial_value) = initial_value {
MemBuilder::<VA>::check_initial_value_bit_slice( MemBuilder::<Element, Len>::check_initial_value_bit_slice(
array_type.element(), array_type.element(),
Some(array_type.len()), Some(array_type.len()),
initial_value, initial_value,
); );
} }
let addr_width = memory_addr_width(array_type.len()); let addr_width = memory_addr_width(array_type.len());
let expected_mem_element_type = array_type.element().canonical_dyn(); let expected_mem_element_type = array_type.element().canonical();
assert!( assert!(
expected_mem_element_type.is_storable(), expected_mem_element_type.is_storable(),
"memory element type must be a storable type" "memory element type must be a storable type"
@ -637,7 +590,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
port_index, port_index,
addr_type, addr_type,
mem_element_type, mem_element_type,
} = **port; } = *port;
assert_eq!(mem_name, scoped_name, "memory name must match with ports"); assert_eq!(mem_name, scoped_name, "memory name must match with ports");
assert_eq!( assert_eq!(
port_index, index, port_index, index,
@ -659,7 +612,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
}; };
assert_eq!( assert_eq!(
Some(port), Some(port),
ports.get(port.port_index).map(|v| &**v), ports.get(port.port_index),
"port on memory must match annotation's target base" "port on memory must match annotation's target base"
); );
} }
@ -682,13 +635,13 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
pub fn source_location(self) -> SourceLocation { pub fn source_location(self) -> SourceLocation {
self.0.source_location self.0.source_location
} }
pub fn array_type(self) -> ArrayType<VA> { pub fn array_type(self) -> ArrayType<Element, Len> {
self.0.array_type.clone() self.0.array_type.clone()
} }
pub fn initial_value(self) -> Option<Interned<BitSlice>> { pub fn initial_value(self) -> Option<Interned<BitSlice>> {
self.0.initial_value self.0.initial_value
} }
pub fn ports(self) -> Interned<[Interned<MemPort<DynPortType>>]> { pub fn ports(self) -> Interned<[MemPort<DynPortType>]> {
self.0.ports self.0.ports
} }
pub fn read_latency(self) -> usize { pub fn read_latency(self) -> usize {
@ -706,7 +659,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
pub fn mem_annotations(self) -> Interned<[Annotation]> { pub fn mem_annotations(self) -> Interned<[Annotation]> {
self.0.mem_annotations self.0.mem_annotations
} }
pub fn canonical(self) -> Mem<[DynCanonicalValue]> { pub fn canonical(self) -> Mem {
let MemImpl { let MemImpl {
scoped_name, scoped_name,
source_location, source_location,
@ -719,7 +672,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
port_annotations, port_annotations,
mem_annotations, mem_annotations,
} = *self.0; } = *self.0;
let array_type = array_type.canonical(); let array_type = array_type.as_dyn_array();
Mem(Intern::intern_sized(MemImpl { Mem(Intern::intern_sized(MemImpl {
scoped_name, scoped_name,
source_location, source_location,
@ -751,10 +704,10 @@ impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> {
pub(crate) struct MemBuilderTarget { pub(crate) struct MemBuilderTarget {
pub(crate) scoped_name: ScopedNameId, pub(crate) scoped_name: ScopedNameId,
pub(crate) source_location: SourceLocation, pub(crate) source_location: SourceLocation,
pub(crate) mem_element_type: Interned<dyn DynCanonicalType>, pub(crate) mem_element_type: CanonicalType,
pub(crate) depth: Option<usize>, pub(crate) depth: Option<usize>,
pub(crate) initial_value: Option<Interned<BitSlice>>, pub(crate) initial_value: Option<Interned<BitSlice>>,
pub(crate) ports: Vec<Interned<MemPort<DynPortType>>>, pub(crate) ports: Vec<MemPort<DynPortType>>,
pub(crate) read_latency: usize, pub(crate) read_latency: usize,
pub(crate) write_latency: NonZeroU32, pub(crate) write_latency: NonZeroU32,
pub(crate) read_under_write: ReadUnderWrite, pub(crate) read_under_write: ReadUnderWrite,
@ -763,11 +716,11 @@ pub(crate) struct MemBuilderTarget {
} }
impl MemBuilderTarget { impl MemBuilderTarget {
pub(crate) fn make_memory(&self) -> Option<Mem<[DynCanonicalValue]>> { pub(crate) fn make_memory(&self) -> Option<Mem> {
Some(Mem::new_unchecked( Some(Mem::new_unchecked(
self.scoped_name, self.scoped_name,
self.source_location, self.source_location,
ArrayType::new_slice(self.mem_element_type, self.depth?), ArrayType::new_dyn(self.mem_element_type, self.depth?),
self.initial_value, self.initial_value,
Intern::intern(&self.ports), Intern::intern(&self.ports),
self.read_latency, self.read_latency,
@ -817,16 +770,18 @@ impl fmt::Debug for MemBuilderTarget {
} }
} }
pub struct MemBuilder<VA: ValueArrayOrSlice + ?Sized> { pub struct MemBuilder<Element: Type, Len: Size> {
mem_element_type: VA::ElementType, mem_element_type: Element,
target: Rc<RefCell<MemBuilderTarget>>, target: Rc<RefCell<MemBuilderTarget>>,
_phantom: PhantomData<Len>,
} }
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for MemBuilder<VA> { impl<Element: Type, Len: Size> fmt::Debug for MemBuilder<Element, Len> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { let Self {
mem_element_type, mem_element_type,
target, target,
_phantom: _,
} = &self; } = &self;
target.borrow().debug_fmt("MemBuilder", mem_element_type, f) target.borrow().debug_fmt("MemBuilder", mem_element_type, f)
} }
@ -838,15 +793,16 @@ pub fn memory_addr_width(depth: usize) -> usize {
.map_or(usize::BITS, usize::ilog2) as usize .map_or(usize::BITS, usize::ilog2) as usize
} }
impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
#[track_caller] #[track_caller]
fn check_initial_value_bit_slice( fn check_initial_value_bit_slice(
mem_element_type: &VA::ElementType, mem_element_type: Element,
depth: Option<usize>, depth: Option<usize>,
initial_value: Interned<BitSlice>, initial_value: Interned<BitSlice>,
) -> Interned<BitSlice> { ) -> Interned<BitSlice> {
let element_bit_width = mem_element_type.canonical().bit_width();
if let Some(depth) = depth { if let Some(depth) = depth {
let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect( let expected_len = depth.checked_mul(element_bit_width).expect(
"memory must be small enough that its initializer bit length fits in usize", "memory must be small enough that its initializer bit length fits in usize",
); );
assert_eq!( assert_eq!(
@ -858,7 +814,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
assert!( assert!(
initial_value initial_value
.len() .len()
.checked_rem(mem_element_type.bit_width()) .checked_rem(element_bit_width)
.unwrap_or(initial_value.len()) .unwrap_or(initial_value.len())
== 0, == 0,
"Mem's initializer bit length must be a multiple of the element type's bit width", "Mem's initializer bit length must be a multiple of the element type's bit width",
@ -867,14 +823,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
} }
#[track_caller] #[track_caller]
fn check_initial_value_expr( fn check_initial_value_expr(
mem_element_type: &VA::ElementType, mem_element_type: &Element,
depth: Option<usize>, depth: Option<usize>,
initial_value: Expr<Array<[DynCanonicalValue]>>, initial_value: Expr<Array>,
) -> Interned<BitSlice> { ) -> Interned<BitSlice> {
let initial_value_ty = initial_value.canonical_type(); let initial_value_ty = Expr::ty(initial_value);
assert_eq!( assert_eq!(
*mem_element_type, *mem_element_type,
<VA::ElementType as DynType>::from_dyn_canonical_type(*initial_value_ty.element()), Element::from_canonical(initial_value_ty.element()),
"Mem's element type must match initializer's element type", "Mem's element type must match initializer's element type",
); );
if let Some(depth) = depth { if let Some(depth) = depth {
@ -889,7 +845,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
}; };
debug_assert_eq!( debug_assert_eq!(
retval.len(), retval.len(),
initial_value_ty.bit_width(), initial_value_ty.type_properties().bit_width,
"initial value produced wrong literal bits length" "initial value produced wrong literal bits length"
); );
retval retval
@ -898,9 +854,9 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
pub(crate) fn new( pub(crate) fn new(
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
mem_element_type: VA::ElementType, mem_element_type: Element,
) -> (Self, Rc<RefCell<MemBuilderTarget>>) { ) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
let canonical_mem_element_type = mem_element_type.canonical_dyn(); let canonical_mem_element_type = mem_element_type.canonical();
assert!( assert!(
canonical_mem_element_type.is_storable(), canonical_mem_element_type.is_storable(),
"memory element type must be a storable type" "memory element type must be a storable type"
@ -909,7 +865,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
scoped_name, scoped_name,
source_location, source_location,
mem_element_type: canonical_mem_element_type, mem_element_type: canonical_mem_element_type,
depth: VA::FIXED_LEN_TYPE.map(VA::len_from_len_type), depth: Len::KNOWN_VALUE,
initial_value: None, initial_value: None,
ports: vec![], ports: vec![],
read_latency: 0, read_latency: 0,
@ -922,6 +878,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
Self { Self {
mem_element_type, mem_element_type,
target: Rc::clone(&target), target: Rc::clone(&target),
_phantom: PhantomData,
}, },
target, target,
) )
@ -931,19 +888,19 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
&mut self, &mut self,
source_location: SourceLocation, source_location: SourceLocation,
port_kind: PortKind, port_kind: PortKind,
) -> Interned<MemPort<DynPortType>> { ) -> MemPort<DynPortType> {
let mut target = self.target.borrow_mut(); let mut target = self.target.borrow_mut();
let Some(depth) = target.depth else { let Some(depth) = target.depth else {
panic!("MemBuilder::depth must be called before adding ports"); panic!("MemBuilder::depth must be called before adding ports");
}; };
let port = Intern::intern_sized(MemPort { let port = MemPort {
mem_name: target.scoped_name, mem_name: target.scoped_name,
source_location, source_location,
port_kind, port_kind,
port_index: target.ports.len(), port_index: target.ports.len(),
addr_type: DynUIntType::new(memory_addr_width(depth)), addr_type: UInt::new(memory_addr_width(depth)),
mem_element_type: target.mem_element_type, mem_element_type: target.mem_element_type,
}); };
target.ports.push(port); target.ports.push(port);
port port
} }
@ -952,50 +909,53 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
&mut self, &mut self,
source_location: SourceLocation, source_location: SourceLocation,
kind: PortKind, kind: PortKind,
) -> Expr<DynBundle> { ) -> Expr<Bundle> {
Expr::new_unchecked(ExprEnum::MemPort(self.new_port_impl(source_location, kind))) self.new_port_impl(source_location, kind).to_expr()
} }
#[track_caller] #[track_caller]
pub fn new_port(&mut self, kind: PortKind) -> Expr<DynBundle> { pub fn new_port(&mut self, kind: PortKind) -> Expr<Bundle> {
self.new_port_with_loc(SourceLocation::caller(), kind) self.new_port_with_loc(SourceLocation::caller(), kind)
} }
#[track_caller] #[track_caller]
pub fn new_read_port_with_loc( pub fn new_read_port_with_loc(
&mut self, &mut self,
source_location: SourceLocation, source_location: SourceLocation,
) -> Expr<ReadStruct<VA::Element>> { ) -> Expr<ReadStruct<Element, DynSize>> {
Expr::new_unchecked(ExprEnum::MemPort( Expr::from_bundle(
self.new_port_impl(source_location, PortKind::ReadOnly), self.new_port_impl(source_location, PortKind::ReadOnly)
)) .to_expr(),
)
} }
#[track_caller] #[track_caller]
pub fn new_read_port(&mut self) -> Expr<ReadStruct<VA::Element>> { pub fn new_read_port(&mut self) -> Expr<ReadStruct<Element, DynSize>> {
self.new_read_port_with_loc(SourceLocation::caller()) self.new_read_port_with_loc(SourceLocation::caller())
} }
#[track_caller] #[track_caller]
pub fn new_write_port_with_loc( pub fn new_write_port_with_loc(
&mut self, &mut self,
source_location: SourceLocation, source_location: SourceLocation,
) -> Expr<WriteStruct<VA::Element>> { ) -> Expr<WriteStruct<Element, DynSize>> {
Expr::new_unchecked(ExprEnum::MemPort( Expr::from_bundle(
self.new_port_impl(source_location, PortKind::WriteOnly), self.new_port_impl(source_location, PortKind::WriteOnly)
)) .to_expr(),
)
} }
#[track_caller] #[track_caller]
pub fn new_write_port(&mut self) -> Expr<WriteStruct<VA::Element>> { pub fn new_write_port(&mut self) -> Expr<WriteStruct<Element, DynSize>> {
self.new_write_port_with_loc(SourceLocation::caller()) self.new_write_port_with_loc(SourceLocation::caller())
} }
#[track_caller] #[track_caller]
pub fn new_rw_port_with_loc( pub fn new_rw_port_with_loc(
&mut self, &mut self,
source_location: SourceLocation, source_location: SourceLocation,
) -> Expr<ReadWriteStruct<VA::Element>> { ) -> Expr<ReadWriteStruct<Element, DynSize>> {
Expr::new_unchecked(ExprEnum::MemPort( Expr::from_bundle(
self.new_port_impl(source_location, PortKind::ReadWrite), self.new_port_impl(source_location, PortKind::ReadWrite)
)) .to_expr(),
)
} }
#[track_caller] #[track_caller]
pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<VA::Element>> { pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<Element, DynSize>> {
self.new_rw_port_with_loc(SourceLocation::caller()) self.new_rw_port_with_loc(SourceLocation::caller())
} }
pub fn scoped_name(&self) -> ScopedNameId { pub fn scoped_name(&self) -> ScopedNameId {
@ -1004,7 +964,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
pub fn source_location(&self) -> SourceLocation { pub fn source_location(&self) -> SourceLocation {
self.target.borrow().source_location self.target.borrow().source_location
} }
pub fn get_mem_element_type(&self) -> &VA::ElementType { pub fn get_mem_element_type(&self) -> &Element {
&self.mem_element_type &self.mem_element_type
} }
#[allow(clippy::result_unit_err)] #[allow(clippy::result_unit_err)]
@ -1027,28 +987,28 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
target.depth = Some(depth); target.depth = Some(depth);
} }
#[allow(clippy::result_unit_err)] #[allow(clippy::result_unit_err)]
pub fn get_array_type(&self) -> Result<ArrayType<VA>, ()> { pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> {
Ok(ArrayType::new_with_len( Ok(ArrayType::new(
self.mem_element_type.clone(), self.mem_element_type.clone(),
self.get_depth()?, Len::from_usize(self.get_depth()?),
)) ))
} }
pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> { pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> {
self.target.borrow().initial_value self.target.borrow().initial_value
} }
#[track_caller] #[track_caller]
pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<VA>>) { pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<Element, Len>>) {
let mut target = self.target.borrow_mut(); let mut target = self.target.borrow_mut();
if target.initial_value.is_some() { if target.initial_value.is_some() {
panic!("can't set Mem's initial value more than once"); panic!("can't set Mem's initial value more than once");
} }
let initial_value = initial_value.to_expr().canonical(); let initial_value = Expr::as_dyn_array(initial_value.to_expr());
target.initial_value = Some(Self::check_initial_value_expr( target.initial_value = Some(Self::check_initial_value_expr(
&self.mem_element_type, &self.mem_element_type,
target.depth, target.depth,
initial_value, initial_value,
)); ));
target.depth = Some(initial_value.ty().len()); target.depth = Some(Expr::ty(initial_value).len());
} }
#[track_caller] #[track_caller]
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) { pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
@ -1057,11 +1017,11 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
panic!("can't set Mem's initial value more than once"); panic!("can't set Mem's initial value more than once");
} }
target.initial_value = Some(Self::check_initial_value_bit_slice( target.initial_value = Some(Self::check_initial_value_bit_slice(
&self.mem_element_type, self.mem_element_type,
target.depth, target.depth,
initial_value, initial_value,
)); ));
let element_bit_width = self.mem_element_type.bit_width(); let element_bit_width = self.mem_element_type.canonical().bit_width();
if element_bit_width != 0 { if element_bit_width != 0 {
target.depth = Some(initial_value.len() / element_bit_width); target.depth = Some(initial_value.len() / element_bit_width);
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
// TODO:
pub mod simplify_enums; pub mod simplify_enums;
pub mod simplify_memories; //pub mod simplify_memories;
pub mod visit; pub mod visit;

View file

@ -2,10 +2,14 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
array::{Array, ArrayType}, array::{Array, ArrayType},
bundle::{BundleType, BundleValue, DynBundle, DynBundleType}, bundle::{Bundle, BundleType},
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType}, enum_::{Enum, EnumType, EnumVariant},
expr::{ops, Expr, ExprEnum, ToExpr}, expr::{
int::{DynUInt, DynUIntType, IntCmp}, ops::{self, EnumLiteral},
CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr,
},
hdl,
int::{DynSize, IntCmp, Size, UInt, UIntType},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{DynPortType, Mem, MemPort}, memory::{DynPortType, Mem, MemPort},
module::{ module::{
@ -13,7 +17,7 @@ use crate::{
Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum}, ty::{CanonicalType, Type},
wire::Wire, wire::Wire,
}; };
use core::fmt; use core::fmt;
@ -21,7 +25,7 @@ use hashbrown::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub enum SimplifyEnumsError { pub enum SimplifyEnumsError {
EnumIsNotCastableFromBits { enum_type: DynEnumType }, EnumIsNotCastableFromBits { enum_type: Enum },
} }
impl fmt::Display for SimplifyEnumsError { impl fmt::Display for SimplifyEnumsError {
@ -37,26 +41,23 @@ impl fmt::Display for SimplifyEnumsError {
impl std::error::Error for SimplifyEnumsError {} impl std::error::Error for SimplifyEnumsError {}
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)] #[hdl]
struct TagAndBody<T> { struct TagAndBody<T, BodyWidth: Size> {
tag: T, tag: T,
body: DynUInt, body: UIntType<BodyWidth>,
} }
type TagAndBodyType<T> = <TagAndBody<T> as ToExpr>::Type;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum EnumTypeState { enum EnumTypeState {
TagEnumAndBody(TagAndBodyType<DynEnum>), TagEnumAndBody(TagAndBody<Enum, DynSize>),
TagUIntAndBody(TagAndBodyType<DynUInt>), TagUIntAndBody(TagAndBody<UInt, DynSize>),
UInt(DynUIntType), UInt(UInt),
Unchanged, Unchanged,
} }
struct State { struct State {
enum_types: HashMap<DynEnumType, EnumTypeState>, enum_types: HashMap<Enum, EnumTypeState>,
replacement_mem_ports: replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
HashMap<Interned<MemPort<DynPortType>>, Interned<Wire<Interned<dyn DynCanonicalType>>>>,
kind: SimplifyEnumsKind, kind: SimplifyEnumsKind,
name_id_gen: NameIdGen, name_id_gen: NameIdGen,
} }
@ -64,12 +65,12 @@ struct State {
impl State { impl State {
fn get_or_make_enum_type_state( fn get_or_make_enum_type_state(
&mut self, &mut self,
enum_type: DynEnumType, enum_type: Enum,
) -> Result<EnumTypeState, SimplifyEnumsError> { ) -> Result<EnumTypeState, SimplifyEnumsError> {
if let Some(retval) = self.enum_types.get(&enum_type) { if let Some(retval) = self.enum_types.get(&enum_type) {
return Ok(retval.clone()); return Ok(retval.clone());
} }
if !enum_type.is_castable_from_bits() { if !enum_type.type_properties().is_castable_from_bits {
return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type }); return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
} }
let has_body = enum_type let has_body = enum_type
@ -78,29 +79,29 @@ impl State {
.any(|variant| variant.ty.is_some()); .any(|variant| variant.ty.is_some());
let retval = match (self.kind, has_body) { let retval = match (self.kind, has_body) {
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => { (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { EnumTypeState::TagEnumAndBody(TagAndBody {
tag: DynEnumType::new(Interned::from_iter(enum_type.variants().iter().map( tag: Enum::new(Interned::from_iter(enum_type.variants().iter().map(|v| {
|v| VariantType { EnumVariant {
name: v.name, name: v.name,
ty: None, ty: None,
}, }
))), }))),
body: DynUIntType::new( body: UInt::new_dyn(
enum_type.bit_width() - enum_type.discriminant_bit_width(), enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(),
), ),
}) })
} }
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged, (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged,
(SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => { (SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => {
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { EnumTypeState::TagUIntAndBody(TagAndBody {
tag: DynUIntType::new(enum_type.discriminant_bit_width()), tag: UInt::new_dyn(enum_type.discriminant_bit_width()),
body: DynUIntType::new( body: UInt::new_dyn(
enum_type.bit_width() - enum_type.discriminant_bit_width(), enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(),
), ),
}) })
} }
(SimplifyEnumsKind::ReplaceWithUInt, _) => { (SimplifyEnumsKind::ReplaceWithUInt, _) => {
EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width())) EnumTypeState::UInt(UInt::new_dyn(enum_type.type_properties().bit_width))
} }
}; };
self.enum_types.insert(enum_type, retval.clone()); self.enum_types.insert(enum_type, retval.clone());
@ -108,21 +109,14 @@ impl State {
} }
} }
fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt {
let Some(value) = value else {
return DynUInt::with_type(target_ty, 0u8);
};
DynUInt::from_bit_slice(&value.to_bits())
}
fn connect_port( fn connect_port(
stmts: &mut Vec<Stmt>, stmts: &mut Vec<Stmt>,
lhs: Expr<DynCanonicalValue>, lhs: Expr<CanonicalType>,
rhs: Expr<DynCanonicalValue>, rhs: Expr<CanonicalType>,
source_location: SourceLocation, source_location: SourceLocation,
) { ) {
println!("connect_port: lhs={lhs:?} rhs={rhs:?}"); println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
if lhs.canonical_type() == rhs.canonical_type() { if Expr::ty(lhs) == Expr::ty(rhs) {
stmts.push( stmts.push(
dbg!(StmtConnect { dbg!(StmtConnect {
lhs, lhs,
@ -133,55 +127,53 @@ fn connect_port(
); );
return; return;
} }
match ( match (Expr::ty(lhs), Expr::ty(rhs)) {
lhs.canonical_type().type_enum(), (CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_)) => {
rhs.canonical_type().type_enum(), let lhs = Expr::<Bundle>::from_canonical(lhs);
) {
(TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
let lhs = lhs.with_type::<DynBundle>();
for field in lhs_type.fields() { for field in lhs_type.fields() {
assert!(!field.flipped); assert!(!field.flipped);
connect_port(stmts, lhs.field(&field.name), rhs, source_location); connect_port(stmts, Expr::field(lhs, &field.name), rhs, source_location);
} }
} }
(TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => { (CanonicalType::UInt(_), CanonicalType::Bundle(rhs_type)) => {
let rhs = rhs.with_type::<DynBundle>(); let rhs = Expr::<Bundle>::from_canonical(rhs);
for field in rhs_type.fields() { for field in rhs_type.fields() {
assert!(!field.flipped); assert!(!field.flipped);
connect_port(stmts, lhs, rhs.field(&field.name), source_location); connect_port(stmts, lhs, Expr::field(rhs, &field.name), source_location);
} }
} }
(TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => { (CanonicalType::Bundle(lhs_type), CanonicalType::Bundle(_)) => {
let lhs = lhs.with_type::<DynBundle>(); let lhs = Expr::<Bundle>::from_canonical(lhs);
let rhs = rhs.with_type::<DynBundle>(); let rhs = Expr::<Bundle>::from_canonical(rhs);
for field in lhs_type.fields() { for field in lhs_type.fields() {
let (lhs_field, rhs_field) = if field.flipped { let (lhs_field, rhs_field) = if field.flipped {
(rhs.field(&field.name), lhs.field(&field.name)) (Expr::field(rhs, &field.name), Expr::field(lhs, &field.name))
} else { } else {
(lhs.field(&field.name), rhs.field(&field.name)) (Expr::field(lhs, &field.name), Expr::field(rhs, &field.name))
}; };
connect_port(stmts, lhs_field, rhs_field, source_location); connect_port(stmts, lhs_field, rhs_field, source_location);
} }
} }
(TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => { (CanonicalType::Array(lhs_type), CanonicalType::Array(_)) => {
let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>(); let lhs = Expr::<Array>::from_canonical(lhs);
let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>(); let rhs = Expr::<Array>::from_canonical(rhs);
for index in 0..lhs_type.len() { for index in 0..lhs_type.len() {
connect_port(stmts, lhs[index], rhs[index], source_location); connect_port(stmts, lhs[index], rhs[index], source_location);
} }
} }
(TypeEnum::BundleType(_), _) (CanonicalType::Bundle(_), _)
| (TypeEnum::EnumType(_), _) | (CanonicalType::Enum(_), _)
| (TypeEnum::ArrayType(_), _) | (CanonicalType::Array(_), _)
| (TypeEnum::UInt(_), _) | (CanonicalType::UInt(_), _)
| (TypeEnum::SInt(_), _) | (CanonicalType::SInt(_), _)
| (TypeEnum::Clock(_), _) | (CanonicalType::Bool(_), _)
| (TypeEnum::AsyncReset(_), _) | (CanonicalType::Clock(_), _)
| (TypeEnum::SyncReset(_), _) | (CanonicalType::AsyncReset(_), _)
| (TypeEnum::Reset(_), _) => unreachable!( | (CanonicalType::SyncReset(_), _)
| (CanonicalType::Reset(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}", "trying to connect memory ports:\n{:?}\n{:?}",
lhs.canonical_type().type_enum(), Expr::ty(lhs),
rhs.canonical_type().type_enum(), Expr::ty(rhs),
), ),
} }
} }
@ -189,18 +181,11 @@ fn connect_port(
impl Folder for State { impl Folder for State {
type Error = SimplifyEnumsError; type Error = SimplifyEnumsError;
fn fold_dyn_enum(&mut self, _v: DynEnum) -> Result<DynEnum, Self::Error> { fn fold_enum(&mut self, _v: Enum) -> Result<Enum, Self::Error> {
unreachable!() unreachable!()
} }
fn fold_dyn_enum_type(&mut self, _v: DynEnumType) -> Result<DynEnumType, Self::Error> { fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
unreachable!()
}
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
where
T::Type: BundleType<Value = T>,
{
let old_name_id_gen = let old_name_id_gen =
std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical())); std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical()));
let retval = Fold::default_fold(v, self); let retval = Fold::default_fold(v, self);
@ -211,98 +196,86 @@ impl Folder for State {
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> { fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
match op { match op {
ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? { ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { tag, body }) => { EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
TagAndBodyType::<DynEnum>::builder() <TagAndBody<Enum, DynSize> as BundleType>::Builder::default()
.field_tag(DynEnum::new_by_index(tag, op.variant_index(), None)) .field_tag(EnumLiteral::new_by_index(tag, op.variant_index(), None))
.field_body(match op.variant_value() { .field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(body, 0u8).to_expr(), None => body.zero().to_expr(),
}) })
.build() .to_expr(),
.expr_enum()
}
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { tag, body }) => {
TagAndBodyType::<DynUInt>::builder()
.field_tag(DynUInt::with_type(tag, op.variant_index()))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(body, 0u8).to_expr(),
})
.build()
.expr_enum()
}
EnumTypeState::UInt(_) => TagAndBodyType::<DynUInt>::builder()
.field_tag(DynUInt::with_type(
DynUIntType::new(op.ty().discriminant_bit_width()),
op.variant_index(),
))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(
DynUIntType::new(
op.ty().bit_width() - op.ty().discriminant_bit_width(),
), ),
0u8, EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => *Expr::expr_enum(
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
.field_tag(tag.from_int_wrapping(op.variant_index()))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => body.zero().to_expr(),
})
.to_expr(),
),
EnumTypeState::UInt(_) => *Expr::expr_enum(
<TagAndBody<UInt, DynSize> as BundleType>::Builder::default()
.field_tag(
UIntType::new(op.ty().discriminant_bit_width())
.from_int_wrapping(op.variant_index()),
) )
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => UIntType::new(
op.ty().type_properties().bit_width
- op.ty().discriminant_bit_width(),
)
.zero()
.to_expr(), .to_expr(),
}) })
.build() .cast_to_bits(),
.cast_to_bits()
.expr_enum(),
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(
ops::EnumLiteral::new_unchecked(
op.variant_value().map(|v| v.fold(self)).transpose()?,
op.variant_index(),
op.ty(),
)
.intern_sized(),
), ),
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(ops::EnumLiteral::new_by_index(
op.ty(),
op.variant_index(),
op.variant_value().map(|v| v.fold(self)).transpose()?,
)),
}), }),
ExprEnum::VariantAccess(op) => { ExprEnum::VariantAccess(op) => Ok(
Ok(match self.get_or_make_enum_type_state(op.base().ty())? { match self.get_or_make_enum_type_state(Expr::ty(op.base()))? {
EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => { EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
match op.variant_type().ty { match op.variant_type() {
Some(_) => op Some(variant_type) => *Expr::expr_enum(
.base() Expr::<TagAndBody<CanonicalType, DynSize>>::from_canonical(
.expr_enum() (*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
.fold(self)? )
.to_expr() .body[..variant_type.bit_width()]
.with_type::<TagAndBody<DynCanonicalValue>>() .cast_bits_to(variant_type),
.body[..op.ty().bit_width()] ),
.cast_bits_to::<DynCanonicalValue>(op.ty()) None => *Expr::expr_enum(().to_expr()),
.expr_enum(),
None => ().to_expr().expr_enum(),
} }
} }
EnumTypeState::UInt(_) => match op.variant_type().ty { EnumTypeState::UInt(_) => match op.variant_type() {
Some(_) => { Some(variant_type) => {
let base_int = op let base_int = Expr::<UInt>::from_canonical(
.base() (*Expr::expr_enum(op.base())).fold(self)?.to_expr(),
.expr_enum() );
.fold(self)?
.to_expr()
.with_type::<DynUInt>();
dbg!(base_int); dbg!(base_int);
let base_ty = op.base().ty(); let base_ty = Expr::ty(op.base());
let ty_bit_width = op.ty().bit_width(); let variant_type_bit_width = variant_type.bit_width();
base_int[base_ty.discriminant_bit_width()..][..ty_bit_width] *Expr::expr_enum(
.cast_bits_to::<DynCanonicalValue>(op.ty()) base_int[base_ty.discriminant_bit_width()..]
.expr_enum() [..variant_type_bit_width]
.cast_bits_to(variant_type),
)
} }
None => ().to_expr().expr_enum(), None => *Expr::expr_enum(().to_expr()),
}, },
EnumTypeState::Unchanged => match op.variant_type().ty { EnumTypeState::Unchanged => match op.variant_type() {
Some(_) => ExprEnum::VariantAccess( Some(_) => ExprEnum::VariantAccess(ops::VariantAccess::new_by_index(
ops::VariantAccess::new_unchecked(
op.base().fold(self)?, op.base().fold(self)?,
op.variant_index(), op.variant_index(),
) )),
.intern_sized(), None => *Expr::expr_enum(().to_expr()),
),
None => ().to_expr().expr_enum(),
}, },
}) },
} ),
ExprEnum::MemPort(mem_port) => Ok( ExprEnum::MemPort(mem_port) => Ok(
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
ExprEnum::Wire(wire) ExprEnum::Wire(wire)
@ -310,24 +283,34 @@ impl Folder for State {
ExprEnum::MemPort(mem_port.fold(self)?) ExprEnum::MemPort(mem_port.fold(self)?)
}, },
), ),
ExprEnum::Literal(_) ExprEnum::UIntLiteral(_)
| ExprEnum::ArrayLiteral(_) | ExprEnum::SIntLiteral(_)
| ExprEnum::BoolLiteral(_)
| ExprEnum::BundleLiteral(_) | ExprEnum::BundleLiteral(_)
| ExprEnum::ArrayLiteral(_)
| ExprEnum::NotU(_) | ExprEnum::NotU(_)
| ExprEnum::NotS(_) | ExprEnum::NotS(_)
| ExprEnum::NotB(_)
| ExprEnum::Neg(_) | ExprEnum::Neg(_)
| ExprEnum::BitAndU(_) | ExprEnum::BitAndU(_)
| ExprEnum::BitAndS(_) | ExprEnum::BitAndS(_)
| ExprEnum::BitAndB(_)
| ExprEnum::BitOrU(_) | ExprEnum::BitOrU(_)
| ExprEnum::BitOrS(_) | ExprEnum::BitOrS(_)
| ExprEnum::BitOrB(_)
| ExprEnum::BitXorU(_) | ExprEnum::BitXorU(_)
| ExprEnum::BitXorS(_) | ExprEnum::BitXorS(_)
| ExprEnum::BitXorB(_)
| ExprEnum::AddU(_) | ExprEnum::AddU(_)
| ExprEnum::AddS(_) | ExprEnum::AddS(_)
| ExprEnum::SubU(_) | ExprEnum::SubU(_)
| ExprEnum::SubS(_) | ExprEnum::SubS(_)
| ExprEnum::MulU(_) | ExprEnum::MulU(_)
| ExprEnum::MulS(_) | ExprEnum::MulS(_)
| ExprEnum::DivU(_)
| ExprEnum::DivS(_)
| ExprEnum::RemU(_)
| ExprEnum::RemS(_)
| ExprEnum::DynShlU(_) | ExprEnum::DynShlU(_)
| ExprEnum::DynShlS(_) | ExprEnum::DynShlS(_)
| ExprEnum::DynShrU(_) | ExprEnum::DynShrU(_)
@ -336,41 +319,68 @@ impl Folder for State {
| ExprEnum::FixedShlS(_) | ExprEnum::FixedShlS(_)
| ExprEnum::FixedShrU(_) | ExprEnum::FixedShrU(_)
| ExprEnum::FixedShrS(_) | ExprEnum::FixedShrS(_)
| ExprEnum::CmpLtB(_)
| ExprEnum::CmpLeB(_)
| ExprEnum::CmpGtB(_)
| ExprEnum::CmpGeB(_)
| ExprEnum::CmpEqB(_)
| ExprEnum::CmpNeB(_)
| ExprEnum::CmpLtU(_) | ExprEnum::CmpLtU(_)
| ExprEnum::CmpLtS(_)
| ExprEnum::CmpLeU(_) | ExprEnum::CmpLeU(_)
| ExprEnum::CmpLeS(_)
| ExprEnum::CmpGtU(_) | ExprEnum::CmpGtU(_)
| ExprEnum::CmpGtS(_)
| ExprEnum::CmpGeU(_) | ExprEnum::CmpGeU(_)
| ExprEnum::CmpGeS(_)
| ExprEnum::CmpEqU(_) | ExprEnum::CmpEqU(_)
| ExprEnum::CmpEqS(_)
| ExprEnum::CmpNeU(_) | ExprEnum::CmpNeU(_)
| ExprEnum::CmpLtS(_)
| ExprEnum::CmpLeS(_)
| ExprEnum::CmpGtS(_)
| ExprEnum::CmpGeS(_)
| ExprEnum::CmpEqS(_)
| ExprEnum::CmpNeS(_) | ExprEnum::CmpNeS(_)
| ExprEnum::CastUIntToUInt(_) | ExprEnum::CastUIntToUInt(_)
| ExprEnum::CastUIntToSInt(_) | ExprEnum::CastUIntToSInt(_)
| ExprEnum::CastSIntToUInt(_) | ExprEnum::CastSIntToUInt(_)
| ExprEnum::CastSIntToSInt(_) | ExprEnum::CastSIntToSInt(_)
| ExprEnum::SliceUInt(_) | ExprEnum::CastBoolToUInt(_)
| ExprEnum::SliceSInt(_) | ExprEnum::CastBoolToSInt(_)
| ExprEnum::ReduceBitAnd(_) | ExprEnum::CastUIntToBool(_)
| ExprEnum::ReduceBitOr(_) | ExprEnum::CastSIntToBool(_)
| ExprEnum::ReduceBitXor(_) | ExprEnum::CastBoolToSyncReset(_)
| ExprEnum::CastUIntToSyncReset(_)
| ExprEnum::CastSIntToSyncReset(_)
| ExprEnum::CastBoolToAsyncReset(_)
| ExprEnum::CastUIntToAsyncReset(_)
| ExprEnum::CastSIntToAsyncReset(_)
| ExprEnum::CastSyncResetToBool(_)
| ExprEnum::CastSyncResetToUInt(_)
| ExprEnum::CastSyncResetToSInt(_)
| ExprEnum::CastSyncResetToReset(_)
| ExprEnum::CastAsyncResetToBool(_)
| ExprEnum::CastAsyncResetToUInt(_)
| ExprEnum::CastAsyncResetToSInt(_)
| ExprEnum::CastAsyncResetToReset(_)
| ExprEnum::CastResetToBool(_)
| ExprEnum::CastResetToUInt(_)
| ExprEnum::CastResetToSInt(_)
| ExprEnum::CastBoolToClock(_)
| ExprEnum::CastUIntToClock(_)
| ExprEnum::CastSIntToClock(_)
| ExprEnum::CastClockToBool(_)
| ExprEnum::CastClockToUInt(_)
| ExprEnum::CastClockToSInt(_)
| ExprEnum::FieldAccess(_) | ExprEnum::FieldAccess(_)
| ExprEnum::ArrayIndex(_) | ExprEnum::ArrayIndex(_)
| ExprEnum::DynArrayIndex(_) | ExprEnum::DynArrayIndex(_)
| ExprEnum::ReduceBitAndU(_)
| ExprEnum::ReduceBitAndS(_)
| ExprEnum::ReduceBitOrU(_)
| ExprEnum::ReduceBitOrS(_)
| ExprEnum::ReduceBitXorU(_)
| ExprEnum::ReduceBitXorS(_)
| ExprEnum::IntSliceU(_)
| ExprEnum::IntSliceS(_)
| ExprEnum::CastToBits(_) | ExprEnum::CastToBits(_)
| ExprEnum::CastBitsTo(_) | ExprEnum::CastBitsTo(_)
| ExprEnum::CastBitToClock(_)
| ExprEnum::CastBitToSyncReset(_)
| ExprEnum::CastBitToAsyncReset(_)
| ExprEnum::CastSyncResetToReset(_)
| ExprEnum::CastAsyncResetToReset(_)
| ExprEnum::CastClockToBit(_)
| ExprEnum::CastSyncResetToBit(_)
| ExprEnum::CastAsyncResetToBit(_)
| ExprEnum::CastResetToBit(_)
| ExprEnum::ModuleIO(_) | ExprEnum::ModuleIO(_)
| ExprEnum::Instance(_) | ExprEnum::Instance(_)
| ExprEnum::Wire(_) | ExprEnum::Wire(_)
@ -382,7 +392,7 @@ impl Folder for State {
let mut memories = vec![]; let mut memories = vec![];
let mut stmts = vec![]; let mut stmts = vec![];
for memory in block.memories { for memory in block.memories {
let old_element_ty = *memory.array_type().element(); let old_element_ty = memory.array_type().element();
let new_element_ty = memory.array_type().element().fold(self)?; let new_element_ty = memory.array_type().element().fold(self)?;
if new_element_ty != old_element_ty { if new_element_ty != old_element_ty {
let mut new_ports = vec![]; let mut new_ports = vec![];
@ -394,7 +404,7 @@ impl Folder for State {
port.addr_type(), port.addr_type(),
new_element_ty, new_element_ty,
); );
new_ports.push(new_port.intern()); new_ports.push(new_port);
let new_port_ty = new_port.ty(); let new_port_ty = new_port.ty();
let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields()); let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields());
if let Some(wmask_name) = new_port.port_kind().wmask_name() { if let Some(wmask_name) = new_port.port_kind().wmask_name() {
@ -404,7 +414,7 @@ impl Folder for State {
.unwrap(); .unwrap();
wire_ty_fields[index].ty = port.ty().fields()[index].ty; wire_ty_fields[index].ty = port.ty().fields()[index].ty;
} }
let wire_ty = DynBundleType::new(Intern::intern_owned(wire_ty_fields)); let wire_ty = Bundle::new(Intern::intern_owned(wire_ty_fields));
if wire_ty == new_port_ty { if wire_ty == new_port_ty {
continue; continue;
} }
@ -419,23 +429,22 @@ impl Folder for State {
stmts.push( stmts.push(
StmtWire { StmtWire {
annotations: Default::default(), annotations: Default::default(),
wire: wire.to_dyn_canonical_wire(), wire: wire.canonical(),
} }
.into(), .into(),
); );
connect_port( connect_port(
&mut stmts, &mut stmts,
new_port.to_expr().to_canonical_dyn(), Expr::canonical(new_port.to_expr()),
wire.to_expr().to_canonical_dyn(), Expr::canonical(wire.to_expr()),
port.source_location(), port.source_location(),
); );
self.replacement_mem_ports self.replacement_mem_ports.insert(port, wire.canonical());
.insert(port, wire.to_dyn_canonical_wire().intern_sized());
} }
memories.push(Mem::new_unchecked( memories.push(Mem::new_unchecked(
memory.scoped_name(), memory.scoped_name(),
memory.source_location(), memory.source_location(),
ArrayType::new_slice(new_element_ty, memory.array_type().len()), ArrayType::new_dyn(new_element_ty, memory.array_type().len()),
memory.initial_value(), memory.initial_value(),
Intern::intern_owned(new_ports), Intern::intern_owned(new_ports),
memory.read_latency(), memory.read_latency(),
@ -458,7 +467,7 @@ impl Folder for State {
fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> { fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> {
fn match_int_tag( fn match_int_tag(
state: &mut State, state: &mut State,
int_tag_expr: Expr<DynUInt>, int_tag_expr: Expr<UInt>,
source_location: SourceLocation, source_location: SourceLocation,
blocks: Interned<[Block]>, blocks: Interned<[Block]>,
) -> Result<StmtIf, SimplifyEnumsError> { ) -> Result<StmtIf, SimplifyEnumsError> {
@ -473,16 +482,15 @@ impl Folder for State {
}); });
}; };
let mut retval = StmtIf { let mut retval = StmtIf {
cond: int_tag_expr.cmp_eq(DynUInt::with_type( cond: int_tag_expr
int_tag_expr.ty(), .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
next_to_last_variant_index,
)),
source_location, source_location,
blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?], blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?],
}; };
for (variant_index, block) in blocks_iter.rev() { for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf { retval = StmtIf {
cond: int_tag_expr.cmp_eq(DynUInt::with_type(int_tag_expr.ty(), variant_index)), cond: int_tag_expr
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
source_location, source_location,
blocks: [ blocks: [
block.fold(state)?, block.fold(state)?,
@ -500,33 +508,27 @@ impl Folder for State {
expr, expr,
source_location, source_location,
blocks, blocks,
}) => match self.get_or_make_enum_type_state(expr.ty())? { }) => match self.get_or_make_enum_type_state(Expr::ty(expr))? {
EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch { EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
expr: expr expr: Expr::<TagAndBody<Enum, DynSize>>::from_canonical(
.expr_enum() Expr::canonical(expr).fold(self)?,
.fold(self)? )
.to_expr()
.with_type::<TagAndBody<DynEnum>>()
.tag, .tag,
source_location, source_location,
blocks: blocks.fold(self)?, blocks: blocks.fold(self)?,
} }
.into()), .into()),
EnumTypeState::TagUIntAndBody(_) => { EnumTypeState::TagUIntAndBody(_) => {
let int_tag_expr = expr let int_tag_expr = Expr::<TagAndBody<UInt, DynSize>>::from_canonical(
.expr_enum() Expr::canonical(expr).fold(self)?,
.fold(self)? )
.to_expr()
.with_type::<TagAndBody<DynUInt>>()
.tag; .tag;
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
} }
EnumTypeState::UInt(_) => { EnumTypeState::UInt(_) => {
let int_tag_expr = expr let int_tag_expr =
.expr_enum() Expr::<UInt>::from_canonical(Expr::canonical(expr).fold(self)?)
.fold(self)? [..Expr::ty(expr).discriminant_bit_width()];
.to_expr()
.with_type::<DynUInt>()[..expr.ty().discriminant_bit_width()];
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
} }
EnumTypeState::Unchanged => Ok(StmtMatch { EnumTypeState::Unchanged => Ok(StmtMatch {
@ -544,92 +546,49 @@ impl Folder for State {
unreachable!() unreachable!()
} }
fn fold_type_enum(&mut self, type_enum: TypeEnum) -> Result<TypeEnum, Self::Error> { fn fold_canonical_type(
match type_enum { &mut self,
TypeEnum::EnumType(enum_type) => { canonical_type: CanonicalType,
) -> Result<CanonicalType, Self::Error> {
match canonical_type {
CanonicalType::Enum(enum_type) => {
Ok(match self.get_or_make_enum_type_state(enum_type)? { Ok(match self.get_or_make_enum_type_state(enum_type)? {
EnumTypeState::TagEnumAndBody(ty) => TypeEnum::BundleType(ty.canonical()), EnumTypeState::TagEnumAndBody(ty) => ty.canonical(),
EnumTypeState::TagUIntAndBody(ty) => TypeEnum::BundleType(ty.canonical()), EnumTypeState::TagUIntAndBody(ty) => ty.canonical(),
EnumTypeState::UInt(ty) => TypeEnum::UInt(ty), EnumTypeState::UInt(ty) => ty.canonical(),
EnumTypeState::Unchanged => TypeEnum::EnumType(enum_type), EnumTypeState::Unchanged => enum_type.canonical(),
}) })
} }
TypeEnum::BundleType(_) CanonicalType::Bundle(_)
| TypeEnum::ArrayType(_) | CanonicalType::Array(_)
| TypeEnum::UInt(_) | CanonicalType::UInt(_)
| TypeEnum::SInt(_) | CanonicalType::SInt(_)
| TypeEnum::Clock(_) | CanonicalType::Bool(_)
| TypeEnum::AsyncReset(_) | CanonicalType::Clock(_)
| TypeEnum::SyncReset(_) | CanonicalType::AsyncReset(_)
| TypeEnum::Reset(_) => type_enum.default_fold(self), | CanonicalType::SyncReset(_)
| CanonicalType::Reset(_) => canonical_type.default_fold(self),
} }
} }
fn fold_value_enum(&mut self, value_enum: ValueEnum) -> Result<ValueEnum, Self::Error> { fn fold_enum_variant(&mut self, _v: EnumVariant) -> Result<EnumVariant, Self::Error> {
match value_enum {
ValueEnum::Enum(enum_value) => {
Ok(match self.get_or_make_enum_type_state(enum_value.ty())? {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
tag: tag_ty,
body: body_ty,
}) => ValueEnum::Bundle(
TagAndBody {
tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
}
.to_canonical(),
),
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
tag: tag_ty,
body: body_ty,
}) => ValueEnum::Bundle(
TagAndBody {
tag: DynUInt::with_type(tag_ty, enum_value.variant_index()),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
}
.to_canonical(),
),
EnumTypeState::UInt(target_ty) => {
ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty))
}
EnumTypeState::Unchanged => ValueEnum::Enum(enum_value),
})
}
ValueEnum::Bundle(_)
| ValueEnum::Array(_)
| ValueEnum::UInt(_)
| ValueEnum::SInt(_)
| ValueEnum::Clock(_)
| ValueEnum::AsyncReset(_)
| ValueEnum::SyncReset(_)
| ValueEnum::Reset(_) => value_enum.default_fold(self),
}
}
fn fold_variant_type<T>(&mut self, _v: VariantType<T>) -> Result<VariantType<T>, Self::Error> {
unreachable!() unreachable!()
} }
fn fold_enum_literal<EnumTy>( fn fold_enum_literal<T: EnumType>(
&mut self, &mut self,
_v: ops::EnumLiteral<EnumTy>, _v: ops::EnumLiteral<T>,
) -> Result<ops::EnumLiteral<EnumTy>, Self::Error> ) -> Result<ops::EnumLiteral<T>, Self::Error>
where where
EnumTy: EnumType, T: Fold<Self>,
EnumTy::Value: EnumValue<Type = EnumTy>,
{ {
unreachable!() unreachable!()
} }
fn fold_variant_access<T, VariantTy>( fn fold_variant_access<VariantType: Type>(
&mut self, &mut self,
_v: ops::VariantAccess<T, VariantTy>, _v: ops::VariantAccess<VariantType>,
) -> Result<ops::VariantAccess<T, VariantTy>, Self::Error> ) -> Result<ops::VariantAccess<VariantType>, Self::Error> {
where
T: EnumType,
T::Value: EnumValue,
VariantTy: Type,
{
unreachable!() unreachable!()
} }
} }
@ -642,9 +601,9 @@ pub enum SimplifyEnumsKind {
} }
pub fn simplify_enums( pub fn simplify_enums(
module: Interned<Module<DynBundle>>, module: Interned<Module<Bundle>>,
kind: SimplifyEnumsKind, kind: SimplifyEnumsKind,
) -> Result<Interned<Module<DynBundle>>, SimplifyEnumsError> { ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
module.fold(&mut State { module.fold(&mut State {
enum_types: HashMap::new(), enum_types: HashMap::new(),
replacement_mem_ports: HashMap::new(), replacement_mem_ports: HashMap::new(),

View file

@ -3,18 +3,19 @@
#![allow(clippy::multiple_bound_locations)] #![allow(clippy::multiple_bound_locations)]
use crate::{ use crate::{
annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation}, annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, array::ArrayType,
bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType}, bundle::{Bundle, BundleField, BundleType},
clock::{Clock, ClockType}, clock::Clock,
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType}, enum_::{Enum, EnumType, EnumVariant},
expr::{ expr::{
ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement, ops,
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr, target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
}, },
int::{ Expr, ExprEnum,
DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, IntType,
IntTypeTrait,
}, },
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
module::{ module::{
@ -24,10 +25,9 @@ use crate::{
StmtMatch, StmtReg, StmtWire, StmtMatch, StmtReg, StmtWire,
}, },
reg::Reg, reg::Reg,
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType}, reset::{AsyncReset, Reset, SyncReset},
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum}, ty::{CanonicalType, Type},
util::{ConstBool, GenericConstBool},
wire::Wire, wire::Wire,
}; };
use num_bigint::{BigInt, BigUint}; use num_bigint::{BigInt, BigUint};
@ -473,7 +473,4 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut
} }
} }
type InternedDynType = Interned<dyn DynType>;
type InternedDynCanonicalType = Interned<dyn DynCanonicalType>;
include!(concat!(env!("OUT_DIR"), "/visit.rs")); include!(concat!(env!("OUT_DIR"), "/visit.rs"));

View file

@ -2,21 +2,21 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
clock::ClockDomain, clock::ClockDomain,
expr::{Expr, ExprTrait, Flow, ToExpr}, expr::{Expr, Flow},
intern::Interned, intern::Interned,
module::{NameId, ScopedNameId}, module::{NameId, ScopedNameId},
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynType, Type}, ty::{CanonicalType, Type},
}; };
use std::fmt; use std::fmt;
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Reg<T: Type> { pub struct Reg<T: Type> {
name: ScopedNameId, name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
ty: T, ty: T,
clock_domain: Expr<ClockDomain>, clock_domain: Expr<ClockDomain>,
init: Option<Expr<T::Value>>, init: Option<Expr<T>>,
} }
impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> { impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
@ -37,20 +37,8 @@ impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
} }
} }
impl<T: Type> ToExpr for Reg<T> {
type Type = T;
fn ty(&self) -> Self::Type {
self.ty.clone()
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::new_unchecked(self.expr_enum())
}
}
impl<T: Type> Reg<T> { impl<T: Type> Reg<T> {
pub fn canonical(&self) -> Reg<T::CanonicalType> { pub fn canonical(&self) -> Reg<CanonicalType> {
let Self { let Self {
name, name,
source_location, source_location,
@ -66,49 +54,20 @@ impl<T: Type> Reg<T> {
init: init.map(Expr::canonical), init: init.map(Expr::canonical),
} }
} }
pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> {
let Self {
name,
source_location,
ref ty,
clock_domain,
init,
} = *self;
Reg {
name,
source_location,
ty: ty.to_dyn(),
clock_domain,
init: init.map(Expr::to_dyn),
}
}
pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> {
let Self {
name,
source_location,
ref ty,
clock_domain,
init,
} = *self;
Reg {
name,
source_location,
ty: ty.canonical_dyn(),
clock_domain,
init: init.map(Expr::to_canonical_dyn),
}
}
#[track_caller] #[track_caller]
pub fn new_unchecked( pub fn new_unchecked(
scoped_name: ScopedNameId, scoped_name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
ty: T, ty: T,
clock_domain: Expr<ClockDomain>, clock_domain: Expr<ClockDomain>,
init: Option<Expr<T::Value>>, init: Option<Expr<T>>,
) -> Self { ) -> Self {
assert!(ty.is_storable(), "register type must be a storable type"); assert!(
ty.canonical().is_storable(),
"register type must be a storable type"
);
if let Some(init) = init { if let Some(init) = init {
assert_eq!(ty, init.ty(), "register's type must match init type"); assert_eq!(ty, Expr::ty(init), "register's type must match init type");
} }
Self { Self {
name: scoped_name, name: scoped_name,
@ -118,6 +77,9 @@ impl<T: Type> Reg<T> {
init, init,
} }
} }
pub fn ty(&self) -> T {
self.ty
}
pub fn source_location(&self) -> SourceLocation { pub fn source_location(&self) -> SourceLocation {
self.source_location self.source_location
} }
@ -139,7 +101,7 @@ impl<T: Type> Reg<T> {
pub fn clock_domain(&self) -> Expr<ClockDomain> { pub fn clock_domain(&self) -> Expr<ClockDomain> {
self.clock_domain self.clock_domain
} }
pub fn init(&self) -> Option<Expr<T::Value>> { pub fn init(&self) -> Option<Expr<T>> {
self.init self.init
} }
pub fn flow(&self) -> Flow { pub fn flow(&self) -> Flow {

View file

@ -2,358 +2,119 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, ToExpr}, expr::{Expr, ToExpr},
int::{UInt, UIntType}, int::Bool,
intern::Interned,
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
},
util::interned_bit,
}; };
use bitvec::slice::BitSlice;
pub trait ResetTypeTrait: CanonicalType + StaticType<MaskType = UIntType<1>> {} mod sealed {
pub trait ResetTypeSealed {}
}
pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {}
macro_rules! reset_type {
($name:ident, $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal) => {
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct AsyncResetType; pub struct $name;
impl AsyncResetType { impl Type for $name {
pub const fn new() -> Self { type BaseType = $name;
Self type MaskType = Bool;
}
}
impl Type for AsyncResetType { impl_match_variant_as_self!();
type Value = AsyncReset;
type CanonicalType = AsyncResetType;
type CanonicalValue = AsyncReset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
UIntType::new() Bool
} }
fn canonical(&self) -> Self::CanonicalType { fn canonical(&self) -> CanonicalType {
*self CanonicalType::$name(*self)
} }
fn source_location(&self) -> SourceLocation { fn source_location() -> SourceLocation {
SourceLocation::builtin() SourceLocation::builtin()
} }
fn type_enum(&self) -> TypeEnum { fn from_canonical(canonical_type: CanonicalType) -> Self {
TypeEnum::AsyncReset(*self) let CanonicalType::$name(retval) = canonical_type else {
} panic!("expected {}", stringify!($name));
};
fn from_canonical_type(t: Self::CanonicalType) -> Self { retval
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
} }
} }
impl Connect<Self> for AsyncResetType {} impl $name {
pub fn type_properties(self) -> TypeProperties {
impl CanonicalType for AsyncResetType { Self::TYPE_PROPERTIES
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset;
} }
pub fn can_connect(self, _rhs: Self) -> bool {
impl StaticType for AsyncResetType { true
fn static_type() -> Self {
Self
} }
} }
impl ResetTypeTrait for AsyncResetType {} impl StaticType for $name {
const TYPE: Self = Self;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] const MASK_TYPE: Self::MaskType = Bool;
pub struct AsyncReset(pub bool); const TYPE_PROPERTIES: TypeProperties = TypeProperties {
is_passive: true,
impl ToExpr for AsyncReset { is_storable: false,
type Type = AsyncResetType; is_castable_from_bits: $is_castable_from_bits,
bit_width: 1,
fn ty(&self) -> Self::Type { };
AsyncResetType const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
} }
fn to_expr(&self) -> Expr<Self> { impl sealed::ResetTypeSealed for $name {}
Expr::from_value(self)
}
}
impl Value for AsyncReset { impl ResetType for $name {}
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for AsyncReset { pub trait $Trait {
fn value_enum_impl(this: &Self) -> ValueEnum { fn $trait_fn(&self) -> Expr<$name>;
ValueEnum::AsyncReset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct SyncResetType;
impl SyncResetType {
pub const fn new() -> Self {
Self
}
}
impl Type for SyncResetType {
type CanonicalType = SyncResetType;
type Value = SyncReset;
type CanonicalValue = SyncReset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
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::SyncReset(*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 SyncResetType {}
impl CanonicalType for SyncResetType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset;
}
impl StaticType for SyncResetType {
fn static_type() -> Self {
Self
}
}
impl ResetTypeTrait for SyncResetType {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct SyncReset(pub bool);
impl ToExpr for SyncReset {
type Type = SyncResetType;
fn ty(&self) -> Self::Type {
SyncResetType
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for SyncReset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for SyncReset {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::SyncReset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub struct ResetType;
impl ResetType {
pub const fn new() -> Self {
Self
}
}
impl Type for ResetType {
type Value = Reset;
type CanonicalType = ResetType;
type CanonicalValue = Reset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
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::Reset(*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 ResetType {}
impl CanonicalType for ResetType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset;
}
impl StaticType for ResetType {
fn static_type() -> Self {
Self
}
}
impl ResetTypeTrait for ResetType {}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Reset {}
impl ToExpr for Reset {
type Type = ResetType;
fn ty(&self) -> Self::Type {
match *self {}
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for Reset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
match *this {}
}
}
impl CanonicalValue for Reset {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Reset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
match *this {}
}
}
macro_rules! make_to_reset {
(
$(#[from_value($from_value_ty:ty)])*
$vis:vis trait $Trait:ident {
fn $fn:ident(&self) -> Expr<$T:ty>;
}
) => {
$vis trait $Trait {
fn $fn(&self) -> Expr<$T>;
} }
impl<T: ?Sized + $Trait> $Trait for &'_ T { impl<T: ?Sized + $Trait> $Trait for &'_ T {
fn $fn(&self) -> Expr<$T> { fn $trait_fn(&self) -> Expr<$name> {
(**self).$fn() (**self).$trait_fn()
} }
} }
impl<T: ?Sized + $Trait> $Trait for &'_ mut T { impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
fn $fn(&self) -> Expr<$T> { fn $trait_fn(&self) -> Expr<$name> {
(**self).$fn() (**self).$trait_fn()
} }
} }
impl<T: ?Sized + $Trait> $Trait for Box<T> { impl<T: ?Sized + $Trait> $Trait for Box<T> {
fn $fn(&self) -> Expr<$T> { fn $trait_fn(&self) -> Expr<$name> {
(**self).$fn() (**self).$trait_fn()
} }
} }
impl $Trait for Expr<$T> { impl $Trait for Expr<$name> {
fn $fn(&self) -> Expr<$T> { fn $trait_fn(&self) -> Expr<$name> {
*self *self
} }
} }
impl $Trait for $T {
fn $fn(&self) -> Expr<$T> {
self.to_expr()
}
}
$(impl $Trait for $from_value_ty {
fn $fn(&self) -> Expr<$T> {
self.to_expr().$fn()
}
})*
}; };
} }
make_to_reset! { reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true);
#[from_value(SyncReset)] reset_type!(SyncReset, ToSyncReset::to_sync_reset, true);
#[from_value(AsyncReset)] reset_type!(
pub trait ToReset { Reset,
fn to_reset(&self) -> Expr<Reset>; ToReset::to_reset,
false // Reset is not castable from bits because we don't know if it's async or sync
);
impl ToSyncReset for bool {
fn to_sync_reset(&self) -> Expr<SyncReset> {
self.to_expr().to_sync_reset()
} }
} }
make_to_reset! { impl ToAsyncReset for bool {
#[from_value(bool)] fn to_async_reset(&self) -> Expr<AsyncReset> {
#[from_value(UInt<1>)] self.to_expr().to_async_reset()
pub trait ToAsyncReset {
fn to_async_reset(&self) -> Expr<AsyncReset>;
}
}
make_to_reset! {
#[from_value(bool)]
#[from_value(UInt<1>)]
pub trait ToSyncReset {
fn to_sync_reset(&self) -> Expr<SyncReset>;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -3,10 +3,13 @@
mod const_bool; mod const_bool;
mod const_cmp; mod const_cmp;
mod const_usize;
mod misc; mod misc;
#[doc(inline)] #[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
#[doc(inline)]
pub use const_usize::{ConstUsize, GenericConstUsize};
#[doc(inline)] #[doc(inline)]
pub use const_cmp::{ pub use const_cmp::{

View 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;
}

View file

@ -1,15 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{Expr, ExprTrait, Flow, ToExpr}, expr::Flow,
intern::Interned, intern::Interned,
module::{NameId, ScopedNameId}, module::{NameId, ScopedNameId},
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynType, Type}, ty::{CanonicalType, Type},
}; };
use std::fmt; use std::fmt;
#[derive(Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Wire<T: Type> { pub struct Wire<T: Type> {
name: ScopedNameId, name: ScopedNameId,
source_location: SourceLocation, source_location: SourceLocation,
@ -25,20 +25,8 @@ impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
} }
} }
impl<T: Type> ToExpr for Wire<T> {
type Type = T;
fn ty(&self) -> Self::Type {
self.ty.clone()
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::new_unchecked(self.expr_enum())
}
}
impl<T: Type> Wire<T> { impl<T: Type> Wire<T> {
pub fn canonical(&self) -> Wire<T::CanonicalType> { pub fn canonical(&self) -> Wire<CanonicalType> {
let Self { let Self {
name, name,
source_location, source_location,
@ -50,29 +38,8 @@ impl<T: Type> Wire<T> {
ty: ty.canonical(), ty: ty.canonical(),
} }
} }
pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> { pub fn ty(&self) -> T {
let Self { self.ty
name,
source_location,
ref ty,
} = *self;
Wire {
name,
source_location,
ty: ty.to_dyn(),
}
}
pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> {
let Self {
name,
source_location,
ref ty,
} = *self;
Wire {
name,
source_location,
ty: ty.canonical_dyn(),
}
} }
pub fn new_unchecked( pub fn new_unchecked(
scoped_name: ScopedNameId, scoped_name: ScopedNameId,

View file

@ -1,32 +1,26 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::{ use fayalite::{
int::UInt, array::ArrayType,
ty::{StaticValue, Value}, hdl,
int::{IntType, Size, UInt},
}; };
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub struct S<T> { pub struct S<T: IntType, Len: Size> {
pub a: T, pub a: T,
b: UInt<3>, b: UInt<3>,
pub(crate) c: ArrayType<UInt<1>, Len>,
} }
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub enum E<T> { pub enum E<T> {
A, A,
B {}, B(UInt<3>),
C(), C(T),
D(UInt<3>),
E { a: UInt<3> },
F(UInt<3>, UInt<3>),
G(T),
H(T, UInt<1>),
} }
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] #[hdl(outline_generated)]
#[hdl(outline_generated, static, where(T: StaticValue))] pub struct S2<T = ()> {
pub struct S2<T> {
pub v: E<T>, pub v: E<T>,
} }

File diff suppressed because it is too large Load diff