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

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 8060ca84de
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
21 changed files with 7494 additions and 7304 deletions

View file

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

View file

@ -0,0 +1,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)
}

View file

@ -0,0 +1,656 @@
use crate::{
hdl_type_common::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
ParsedType, SplitForImpl, TypesParser, WrappedInConst,
},
Errors, HdlAttr, PairsIterExt,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens};
use syn::{
parse_quote_spanned,
punctuated::{Pair, Punctuated},
token::{Brace, Paren},
Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident,
ItemEnum, ItemStruct, Token, Type, Variant, Visibility,
};
crate::options! {
#[options = VariantOptions]
pub(crate) enum VariantOption {}
}
crate::options! {
#[options = FieldOptions]
pub(crate) enum FieldOption {}
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedVariantField {
pub(crate) paren_token: Paren,
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<FieldOptions>,
pub(crate) ty: MaybeParsed<ParsedType, Type>,
pub(crate) comma_token: Option<Token![,]>,
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedVariant {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<VariantOptions>,
pub(crate) ident: Ident,
pub(crate) field: Option<ParsedVariantField>,
}
impl ParsedVariant {
fn parse(
errors: &mut Errors,
variant: Variant,
generics: &MaybeParsed<ParsedGenerics, Generics>,
) -> Self {
let Variant {
mut attrs,
ident,
fields,
discriminant,
} = variant;
let options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
let field = match fields {
Fields::Unnamed(FieldsUnnamed {
paren_token,
unnamed,
}) if unnamed.len() == 1 => {
let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple();
let Field {
mut attrs,
vis,
mutability,
ident: _,
colon_token: _,
ty,
} = field;
let options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
if !matches!(vis, Visibility::Inherited) {
errors.error(
&vis,
"enum variant fields must not have a visibility specifier",
);
}
if !matches!(mutability, FieldMutability::None) {
// FIXME: use mutability as the spanned tokens,
// blocked on https://github.com/dtolnay/syn/issues/1717
errors.error(&ty, "field mutability is not supported");
}
Some(ParsedVariantField {
paren_token,
attrs,
options,
ty: TypesParser::maybe_run(generics.as_ref(), ty, errors),
comma_token,
})
}
Fields::Unit => None,
Fields::Unnamed(fields) if fields.unnamed.is_empty() => None,
Fields::Named(fields) if fields.named.is_empty() => None,
Fields::Unnamed(_) | Fields::Named(_) => {
errors.error(
fields,
"enum variant must either have no fields or a single parenthesized field",
);
None
}
};
if let Some((eq, _)) = discriminant {
errors.error(eq, "custom enum discriminants are not allowed");
}
Self {
attrs,
options,
ident,
field,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct ParsedEnum {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>,
pub(crate) vis: Visibility,
pub(crate) enum_token: Token![enum],
pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) brace_token: Brace,
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
pub(crate) match_variant_ident: Ident,
}
impl ParsedEnum {
fn parse(item: ItemEnum) -> syn::Result<Self> {
let ItemEnum {
mut attrs,
vis,
enum_token,
ident,
mut generics,
brace_token,
variants,
} = item;
let mut errors = Errors::new();
let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
errors.ok(options.body.validate());
let ItemOptions {
outline_generated: _,
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)
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1290,7 +1290,10 @@ impl Visitor {
memory, memory,
paren, paren,
ty_expr, ty_expr,
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())), } => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
),
MemoryFn::MemoryArray { MemoryFn::MemoryArray {
memory_array, memory_array,
paren, paren,

View file

@ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
Err(err) => err.into_compile_error().into(), Err(err) => err.into_compile_error().into(),
} }
} }
// intentionally not documented here, see `fayalite::hdl` for docs
#[proc_macro_attribute]
pub fn hdl(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) {
Ok(retval) => retval.into(),
Err(err) => err.into_compile_error().into(),
}
}

View file

@ -1,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

View file

@ -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(outline_generated)]
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()
}
}

View file

