WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 13s
All checks were successful
/ test (push) Successful in 13s
This commit is contained in:
parent
cd99dbc849
commit
a9b5c1c5c3
|
@ -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
|
||||||
|
|
841
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
841
crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
Normal file
|
@ -0,0 +1,841 @@
|
||||||
|
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: ::fayalite::expr::Expr<#ty>,
|
||||||
|
) -> #filled_ty {
|
||||||
|
let Self {
|
||||||
|
#phantom_field_name: _,
|
||||||
|
#(#pat_fields)*
|
||||||
|
} = self;
|
||||||
|
#ident {
|
||||||
|
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||||
|
#(#field_idents,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled);
|
||||||
|
let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||||
|
let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl();
|
||||||
|
quote_spanned! {self.ident.span()=>
|
||||||
|
#[automatically_derived]
|
||||||
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
|
impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty
|
||||||
|
#unfilled_where_clause
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
#ident {
|
||||||
|
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||||
|
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled);
|
||||||
|
let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||||
|
let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl();
|
||||||
|
let type_generics = self.generics.split_for_impl().1;
|
||||||
|
quote_spanned! {self.ident.span()=>
|
||||||
|
#[automatically_derived]
|
||||||
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
|
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
||||||
|
#filled_where_clause
|
||||||
|
{
|
||||||
|
type Type = #target #type_generics;
|
||||||
|
fn to_expr(
|
||||||
|
&self,
|
||||||
|
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
|
||||||
|
let __ty = #target {
|
||||||
|
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
|
||||||
|
};
|
||||||
|
let __field_values = [
|
||||||
|
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
||||||
|
];
|
||||||
|
::fayalite::expr::ToExpr::to_expr(
|
||||||
|
&::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for ParsedBundle {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let Self {
|
||||||
|
attrs,
|
||||||
|
options,
|
||||||
|
vis,
|
||||||
|
struct_token,
|
||||||
|
ident,
|
||||||
|
generics,
|
||||||
|
fields,
|
||||||
|
field_flips,
|
||||||
|
mask_type_ident,
|
||||||
|
mask_type_match_variant_ident,
|
||||||
|
match_variant_ident,
|
||||||
|
builder_ident,
|
||||||
|
mask_type_builder_ident,
|
||||||
|
} = self;
|
||||||
|
let 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 MaskType = #mask_type_ident #type_generics;
|
||||||
|
type MatchVariant = #mask_type_match_variant_ident #type_generics;
|
||||||
|
type MatchActiveScope = ();
|
||||||
|
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||||
|
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||||
|
>;
|
||||||
|
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||||
|
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||||
|
>;
|
||||||
|
fn match_variants(
|
||||||
|
__this: ::fayalite::expr::Expr<Self>,
|
||||||
|
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||||
|
::fayalite::module::NormalModule,
|
||||||
|
>,
|
||||||
|
__source_location: ::fayalite::source_location::SourceLocation,
|
||||||
|
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||||
|
let __retval = #mask_type_match_variant_ident {
|
||||||
|
#(#match_variant_body_fields)*
|
||||||
|
};
|
||||||
|
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||||
|
}
|
||||||
|
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
|
||||||
|
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||||
|
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||||
|
::fayalite::__std::panic!("expected bundle");
|
||||||
|
};
|
||||||
|
let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||||
|
::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
|
||||||
|
Self {
|
||||||
|
#(#from_canonical_body_fields)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||||
|
::fayalite::source_location::SourceLocation::caller()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
type Builder = #unfilled_mask_type_builder_ty;
|
||||||
|
type FilledBuilder = #filled_mask_type_builder_ty;
|
||||||
|
fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||||
|
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||||
|
let __this = *__this;
|
||||||
|
let __retval = #mask_type_match_variant_ident {
|
||||||
|
#(#match_variant_body_fields)*
|
||||||
|
};
|
||||||
|
::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
type MaskType = #mask_type_ident #type_generics;
|
||||||
|
type MatchVariant = #match_variant_ident #type_generics;
|
||||||
|
type MatchActiveScope = ();
|
||||||
|
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||||
|
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||||
|
>;
|
||||||
|
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||||
|
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||||
|
>;
|
||||||
|
fn match_variants(
|
||||||
|
__this: ::fayalite::expr::Expr<Self>,
|
||||||
|
__module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||||
|
::fayalite::module::NormalModule,
|
||||||
|
>,
|
||||||
|
__source_location: ::fayalite::source_location::SourceLocation,
|
||||||
|
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||||
|
let __retval = #match_variant_ident {
|
||||||
|
#(#match_variant_body_fields)*
|
||||||
|
};
|
||||||
|
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||||
|
}
|
||||||
|
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||||
|
#mask_type_ident {
|
||||||
|
#(#mask_type_body_fields)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
|
||||||
|
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||||
|
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||||
|
::fayalite::__std::panic!("expected bundle");
|
||||||
|
};
|
||||||
|
let __fields = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||||
|
::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields");
|
||||||
|
Self {
|
||||||
|
#(#from_canonical_body_fields)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||||
|
::fayalite::source_location::SourceLocation::caller()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
type Builder = #unfilled_builder_ty;
|
||||||
|
type FilledBuilder = #filled_builder_ty;
|
||||||
|
fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||||
|
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||||
|
let __this = *__this;
|
||||||
|
let __retval = #match_variant_ident {
|
||||||
|
#(#match_variant_body_fields)*
|
||||||
|
};
|
||||||
|
::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
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)
|
||||||
|
}
|
656
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
656
crates/fayalite-proc-macros-impl/src/hdl_enum.rs
Normal file
|
@ -0,0 +1,656 @@
|
||||||
|
use crate::{
|
||||||
|
hdl_type_common::{
|
||||||
|
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
|
||||||
|
ParsedType, SplitForImpl, TypesParser, WrappedInConst,
|
||||||
|
},
|
||||||
|
Errors, HdlAttr, PairsIterExt,
|
||||||
|
};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote_spanned, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
parse_quote_spanned,
|
||||||
|
punctuated::{Pair, Punctuated},
|
||||||
|
token::{Brace, Paren},
|
||||||
|
Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident,
|
||||||
|
ItemEnum, ItemStruct, Token, Type, Variant, Visibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::options! {
|
||||||
|
#[options = VariantOptions]
|
||||||
|
pub(crate) enum VariantOption {}
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::options! {
|
||||||
|
#[options = FieldOptions]
|
||||||
|
pub(crate) enum FieldOption {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ParsedVariantField {
|
||||||
|
pub(crate) paren_token: Paren,
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) options: HdlAttr<FieldOptions>,
|
||||||
|
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||||
|
pub(crate) comma_token: Option<Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ParsedVariant {
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) options: HdlAttr<VariantOptions>,
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) field: Option<ParsedVariantField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsedVariant {
|
||||||
|
fn parse(
|
||||||
|
errors: &mut Errors,
|
||||||
|
variant: Variant,
|
||||||
|
generics: &MaybeParsed<ParsedGenerics, Generics>,
|
||||||
|
) -> Self {
|
||||||
|
let Variant {
|
||||||
|
mut attrs,
|
||||||
|
ident,
|
||||||
|
fields,
|
||||||
|
discriminant,
|
||||||
|
} = variant;
|
||||||
|
let options = errors
|
||||||
|
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let field = match fields {
|
||||||
|
Fields::Unnamed(FieldsUnnamed {
|
||||||
|
paren_token,
|
||||||
|
unnamed,
|
||||||
|
}) if unnamed.len() == 1 => {
|
||||||
|
let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple();
|
||||||
|
let Field {
|
||||||
|
mut attrs,
|
||||||
|
vis,
|
||||||
|
mutability,
|
||||||
|
ident: _,
|
||||||
|
colon_token: _,
|
||||||
|
ty,
|
||||||
|
} = field;
|
||||||
|
let options = errors
|
||||||
|
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||||
|
.unwrap_or_default();
|
||||||
|
if !matches!(vis, Visibility::Inherited) {
|
||||||
|
errors.error(
|
||||||
|
&vis,
|
||||||
|
"enum variant fields must not have a visibility specifier",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !matches!(mutability, FieldMutability::None) {
|
||||||
|
// FIXME: use mutability as the spanned tokens,
|
||||||
|
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||||
|
errors.error(&ty, "field mutability is not supported");
|
||||||
|
}
|
||||||
|
Some(ParsedVariantField {
|
||||||
|
paren_token,
|
||||||
|
attrs,
|
||||||
|
options,
|
||||||
|
ty: TypesParser::maybe_run(generics.as_ref(), ty, errors),
|
||||||
|
comma_token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Fields::Unit => None,
|
||||||
|
Fields::Unnamed(fields) if fields.unnamed.is_empty() => None,
|
||||||
|
Fields::Named(fields) if fields.named.is_empty() => None,
|
||||||
|
Fields::Unnamed(_) | Fields::Named(_) => {
|
||||||
|
errors.error(
|
||||||
|
fields,
|
||||||
|
"enum variant must either have no fields or a single parenthesized field",
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some((eq, _)) = discriminant {
|
||||||
|
errors.error(eq, "custom enum discriminants are not allowed");
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
attrs,
|
||||||
|
options,
|
||||||
|
ident,
|
||||||
|
field,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct ParsedEnum {
|
||||||
|
pub(crate) attrs: Vec<Attribute>,
|
||||||
|
pub(crate) options: HdlAttr<ItemOptions>,
|
||||||
|
pub(crate) vis: Visibility,
|
||||||
|
pub(crate) enum_token: Token![enum],
|
||||||
|
pub(crate) ident: Ident,
|
||||||
|
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||||
|
pub(crate) brace_token: Brace,
|
||||||
|
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
|
||||||
|
pub(crate) match_variant_ident: Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsedEnum {
|
||||||
|
fn parse(item: ItemEnum) -> syn::Result<Self> {
|
||||||
|
let ItemEnum {
|
||||||
|
mut attrs,
|
||||||
|
vis,
|
||||||
|
enum_token,
|
||||||
|
ident,
|
||||||
|
mut generics,
|
||||||
|
brace_token,
|
||||||
|
variants,
|
||||||
|
} = item;
|
||||||
|
let mut errors = Errors::new();
|
||||||
|
let mut options = errors
|
||||||
|
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
|
||||||
|
.unwrap_or_default();
|
||||||
|
errors.ok(options.body.validate());
|
||||||
|
let ItemOptions {
|
||||||
|
outline_generated: _,
|
||||||
|
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(
|
||||||
|
self,
|
||||||
|
#index,
|
||||||
|
::fayalite::__std::option::Option::Some(
|
||||||
|
::fayalite::expr::Expr::canonical(
|
||||||
|
::fayalite::expr::ToExpr::to_expr(&v),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics #target #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
#[allow(non_snake_case, dead_code)]
|
||||||
|
#vis fn #ident(self) -> ::fayalite::expr::Expr<Self> {
|
||||||
|
::fayalite::expr::ToExpr::to_expr(
|
||||||
|
&::fayalite::expr::ops::EnumLiteral::new(
|
||||||
|
self,
|
||||||
|
#index,
|
||||||
|
::fayalite::__std::option::Option::None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map(
|
||||||
|
|(index, ParsedVariant { ident, field, .. })| {
|
||||||
|
let ident_str = ident.to_string();
|
||||||
|
let val = if field.is_some() {
|
||||||
|
let missing_value_msg = format!("expected variant {ident} to have a field");
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
::fayalite::__std::assert!(ty.is_none());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
#ident: {
|
||||||
|
let ::fayalite::enum_::EnumVariant {
|
||||||
|
name,
|
||||||
|
ty,
|
||||||
|
} = variants[#index];
|
||||||
|
::fayalite::__std::assert_eq!(&*name, #ident_str);
|
||||||
|
#val
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map(
|
||||||
|
|(index, ParsedVariant { ident, field, .. })| {
|
||||||
|
if field.is_some() {
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
#index => #match_variant_ident::#ident(
|
||||||
|
::fayalite::expr::ToExpr::to_expr(
|
||||||
|
&::fayalite::expr::ops::VariantAccess::new_unchecked(
|
||||||
|
variant_access.base(),
|
||||||
|
variant_access.variant_index(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {ident.span()=>
|
||||||
|
#index => #match_variant_ident::#ident,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
let variants_body_variants = Vec::from_iter(variants.iter().map(
|
||||||
|
|ParsedVariant {
|
||||||
|
attrs: _,
|
||||||
|
options,
|
||||||
|
ident,
|
||||||
|
field,
|
||||||
|
}| {
|
||||||
|
let VariantOptions {} = options.body;
|
||||||
|
let ident_str = ident.to_string();
|
||||||
|
match field {
|
||||||
|
Some(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 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)
|
||||||
|
}
|
2981
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
2981
crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -1,671 +1,207 @@
|
||||||
// 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,
|
module::{ModuleBuilder, NormalModule},
|
||||||
},
|
|
||||||
intern::{Intern, Interned, InternedCompare, Memoize},
|
|
||||||
module::{
|
|
||||||
transform::visit::{Fold, Folder, Visit, Visitor},
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
|
|
||||||
where
|
|
||||||
VA::ElementType: Fold<State>,
|
|
||||||
{
|
|
||||||
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
|
|
||||||
state.fold_array_type(self)
|
|
||||||
}
|
}
|
||||||
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
|
pub fn element(&self) -> T {
|
||||||
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
|
*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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
|
impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
|
||||||
where
|
pub fn new_static(element: T) -> Self {
|
||||||
VA::ElementType: Visit<State>,
|
Self::new(element, Len::SizeType::default())
|
||||||
{
|
|
||||||
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
|
|
||||||
state.visit_array_type(self)
|
|
||||||
}
|
|
||||||
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
|
|
||||||
self.element.visit(state)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
|
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
||||||
pub fn new_array(element: V::Type) -> Self {
|
const TYPE: Self = Self {
|
||||||
ArrayType::new_with_len_type(element, ())
|
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<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
|
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
fn static_type() -> Self {
|
type MaskType = ArrayType<T::MaskType, Len>;
|
||||||
Self::new_array(StaticType::static_type())
|
type MatchVariant = Len::ArrayMatch<T>;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_unchecked(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_unchecked(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
|
@ -2,111 +2,57 @@
|
||||||
// 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 MaskType = Bool;
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticType for ClockType {
|
|
||||||
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 +83,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 +94,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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,31 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct DynEnum {
|
|
||||||
ty: DynEnumType,
|
|
||||||
variant_index: usize,
|
|
||||||
variant_value: Option<DynCanonicalValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynEnum {
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new_by_index(
|
|
||||||
ty: DynEnumType,
|
|
||||||
variant_index: usize,
|
|
||||||
variant_value: Option<DynCanonicalValue>,
|
|
||||||
) -> Self {
|
|
||||||
let variant = ty.variants()[variant_index];
|
|
||||||
assert_eq!(
|
|
||||||
variant_value.as_ref().map(|v| v.ty()),
|
|
||||||
variant.ty,
|
|
||||||
"variant value doesn't match type"
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
ty,
|
|
||||||
variant_index,
|
|
||||||
variant_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[track_caller]
|
|
||||||
pub fn new_by_name(
|
|
||||||
ty: DynEnumType,
|
|
||||||
variant_name: Interned<str>,
|
|
||||||
variant_value: Option<DynCanonicalValue>,
|
|
||||||
) -> Self {
|
|
||||||
let variant_index = ty.name_indexes()[&variant_name];
|
|
||||||
Self::new_by_index(ty, variant_index, variant_value)
|
|
||||||
}
|
|
||||||
pub fn variant_index(&self) -> usize {
|
|
||||||
self.variant_index
|
|
||||||
}
|
|
||||||
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
|
|
||||||
&self.variant_value
|
|
||||||
}
|
|
||||||
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
|
|
||||||
self.ty.variants()[self.variant_index]
|
|
||||||
}
|
|
||||||
pub fn variant_name(&self) -> Interned<str> {
|
|
||||||
self.variant_with_type().name
|
|
||||||
}
|
|
||||||
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
|
|
||||||
self.variant_with_type().ty
|
|
||||||
}
|
|
||||||
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
|
|
||||||
self.variant_with_type()
|
|
||||||
.map_opt_ty(|_| self.variant_value.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
|
||||||
pub struct VariantsHint {
|
|
||||||
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
|
|
||||||
pub more_variants: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VariantsHint {
|
|
||||||
pub fn new(
|
|
||||||
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
|
|
||||||
more_variants: bool,
|
|
||||||
) -> Self {
|
|
||||||
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
|
|
||||||
Self {
|
|
||||||
known_variants,
|
|
||||||
more_variants,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn check_variant(
|
|
||||||
self,
|
|
||||||
index: usize,
|
|
||||||
variant: VariantType<&dyn DynType>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let Some(&known_variant) = self.known_variants.get(index) else {
|
|
||||||
return if self.more_variants {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!(
|
|
||||||
"too many variants: name={:?} index={index}",
|
|
||||||
variant.name
|
|
||||||
))
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let VariantType {
|
|
||||||
name: known_name,
|
|
||||||
ty: type_hint,
|
|
||||||
} = known_variant;
|
|
||||||
let VariantType { name, ty } = variant;
|
|
||||||
if name != known_name {
|
|
||||||
Err(format!(
|
|
||||||
"wrong variant name {name:?}, expected {known_name:?}"
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
match (ty, type_hint) {
|
|
||||||
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
|
|
||||||
(None, None) => Ok(()),
|
|
||||||
(None, Some(_)) => Err(format!(
|
|
||||||
"expected variant {name:?} to have type, no type provided"
|
|
||||||
)),
|
|
||||||
(Some(_), None) => Err(format!(
|
|
||||||
"expected variant {name:?} to have no type, but a type was provided"
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EnumType:
|
pub trait EnumType:
|
||||||
Type<
|
Type<
|
||||||
CanonicalType = DynEnumType,
|
MaskType = Bool,
|
||||||
CanonicalValue = DynEnum,
|
|
||||||
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 +190,22 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
|
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> {
|
||||||
where
|
pub fn variant_access(&self) -> Interned<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) -> (Interned<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 +219,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 +235,55 @@ 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 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(outline_generated)]
|
||||||
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
398
crates/fayalite/src/expr/target.rs
Normal file
398
crates/fayalite/src/expr/target.rs
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -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>
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,25 +20,140 @@ 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;
|
||||||
pub mod module;
|
//pub mod module;
|
||||||
pub mod reg;
|
pub mod reg;
|
||||||
pub mod reset;
|
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;
|
||||||
|
|
||||||
|
// FIXME: switch to real module mod
|
||||||
|
pub mod module {
|
||||||
|
use crate::{
|
||||||
|
bundle::BundleType,
|
||||||
|
enum_::{EnumMatchVariantsIter, EnumType},
|
||||||
|
expr::{ops::VariantAccess, Expr, Flow},
|
||||||
|
intern::Interned,
|
||||||
|
memory::PortName,
|
||||||
|
source_location::SourceLocation,
|
||||||
|
ty::Type,
|
||||||
|
};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct NormalModule;
|
||||||
|
|
||||||
|
pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>);
|
||||||
|
|
||||||
|
impl ModuleBuilder<NormalModule> {
|
||||||
|
pub fn enum_match_variants_helper<Enum: EnumType>(
|
||||||
|
&mut self,
|
||||||
|
_base: Expr<Enum>,
|
||||||
|
_source_location: SourceLocation,
|
||||||
|
) -> EnumMatchVariantsIter<Enum> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Scope(());
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
pub struct NameId(pub Interned<str>, pub usize);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
pub struct ScopedNameId(pub NameId, pub NameId);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
pub struct TargetName(pub ScopedNameId, pub Option<PortName>);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
pub struct Instance<T: BundleType>(T);
|
||||||
|
|
||||||
|
impl<T: BundleType> Instance<T> {
|
||||||
|
pub fn scoped_name(&self) -> ScopedNameId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn ty(&self) -> T {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
pub fn must_connect_to(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn flow(&self) -> Flow {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn source_location(&self) -> SourceLocation {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
pub struct ModuleIO<T: Type>(T);
|
||||||
|
|
||||||
|
impl<T: Type> ModuleIO<T> {
|
||||||
|
pub fn scoped_name(&self) -> ScopedNameId {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn ty(&self) -> T {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
pub fn must_connect_to(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn flow(&self) -> Flow {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn source_location(&self) -> SourceLocation {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> {
|
||||||
|
variant_access: Interned<VariantAccess>,
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> {
|
||||||
|
pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) {
|
||||||
|
(self.variant_access, Scope(()))
|
||||||
|
}
|
||||||
|
pub(crate) fn variant_access(&self) -> Interned<VariantAccess> {
|
||||||
|
self.variant_access
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: EnumType> EnumMatchVariantsIterImpl<T> {
|
||||||
|
pub(crate) fn for_variant_index(
|
||||||
|
&self,
|
||||||
|
_variant_index: usize,
|
||||||
|
) -> EnumMatchVariantAndInactiveScopeImpl<T> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -2,358 +2,115 @@
|
||||||
// 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 MaskType = Bool;
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticType for AsyncResetType {
|
|
||||||
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
|
@ -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::{
|
||||||
|
|
29
crates/fayalite/src/util/const_usize.rs
Normal file
29
crates/fayalite/src/util/const_usize.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
use std::{fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
|
mod sealed {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the only implementation is `ConstUsize<Self::VALUE>`
|
||||||
|
pub trait GenericConstUsize:
|
||||||
|
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
|
||||||
|
{
|
||||||
|
const VALUE: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
pub struct ConstUsize<const VALUE: usize>;
|
||||||
|
|
||||||
|
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
|
||||||
|
|
||||||
|
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
|
||||||
|
const VALUE: usize = VALUE;
|
||||||
|
}
|
|
@ -1,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,
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue