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

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 46d7755ba2
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
18 changed files with 6874 additions and 6891 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,793 @@
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();
quote_spanned! {ident.span()=>
#ident: ::fayalite::ty::StaticType::static_type(),
}
}));
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
#static_where_clause
{
fn static_type() -> Self {
::fayalite::ty::Type::mask_type(
&<#target #static_type_generics as ::fayalite::ty::StaticType>::static_type(),
)
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
#static_where_clause
{
fn static_type() -> Self {
Self {
#(#static_type_body_fields)*
}
}
}
}
.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,630 @@
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::static_type(),
}
} else {
quote_spanned! {ident.span()=>
#ident: (),
}
}
}));
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
#static_where_clause
{
fn static_type() -> Self {
Self {
#(#static_type_body_variants)*
}
}
}
}
.to_tokens(tokens);
}
}
}
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
let item = ParsedEnum::parse(item)?;
let outline_generated = item.options.body.outline_generated;
let mut contents = item.to_token_stream();
if outline_generated.is_some() {
contents = crate::outline_generated(contents, "hdl-enum-");
}
Ok(contents)
}

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,)*
}
}
}
}; };
} }
@ -728,3 +879,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,198 @@
// 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},
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::{
any::Any,
borrow::{Borrow, BorrowMut},
fmt,
hash::Hash,
marker::PhantomData,
ops::IndexMut,
sync::Arc,
}; };
use std::ops::Index;
mod sealed { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub trait Sealed {} struct ArrayImpl<T: Type, Len: Size> {
element: T,
len: Len::SizeType,
type_properties: TypeProperties,
} }
pub trait ValueArrayOrSlice: #[derive(Copy, Clone, PartialEq, Eq, Hash)]
sealed::Sealed pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]> impl_: Interned<ArrayImpl<T, Len>>,
+ 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<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<V: Value> ValueArrayOrSlice for [V] let ArrayImpl {
where element, len: _, ..
V::Type: Type<Value = V>, } = *self.impl_;
{ write!(f, "Array<{element:?}, {}>", self.len())
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] {} pub type Array<
T = CanonicalType,
const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
> = ArrayType<T, ConstUsize<LEN>>;
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N] #[allow(non_upper_case_globals)]
where pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
V::Type: Type<Value = V>, #[allow(non_upper_case_globals)]
{ pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;
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 { impl<T: Type, Len: Size> ArrayType<T, Len> {
std::array::from_fn(|index| { pub fn new(element: T, len: Len::SizeType) -> Self {
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr() let element_props = element.canonical().type_properties();
}) let type_properties = TypeProperties {
} is_passive: element_props.is_passive,
is_storable: element_props.is_storable,
fn len_from_len_type(_v: Self::LenType) -> usize { is_castable_from_bits: element_props.is_castable_from_bits,
N bit_width: element_props
} .bit_width
.checked_mul(Len::as_usize(len))
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { .expect("array too big"),
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 { Self {
element, impl_: ArrayImpl {
len, element,
bit_width, len,
type_properties,
}
.intern_sized(),
} }
} }
} pub fn element(&self) -> &T {
&self.impl_.element
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 len(self) -> usize {
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) Len::as_usize(self.impl_.len)
}
pub fn type_properties(self) -> TypeProperties {
self.impl_.type_properties
}
pub fn as_dyn_array(self) -> Array {
Array::new_dyn(self.element().canonical(), self.len())
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> 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 {
ArrayType::new_with_len_type(element, ())
}
}
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
fn static_type() -> Self { fn static_type() -> Self {
Self::new_array(StaticType::static_type()) Self::new_static(T::static_type())
} }
} }
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> { impl<T: Type> Array<T> {
pub fn new_slice(element: V::Type, len: usize) -> Self { pub fn new_dyn(element: T, len: usize) -> Self {
ArrayType::new_with_len_type(element, len) Self::new(element, len)
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> { impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type CanonicalType = ArrayType<[DynCanonicalValue]>; type MaskType = ArrayType<T::MaskType, Len>;
type Value = Array<VA>; type MatchVariant = Len::ArrayMatch<T>;
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.impl_.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 { impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
TypeEnum::ArrayType(self.canonical()) fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
} let base = Expr::as_dyn_array(*this);
let base_ty = Expr::ty(base);
fn from_canonical_type(t: Self::CanonicalType) -> Self { let retval = Vec::from_iter(
Self { (0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()),
element: VA::ElementType::from_dyn_canonical_type(t.element), );
len: VA::try_len_type_from_len(t.len).expect("length should match"), Interned::<_>::into_inner(Intern::intern_sized(
bit_width: t.bit_width, Len::ArrayMatch::<T>::try_from(retval)
} .ok()
} .expect("unreachable"),
))
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
this,
)?)
} }
} }
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
for ArrayType<Lhs> pub struct ArrayWithoutGenerics;
{
}
impl CanonicalType for ArrayType<[DynCanonicalValue]> { impl<T: Type> Index<T> for ArrayWithoutGenerics {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; type Output = ArrayWithoutLen<T>;
}
#[derive(Debug, PartialEq, Eq, Hash)] fn index(&self, element: T) -> &Self::Output {
pub struct Array<VA: ValueArrayOrSlice + ?Sized> { Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
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> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
type Type = ArrayType<VA>; pub struct ArrayWithoutLen<T: Type> {
element: T,
}
fn ty(&self) -> Self::Type { impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) type Output = Array<T>;
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { fn index(&self, len: usize) -> &Self::Output {
Expr::from_value(self) Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> { impl<T: Type, const LEN: usize> Index<ConstUsize<LEN>> for ArrayWithoutLen<T>
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())
} fn index(&self, len: ConstUsize<LEN>) -> &Self::Output {
} Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len)))
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
<[E]>::ty(self)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
<[E]>::to_expr(self)
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
type Type = ArrayType<[T::Value; N]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), ())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
#[derive(Clone, Debug)]
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
array: Arc<VA>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
type Item = VA::Element;
fn next(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next()?].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next_back()?].clone())
}
}
impl<VA: ValueArrayOrSlice> Array<VA> {
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
self.value.iter()
}
}
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
type Item = &'a VA::Element;
type IntoIter = std::slice::Iter<'a, VA::Element>;
fn into_iter(self) -> Self::IntoIter {
self.value.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
type Item = VA::Element;
type IntoIter = ArrayIntoIter<VA>;
fn into_iter(self) -> Self::IntoIter {
ArrayIntoIter {
indexes: 0..self.len(),
array: self.value,
}
}
}
#[derive(Clone, Debug)]
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
array: Expr<Array<VA>>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
type Item = Expr<VA::Element>;
fn next(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
pub fn len(self) -> usize {
self.canonical_type().len()
}
pub fn is_empty(self) -> bool {
self.canonical_type().is_empty()
}
pub fn iter(self) -> ArrayExprIter<VA> {
ArrayExprIter {
indexes: 0..self.len(),
array: self,
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,150 +1,58 @@
// 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);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
impl fmt::Debug for DynEnumType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn discriminant_bit_width_impl(variant_count: usize) -> usize { fn discriminant_bit_width_impl(variant_count: usize) -> usize {
variant_count variant_count
@ -153,38 +61,47 @@ fn discriminant_bit_width_impl(variant_count: usize) -> usize {
.unwrap_or(0) as usize .unwrap_or(0) as usize
} }
impl DynEnumType { impl Enum {
#[track_caller] #[track_caller]
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { pub fn new(variants: Interned<[EnumVariant]>) -> Self {
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
let mut name_indexes = HashMap::with_capacity(variants.len()); let mut name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize; let mut type_properties = TypeProperties {
let mut is_storable = true; is_passive: true,
let mut is_castable_from_bits = true; is_storable: true,
for (index, &VariantType { name, ty }) in variants.iter().enumerate() { is_castable_from_bits: true,
if let Some(old_index) = name_indexes.insert(name, index) { bit_width: 0,
};
for (index, EnumVariant { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(*name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
} }
if let Some(ty) = ty { if let Some(ty) = ty {
assert!( let TypeProperties {
ty.is_passive(), is_passive,
"variant type must be a passive type: {ty:?}" is_storable,
); is_castable_from_bits,
body_bit_width = body_bit_width.max(ty.bit_width()); bit_width,
is_storable &= ty.is_storable(); } = ty.type_properties();
is_castable_from_bits &= ty.is_castable_from_bits(); assert!(is_passive, "variant type must be a passive type: {ty:?}");
type_properties = TypeProperties {
is_passive: true,
is_storable: type_properties.is_storable & is_storable,
is_castable_from_bits: type_properties.is_castable_from_bits
& is_castable_from_bits,
bit_width: type_properties.bit_width.max(bit_width),
};
} }
} }
let bit_width = body_bit_width type_properties.bit_width = type_properties
.bit_width
.checked_add(discriminant_bit_width_impl(variants.len())) .checked_add(discriminant_bit_width_impl(variants.len()))
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
Self( Self(
DynEnumTypeImpl { EnumImpl {
variants, variants,
name_indexes, name_indexes,
bit_width, type_properties,
is_storable,
is_castable_from_bits,
} }
.intern_sized(), .intern_sized(),
) )
@ -192,233 +109,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, MatchActiveScope = Scope,
MaskType = UIntType<1>, MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MaskValue = UInt<1>, MatchVariantsIter = EnumMatchVariantsIter<Self>,
MatchActiveScope = Scope, >
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<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 +142,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 +171,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 +187,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 { impl Type for Enum {
VariantsHint { type MaskType = Bool;
known_variants: [][..].intern(), type MatchVariant = Option<Expr<CanonicalType>>;
more_variants: true, 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> {
} None,
Some(T),
impl ToExpr for DynEnum {
type Type = DynEnumType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynEnum {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
impl EnumValue for DynEnum {}
impl CanonicalValue for DynEnum {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Enum(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
mod impl_option {
#[allow(dead_code)]
#[derive(crate::ty::Value)]
#[hdl(target(std::option::Option), connect_inexact, outline_generated)]
pub enum Option<T> {
None,
Some(T),
}
} }

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

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

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