@ -1,190 +1,155 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{ use crate::{
bundle::{BundleValue, TypeHintTrait},
expr::{ops::VariantAccess, Expr, ToExpr}, expr::{ops::VariantAccess, Expr, ToExpr},
int::{UInt, UIntType}, hdl,
intern::{Intern, Interned, MemoizeGeneric}, int::Bool,
intern::{Intern, Interned},
module::{ module::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
NormalModule, Scope, NormalModule, Scope,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties},
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
},
}; };
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{ use std::iter::FusedIterator;
borrow::Cow,
fmt,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantType<T> { pub struct EnumVariant {
pub name: Interned<str>, pub name: Interned<str>,
pub ty: Option<T>, pub ty: Option<CanonicalType>,
}
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let VariantType { name, ref ty } = *self.0;
if let Some(ty) = ty {
write!(f, "{name}({ty:?})")
} else {
write!(f, "{name}")
}
}
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> VariantType<T> {
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType { name, ty: f(ty) }
}
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType {
name,
ty: ty.map(f),
}
}
pub fn as_ref_ty(&self) -> VariantType<&T> {
VariantType {
name: self.name,
ty: self.ty.as_ref(),
}
}
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
FmtDebugInEnum(self)
}
}
impl<T: Type> VariantType<T> {
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
self.as_ref_ty().map_ty(T::canonical)
}
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
self.as_ref_ty().map_ty(T::to_dyn)
}
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.as_ref_ty().map_ty(T::canonical_dyn)
}
}
impl VariantType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
let Some(ty) = self.ty else {
panic!("variant {expected_name} has no value but a value is expected");
};
T::from_dyn_canonical_type(ty)
}
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
assert!(
self.ty.is_none(),
"variant {expected_name} has a value but is expected to have no value"
);
}
} }
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
struct DynEnumTypeImpl { struct EnumImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, variants: Interned<[EnumVariant]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize, type_properties: TypeProperties,
is_storable: bool,
is_castable_from_bits: bool,
} }
impl fmt::Debug for DynEnumTypeImpl { impl std::fmt::Debug for EnumImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DynEnumType ")?; f.debug_struct("Enum")
f.debug_set() .field("variants", &self.variants)
.entries( .field("name_indexes", &self.name_indexes)
self.variants .field("type_properties", &self.type_properties)
.iter()
.map(|variant| variant.fmt_debug_in_enum()),
)
.finish() .finish()
} }
} }
impl PartialEq for DynEnumTypeImpl { impl std::hash::Hash for EnumImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.variants.hash(state);
}
}
impl PartialEq for EnumImpl {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.variants == other.variants self.variants == other.variants
} }
} }
impl Hash for DynEnumTypeImpl { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
fn hash<H: Hasher>(&self, state: &mut H) { pub struct Enum(Interned<EnumImpl>);
self.variants.hash(state);
const fn discriminant_bit_width_impl(variant_count: usize) -> usize {
match variant_count.next_power_of_two().checked_ilog2() {
Some(x) => x as usize,
None => 0,
} }
} }
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Clone)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>); pub struct EnumTypePropertiesBuilder {
type_properties: TypeProperties,
impl fmt::Debug for DynEnumType { variant_count: usize,
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
} }
fn discriminant_bit_width_impl(variant_count: usize) -> usize { impl EnumTypePropertiesBuilder {
variant_count #[must_use]
.next_power_of_two() pub const fn new() -> Self {
.checked_ilog2() Self {
.unwrap_or(0) as usize type_properties: TypeProperties {
} is_passive: true,
is_storable: true,
impl DynEnumType { is_castable_from_bits: true,
#[track_caller] bit_width: 0,
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { },
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); variant_count: 0,
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize;
let mut is_storable = true;
let mut is_castable_from_bits = true;
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
}
if let Some(ty) = ty {
assert!(
ty.is_passive(),
"variant type must be a passive type: {ty:?}"
);
body_bit_width = body_bit_width.max(ty.bit_width());
is_storable &= ty.is_storable();
is_castable_from_bits &= ty.is_castable_from_bits();
} }
} }
let bit_width = body_bit_width pub const fn clone(&self) -> Self {
.checked_add(discriminant_bit_width_impl(variants.len())) Self { ..*self }
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); }
Self( #[must_use]
DynEnumTypeImpl { pub const fn variant(self, field_props: Option<TypeProperties>) -> Self {
variants, let Self {
name_indexes, mut type_properties,
bit_width, variant_count,
} = self;
if let Some(TypeProperties {
is_passive,
is_storable, is_storable,
is_castable_from_bits, is_castable_from_bits,
bit_width,
}) = field_props
{
assert!(is_passive, "variant type must be a passive type");
type_properties = TypeProperties {
is_passive: true,
is_storable: type_properties.is_storable & is_storable,
is_castable_from_bits: type_properties.is_castable_from_bits
& is_castable_from_bits,
bit_width: if type_properties.bit_width < bit_width {
bit_width
} else {
type_properties.bit_width
},
};
}
Self {
type_properties,
variant_count: variant_count + 1,
}
}
pub const fn finish(self) -> TypeProperties {
assert!(
self.variant_count != 0,
"zero-variant enums aren't yet supported: \
https://github.com/chipsalliance/firrtl-spec/issues/208",
);
let Some(bit_width) = self
.type_properties
.bit_width
.checked_add(discriminant_bit_width_impl(self.variant_count))
else {
panic!("enum is too big: bit-width overflowed");
};
TypeProperties {
bit_width,
..self.type_properties
}
}
}
impl Enum {
#[track_caller]
pub fn new(variants: Interned<[EnumVariant]>) -> Self {
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut type_props_builder = EnumTypePropertiesBuilder::new();
for (index, EnumVariant { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(*name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
}
type_props_builder = type_props_builder.variant(ty.map(CanonicalType::type_properties));
}
Self(
EnumImpl {
variants,
name_indexes,
type_properties: type_props_builder.finish(),
} }
.intern_sized(), .intern_sized(),
) )
@ -192,233 +157,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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -12,7 +12,6 @@ extern crate self as fayalite;
pub use std as __std; pub use std as __std;
#[doc(inline)] #[doc(inline)]
#[doc(alias = "hdl")]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called. /// a [`Module`][`::fayalite::module::Module`] when called.
/// In the function body it will implicitly create a /// In the function body it will implicitly create a
@ -21,25 +20,86 @@ 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;
pub mod annotations; // FIXME: finish
//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::{
enum_::{EnumMatchVariantsIter, EnumType},
expr::{ops::VariantAccess, Expr},
intern::Interned,
source_location::SourceLocation,
};
use std::marker::PhantomData;
pub struct NormalModule;
pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>);
impl ModuleBuilder<NormalModule> {
pub fn enum_match_variants_helper<Enum: EnumType>(
&mut self,
_base: Expr<Enum>,
_source_location: SourceLocation,
) -> EnumMatchVariantsIter<Enum> {
todo!()
}
}
pub struct Scope(());
pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> {
variant_access: Interned<VariantAccess>,
_phantom: PhantomData<T>,
}
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> {
pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) {
(self.variant_access, Scope(()))
}
pub(crate) fn variant_access(&self) -> Interned<VariantAccess> {
self.variant_access
}
}
#[derive(Clone)]
pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>);
impl<T: EnumType> EnumMatchVariantsIterImpl<T> {
pub(crate) fn for_variant_index(
&self,
_variant_index: usize,
) -> EnumMatchVariantAndInactiveScopeImpl<T> {
todo!()
}
}
}

View file

@ -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

View file

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

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{fmt::Debug, hash::Hash};
mod sealed {
pub trait Sealed {}
}
/// the only implementation is `ConstUsize<Self::VALUE>`
pub trait GenericConstUsize:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: usize;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstUsize<const VALUE: usize>;
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
}
}
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
const VALUE: usize = VALUE;
}

View file

@ -1,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>,
} }