From d5ff103127cd4378454f53d126a781b63d1f2c74 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 7 Aug 2024 03:16:29 -0700 Subject: [PATCH] WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types --- .forgejo/workflows/test.yml | 21 +- .../src/hdl_bundle.rs | 766 +++++ .../fayalite-proc-macros-impl/src/hdl_enum.rs | 602 ++++ .../src/hdl_type_common.rs | 1944 +++++++++++ crates/fayalite-proc-macros-impl/src/lib.rs | 173 +- .../src/module/transform_body.rs | 5 +- crates/fayalite-proc-macros/src/lib.rs | 12 + crates/fayalite/src/array.rs | 730 +---- crates/fayalite/src/bundle.rs | 935 ++---- crates/fayalite/src/enum_.rs | 605 +--- crates/fayalite/src/expr.rs | 1171 +------ crates/fayalite/src/expr/ops.rs | 2869 +++++++---------- crates/fayalite/src/int.rs | 1697 ++-------- crates/fayalite/src/lib.rs | 82 +- crates/fayalite/src/ty.rs | 1010 +----- crates/fayalite/src/util.rs | 3 + crates/fayalite/src/util/const_usize.rs | 29 + crates/fayalite/tests/value_derive.rs | 22 +- 18 files changed, 5768 insertions(+), 6908 deletions(-) create mode 100644 crates/fayalite-proc-macros-impl/src/hdl_bundle.rs create mode 100644 crates/fayalite-proc-macros-impl/src/hdl_enum.rs create mode 100644 crates/fayalite-proc-macros-impl/src/hdl_type_common.rs create mode 100644 crates/fayalite/src/util/const_usize.rs diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 71f4a3b..b6b7f21 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -7,13 +7,14 @@ jobs: - uses: https://code.forgejo.org/actions/checkout@v3 with: fetch-depth: 0 - - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 - source "$HOME/.cargo/env" - echo "$PATH" >> "$GITHUB_PATH" - - uses: https://github.com/Swatinem/rust-cache@v2 - with: - save-if: ${{ github.ref == 'refs/heads/master' }} - - run: cargo test - - run: cargo test --features=unstable-doc - - run: cargo doc --features=unstable-doc +# FIXME: uncomment once the code works again +# - run: | +# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 +# source "$HOME/.cargo/env" +# echo "$PATH" >> "$GITHUB_PATH" +# - uses: https://github.com/Swatinem/rust-cache@v2 +# with: +# save-if: ${{ github.ref == 'refs/heads/master' }} +# - run: cargo test +# - run: cargo test --features=unstable-doc +# - run: cargo doc --features=unstable-doc diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs new file mode 100644 index 0000000..6aad8f4 --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -0,0 +1,766 @@ +use crate::{ + hdl_type_common::{ + common_derives, get_target, ItemOptions, MaybeParsed, 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, + pub(crate) options: HdlAttr, + pub(crate) vis: Visibility, + pub(crate) struct_token: Token![struct], + pub(crate) ident: Ident, + pub(crate) generics: MaybeParsed, + pub(crate) fields: MaybeParsed, + pub(crate) field_flips: Vec>>, + 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> { + 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 { + 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::::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 (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<::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(); + 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< + ::MatchVariant, + >; + type MatchVariantsIter = ::fayalite::__std::iter::Once< + ::MatchVariantAndInactiveScope, + >; + fn match_variants( + __this: ::fayalite::expr::Expr, + __module_builder: &mut ::fayalite::module::ModuleBuilder< + ::fayalite::module::NormalModule, + >, + __source_location: ::fayalite::source_location::SourceLocation, + ) -> ::MatchVariantsIter { + let __retval = #mask_type_match_variant_ident { + #(#match_variant_body_fields)* + }; + ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) + } + fn mask_type(&self) -> ::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) -> &::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< + ::MatchVariant, + >; + type MatchVariantsIter = ::fayalite::__std::iter::Once< + ::MatchVariantAndInactiveScope, + >; + fn match_variants( + __this: ::fayalite::expr::Expr, + __module_builder: &mut ::fayalite::module::ModuleBuilder< + ::fayalite::module::NormalModule, + >, + __source_location: ::fayalite::source_location::SourceLocation, + ) -> ::MatchVariantsIter { + let __retval = #match_variant_ident { + #(#match_variant_body_fields)* + }; + ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) + } + fn mask_type(&self) -> ::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) -> &::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 { + 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) +} diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs new file mode 100644 index 0000000..4f1be8e --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -0,0 +1,602 @@ +use crate::{ + hdl_type_common::{ + common_derives, get_target, ItemOptions, MaybeParsed, ParseFailed, ParseTypes, + 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, + pub(crate) options: HdlAttr, + pub(crate) ty: MaybeParsed, + pub(crate) comma_token: Option, +} + +#[derive(Clone, Debug)] +pub(crate) struct ParsedVariant { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) ident: Ident, + pub(crate) field: Option, +} + +impl ParsedVariant { + fn parse( + errors: &mut Errors, + variant: Variant, + generics: &MaybeParsed, + ) -> 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, + pub(crate) options: HdlAttr, + pub(crate) vis: Visibility, + pub(crate) enum_token: Token![enum], + pub(crate) ident: Ident, + pub(crate) generics: MaybeParsed, + pub(crate) brace_token: Brace, + pub(crate) variants: Punctuated, + pub(crate) match_variant_ident: Ident, +} + +impl ParsedEnum { + fn parse(item: ItemEnum) -> syn::Result { + 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::::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(); + 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>( + self, + v: __V, + ) -> ::fayalite::expr::Expr { + ::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 { + ::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; + type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter; + + fn match_variants( + this: ::fayalite::expr::Expr, + module_builder: &mut ::fayalite::module::ModuleBuilder<::fayalite::module::NormalModule>, + source_location: ::fayalite::source_location::SourceLocation, + ) -> ::MatchVariantsIter { + module_builder.enum_match_variants_helper(this, source_location) + } + fn mask_type(&self) -> ::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: ::MatchVariantAndInactiveScope, + ) -> (::MatchVariant, ::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 { + 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) +} diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs new file mode 100644 index 0000000..b5128fa --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -0,0 +1,1944 @@ +use crate::{ + kw::{self, custom_bounds}, + Errors, HdlAttr, PairsIterExt, +}; +use proc_macro2::{Span, TokenStream}; +use quote::{quote_spanned, ToTokens}; +use std::{collections::HashMap, fmt}; +use syn::{ + parse::{Parse, ParseStream}, + parse_quote, parse_quote_spanned, + punctuated::Punctuated, + spanned::Spanned, + token::{Brace, Paren}, + AngleBracketedGenericArguments, Attribute, ConstParam, Expr, Field, FieldMutability, + FieldsNamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Path, PathArguments, + PathSegment, PredicateType, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, + TypeParen, TypePath, TypeTuple, Visibility, WhereClause, WherePredicate, +}; + +crate::options! { + #[options = ItemOptions] + pub(crate) enum ItemOption { + OutlineGenerated(outline_generated), + ConnectInexact(connect_inexact), + Target(target, Path), + CustomBounds(custom_bounds), + NoStatic(no_static), + } +} + +impl ItemOptions { + pub(crate) fn validate(&mut self) -> syn::Result<()> { + if let Self { + custom_bounds: Some((custom_bounds,)), + no_static: None, + .. + } = self + { + self.no_static = Some((kw::no_static(custom_bounds.span),)); + } + Ok(()) + } +} + +pub(crate) struct WrappedInConst<'a> { + outer: &'a mut TokenStream, + span: Span, + inner: TokenStream, +} + +impl<'a> WrappedInConst<'a> { + pub(crate) fn new(outer: &'a mut TokenStream, span: Span) -> Self { + Self { + outer, + span, + inner: TokenStream::new(), + } + } + pub(crate) fn inner(&mut self) -> &mut TokenStream { + &mut self.inner + } +} + +impl Drop for WrappedInConst<'_> { + fn drop(&mut self) { + let inner = &self.inner; + quote_spanned! {self.span=> + const _: () = { + #inner + }; + } + .to_tokens(self.outer); + } +} + +pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path { + match target { + Some((_, _, target)) => target.clone(), + None => item_ident.clone().into(), + } +} + +pub(crate) fn common_derives(span: Span) -> Attribute { + parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::cmp::Eq, + ::fayalite::__std::cmp::PartialEq, + ::fayalite::__std::hash::Hash, + ::fayalite::__std::marker::Copy, + ::fayalite::__std::clone::Clone, + )] + } +} + +crate::options! { + #[options = LifetimeParamOptions] + enum LifetimeParamOption {} +} + +crate::options! { + #[options = TypeParamOptions] + pub(crate) enum TypeParamOption {} +} + +crate::options! { + #[options = ConstParamOptions] + pub(crate) enum ConstParamOption {} +} + +crate::options! { + #[options = TypeOptions] + enum TypeOption {} +} + +macro_rules! parse_failed { + ($parser:ident, $tokens:expr, $message:expr) => {{ + $parser.errors().error($tokens, $message); + return Err(ParseFailed); + }}; +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum TypeDelimiter { + Group(syn::token::Group), + Paren(Paren), +} + +impl TypeDelimiter { + pub(crate) fn surround(self, tokens: &mut TokenStream, f: F) { + match self { + TypeDelimiter::Group(v) => v.surround(tokens, f), + TypeDelimiter::Paren(v) => v.surround(tokens, f), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeDelimited { + pub(crate) delim: TypeDelimiter, + pub(crate) elem: Box, +} + +impl ToTokens for ParsedTypeDelimited { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { delim, elem } = self; + delim.surround(tokens, |tokens| elem.to_tokens(tokens)); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeTuple { + pub(crate) paren_token: Paren, + pub(crate) elems: Punctuated, +} + +impl ToTokens for ParsedTypeTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { paren_token, elems } = self; + paren_token.surround(tokens, |tokens| { + elems.to_tokens(tokens); + if elems.len() == 1 && !elems.trailing_punct() { + // trailing comma needed to distinguish from just parenthesis + Token![,](paren_token.span.close()).to_tokens(tokens); + } + }) + } +} + +impl ParseTypes for ParsedTypeTuple { + fn parse_types(ty: &mut TypeTuple, parser: &mut TypesParser<'_>) -> Result { + Ok(Self { + paren_token: ty.paren_token, + elems: parser.parse(&mut ty.elems)?, + }) + } +} + +#[derive(Debug, Clone)] +pub(crate) enum ParsedGenericArgument { + Type(ParsedType), + Const(Expr), +} + +impl ToTokens for ParsedGenericArgument { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Type(ty) => ty.to_tokens(tokens), + Self::Const(expr) => expr.to_tokens(tokens), + } + } +} + +impl ParseTypes for ParsedGenericArgument { + fn parse_types( + arg: &mut GenericArgument, + parser: &mut TypesParser<'_>, + ) -> Result { + match arg { + GenericArgument::Type(ty) => Ok(Self::Type(parser.parse(ty)?)), + GenericArgument::Const(expr) => Ok(Self::Const(expr.clone())), + _ => parse_failed!(parser, arg, "expected type or const generic argument"), + } + } +} + +impl From for GenericArgument { + fn from(value: ParsedGenericArgument) -> Self { + match value { + ParsedGenericArgument::Type(ty) => Self::Type(ty.into()), + ParsedGenericArgument::Const(expr) => Self::Const(expr), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenericArguments { + pub(crate) colon2_token: Option, + pub(crate) lt_token: Token![<], + pub(crate) args: Punctuated, + pub(crate) gt_token: Token![>], +} + +impl ToTokens for ParsedGenericArguments { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + colon2_token, + lt_token, + args, + gt_token, + } = self; + colon2_token.to_tokens(tokens); + lt_token.to_tokens(tokens); + args.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + +impl ParseTypes for ParsedGenericArguments { + fn parse_types( + args: &mut AngleBracketedGenericArguments, + parser: &mut TypesParser<'_>, + ) -> Result { + let AngleBracketedGenericArguments { + colon2_token, + lt_token, + ref mut args, + gt_token, + } = *args; + Ok(Self { + colon2_token, + lt_token, + args: parser.parse(args)?, + gt_token, + }) + } +} + +impl From for AngleBracketedGenericArguments { + fn from(value: ParsedGenericArguments) -> Self { + let ParsedGenericArguments { + colon2_token, + lt_token, + args, + gt_token, + } = value; + AngleBracketedGenericArguments { + colon2_token, + lt_token, + args: Punctuated::from_iter(args.into_pairs().map_pair_value(Into::into)), + gt_token, + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeNamed { + pub(crate) leading_colon: Option, + pub(crate) segments: Punctuated, + pub(crate) args: Option, +} + +impl ParsedTypeNamed { + pub(crate) fn get_ident(&self) -> Option<&Ident> { + let Self { + leading_colon: None, + segments, + args: None, + } = self + else { + return None; + }; + if segments.len() == 1 && !segments.trailing_punct() { + Some(&segments[0]) + } else { + None + } + } +} + +impl ToTokens for ParsedTypeNamed { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + leading_colon, + segments, + args, + } = self; + leading_colon.to_tokens(tokens); + segments.to_tokens(tokens); + args.to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeNamedParamType { + pub(crate) ident: Ident, + pub(crate) param_index: usize, +} + +impl ToTokens for ParsedTypeNamedParamType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + ident, + param_index: _, + } = self; + ident.to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeNamedParamSizeType { + pub(crate) ident: Ident, + pub(crate) param_index: usize, +} + +impl ToTokens for ParsedTypeNamedParamSizeType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + ident, + param_index: _, + } = self; + ident.to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) enum ParsedTypeNamedParam { + Type(ParsedTypeNamedParamType), + SizeType(ParsedTypeNamedParamSizeType), +} + +impl ParsedTypeNamedParam { + pub(crate) fn into_ident(self) -> Ident { + match self { + Self::Type(v) => v.ident, + Self::SizeType(v) => v.ident, + } + } + pub(crate) fn ident(&self) -> &Ident { + match self { + Self::Type(v) => &v.ident, + Self::SizeType(v) => &v.ident, + } + } + pub(crate) fn param_index(&self) -> usize { + match self { + Self::Type(v) => v.param_index, + Self::SizeType(v) => v.param_index, + } + } +} + +impl ToTokens for ParsedTypeNamedParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Type(v) => v.to_tokens(tokens), + Self::SizeType(v) => v.to_tokens(tokens), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum ParsedType { + Delimited(ParsedTypeDelimited), + Named(ParsedTypeNamed), + NamedParam(ParsedTypeNamedParam), + Tuple(ParsedTypeTuple), +} + +impl From for Type { + fn from(value: ParsedType) -> Self { + match value { + ParsedType::Delimited(ParsedTypeDelimited { + delim: TypeDelimiter::Group(group_token), + elem, + }) => Type::Group(TypeGroup { + group_token, + elem: Box::new(elem.into()), + }), + ParsedType::Delimited(ParsedTypeDelimited { + delim: TypeDelimiter::Paren(paren_token), + elem, + }) => Type::Paren(TypeParen { + paren_token, + elem: Box::new(elem.into()), + }), + ParsedType::NamedParam(value) => Type::Path(TypePath { + qself: None, + path: value.into_ident().into(), + }), + ParsedType::Named(ParsedTypeNamed { + leading_colon, + segments, + args, + }) => Type::Path(TypePath { + qself: None, + path: Path { + leading_colon, + segments: { + let mut segments = Punctuated::::from_iter( + segments.into_pairs().map_pair_value(Into::into), + ); + if let Some(args) = args { + segments.last_mut().unwrap().arguments = + PathArguments::AngleBracketed(args.into()); + } + segments + }, + }, + }), + ParsedType::Tuple(ParsedTypeTuple { paren_token, elems }) => Type::Tuple(TypeTuple { + paren_token, + elems: Punctuated::from_iter(elems.into_pairs().map_pair_value(Into::into)), + }), + } + } +} + +impl From> for Type { + fn from(value: MaybeParsed) -> Self { + match value { + MaybeParsed::Unrecognized(value) => value, + MaybeParsed::Parsed(value) => value.into(), + } + } +} + +impl From> for Type { + fn from(value: Box) -> Self { + (*value).into() + } +} + +impl ToTokens for ParsedType { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + ParsedType::Delimited(ty) => ty.to_tokens(tokens), + ParsedType::NamedParam(ty) => ty.to_tokens(tokens), + ParsedType::Named(ty) => ty.to_tokens(tokens), + ParsedType::Tuple(ty) => ty.to_tokens(tokens), + } + } +} + +impl ParsedType { + pub(crate) fn unwrap_delimiters(mut self) -> Self { + loop { + match self { + ParsedType::Delimited(ty) => self = *ty.elem, + _ => return self, + } + } + } + pub(crate) fn unwrap_delimiters_ref(mut self: &Self) -> &Self { + loop { + match self { + ParsedType::Delimited(ty) => self = &*ty.elem, + _ => return self, + } + } + } + pub(crate) fn unwrap_delimiters_mut(mut self: &mut Self) -> &mut Self { + loop { + match self { + ParsedType::Delimited(ty) => self = &mut *ty.elem, + _ => return self, + } + } + } +} + +impl ParseTypes for ParsedType { + fn parse_types(path: &mut Path, parser: &mut TypesParser<'_>) -> Result { + let Path { + leading_colon, + ref mut segments, + } = *path; + if segments.is_empty() { + parse_failed!(parser, path, "path must not be empty"); + } + let mut args = None; + let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { + let PathSegment { ident, arguments } = segment; + if let Some(_) = args { + parser + .errors() + .error(&ident, "associated types/consts are not yet implemented"); + } + args = match arguments { + PathArguments::None => None, + PathArguments::AngleBracketed(args) => parser.parse(args).ok(), + PathArguments::Parenthesized(_) => { + parser + .errors() + .error(&segment, "function traits are not allowed"); + args = None; + return segment.ident.clone(); + } + }; + ident.clone() + })); + let named = ParsedTypeNamed { + leading_colon, + segments, + args, + }; + if let Some(ident) = named.get_ident() { + if let Some(¶m_index) = parser.generics.param_name_to_index_map.get(ident) { + if parser + .cur_param_index + .is_some_and(|cur_param_index| param_index >= cur_param_index) + { + parser.errors.error( + parser.generics.params[param_index].ident(), + "cannot use forward declared identifier in generic parameter defaults", + ); + } + return Ok(Self::NamedParam( + match parser.generics.params[param_index] { + ParsedGenericParam::Type(_) => { + ParsedTypeNamedParam::Type(ParsedTypeNamedParamType { + ident: ident.clone(), + param_index, + }) + } + ParsedGenericParam::SizeType(_) => { + ParsedTypeNamedParam::SizeType(ParsedTypeNamedParamSizeType { + ident: ident.clone(), + param_index, + }) + } + }, + )); + } + } + Ok(Self::Named(named)) + } +} + +impl ParseTypes for ParsedType { + fn parse_types(ty: &mut TypePath, parser: &mut TypesParser<'_>) -> Result { + let TypePath { qself, path } = ty; + if let Some(qself) = qself { + // TODO: implement + parse_failed!( + parser, + ty, + "associated types/consts are not yet implemented" + ); + } else { + parser.parse(path) + } + } +} + +impl ParseTypes for ParsedType { + fn parse_types(ty: &mut Type, parser: &mut TypesParser<'_>) -> Result { + Ok(match ty { + Type::Array(_) => parse_failed!(parser, ty, "array types are not allowed here"), + Type::BareFn(_) => parse_failed!(parser, ty, "fn() types are not allowed here"), + Type::Group(TypeGroup { group_token, elem }) => Self::Delimited(ParsedTypeDelimited { + delim: TypeDelimiter::Group(*group_token), + elem: parser.parse(elem)?, + }), + Type::ImplTrait(_) => { + parse_failed!(parser, ty, "impl Trait types are not allowed here") + } + Type::Infer(_) => parse_failed!(parser, ty, "inferred types are not allowed here"), + Type::Macro(_) => parse_failed!(parser, ty, "macro types are not allowed here"), + Type::Never(_) => parse_failed!(parser, ty, "the never type is not allowed here"), + Type::Paren(TypeParen { paren_token, elem }) => Self::Delimited(ParsedTypeDelimited { + delim: TypeDelimiter::Paren(*paren_token), + elem: parser.parse(elem)?, + }), + Type::Path(ty) => parser.parse(ty)?, + Type::Ptr(_) => parse_failed!(parser, ty, "pointer types are not allowed here"), + Type::Reference(_) => parse_failed!(parser, ty, "reference types are not allowed here"), + Type::Slice(_) => parse_failed!(parser, ty, "slice types are not allowed here"), + Type::TraitObject(_) => { + parse_failed!(parser, ty, "dyn Trait types are not allowed here") + } + Type::Tuple(ty) => Self::Tuple(parser.parse(ty)?), + Type::Verbatim(_) => parse_failed!(parser, ty, "unknown type kind"), + _ => parse_failed!(parser, ty, "unknown type kind"), + }) + } +} + +pub(crate) struct ParseFailed; + +pub(crate) struct TypesParser<'a> { + generics: &'a ParsedGenerics, + /// used when parsing generic parameter defaults so + /// earlier parameter defaults can't refer to later parameters + cur_param_index: Option, + errors: &'a mut Errors, +} + +impl<'a> TypesParser<'a> { + pub(crate) fn run_with_errors, I>( + generics: &ParsedGenerics, + input: &mut I, + errors: &mut Errors, + ) -> Result { + TypesParser { + generics, + cur_param_index: None, + errors, + } + .parse(input) + } + pub(crate) fn run, I>( + generics: &ParsedGenerics, + input: &mut I, + ) -> syn::Result { + let mut errors = Errors::new(); + let retval = Self::run_with_errors(generics, input, &mut errors); + errors.finish()?; + retval.map_err(|ParseFailed {}| panic!("parse failed without registering any errors")) + } + pub(crate) fn maybe_run, I, G>( + generics: MaybeParsed<&ParsedGenerics, G>, + mut input: I, + errors: &mut Errors, + ) -> MaybeParsed { + let MaybeParsed::Parsed(generics) = generics else { + return MaybeParsed::Unrecognized(input); + }; + match Self::run_with_errors(generics, &mut input, errors) { + Ok(v) => MaybeParsed::Parsed(v), + Err(ParseFailed {}) => MaybeParsed::Unrecognized(input), + } + } + pub(crate) fn generics(&self) -> &'a ParsedGenerics { + self.generics + } + pub(crate) fn errors(&mut self) -> &mut Errors { + self.errors + } + pub(crate) fn parse, I>(&mut self, input: &mut I) -> Result { + T::parse_types(input, self) + } +} + +pub(crate) trait ParseTypes: Sized { + fn parse_types(input: &mut I, parser: &mut TypesParser<'_>) -> Result; +} + +impl, I> ParseTypes> for Box { + fn parse_types(input: &mut Box, parser: &mut TypesParser<'_>) -> Result { + Ok(Box::new(parser.parse(&mut **input)?)) + } +} + +impl, I, P: Clone> ParseTypes> for Punctuated { + fn parse_types( + input: &mut Punctuated, + parser: &mut TypesParser<'_>, + ) -> Result { + let retval = Punctuated::from_iter( + input + .pairs_mut() + .filter_map_pair_value_mut(|input| parser.parse(input).ok()), + ); + Ok(retval) + } +} + +#[derive(Debug, Clone)] +pub(crate) enum UnparsedGenericParam { + Type { + attrs: Vec, + options: HdlAttr, + ident: Ident, + colon_token: Token![:], + bounds: ParsedBounds, + }, +} + +pub(crate) mod known_items { + use proc_macro2::{Ident, Span, TokenStream}; + use quote::ToTokens; + use syn::{ + parse::{Parse, ParseStream}, + Path, PathArguments, PathSegment, Token, + }; + + macro_rules! impl_known_item_body { + ($known_item:ident) => { + #[derive(Clone, Debug)] + pub(crate) struct $known_item { + pub(crate) path: Path, + } + + #[allow(non_snake_case, dead_code)] + pub(crate) fn $known_item(span: Span) -> $known_item { + let segments = $known_item::PATH_SEGMENTS.iter() + .copied() + .map(|seg| PathSegment::from(Ident::new(seg, span))) + .collect(); + $known_item { + path: Path { + leading_colon: Some(Token![::](span)), + segments, + }, + } + } + + impl ToTokens for $known_item { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.path.to_tokens(tokens); + } + } + + impl $known_item { + pub(crate) fn parse_path(path: Path) -> Result { + if path.is_ident(stringify!($known_item)) { + Ok(Self { path }) + } else if path.segments.len() == Self::PATH_SEGMENTS.len() + && path + .segments + .iter() + .zip(Self::PATH_SEGMENTS) + .all(|(seg, expected)| { + matches!(seg.arguments, PathArguments::None) + && seg.ident == *expected + }) + { + Ok(Self { path }) + } else { + Err(path) + } + } + } + + impl Parse for $known_item { + fn parse(input: ParseStream) -> syn::Result { + Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { + syn::Error::new_spanned( + path, + concat!("expected ", stringify!($known_item)), + ) + }) + } + } + }; + } + + macro_rules! impl_known_item { + ($([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => { + impl_known_item!([$($(::$head)*)? ::$next] $(::$tail)+); + }; + ([$(::$seg:ident)+] ::$known_item:ident) => { + impl_known_item_body!($known_item); + + impl $known_item { + pub(crate) const PATH_SEGMENTS: &'static [&'static str] = &[ + $(stringify!($seg),)+ + stringify!($known_item), + ]; + } + }; + } + + impl_known_item!(::fayalite::bundle::BundleType); + impl_known_item!(::fayalite::enum_::EnumType); + impl_known_item!(::fayalite::int::IntType); + impl_known_item!(::fayalite::int::KnownSize); + impl_known_item!(::fayalite::int::Size); + impl_known_item!(::fayalite::ty::StaticType); + impl_known_item!(::fayalite::ty::Type); +} + +macro_rules! impl_bounds { + ( + #[struct = $struct_type:ident] + $vis:vis enum $enum_type:ident { + $( + $Variant:ident, + )* + } + ) => { + #[derive(Clone, Debug)] + $vis enum $enum_type { + $($Variant(known_items::$Variant),)* + } + + $(impl From for $enum_type { + fn from(v: known_items::$Variant) -> Self { + Self::$Variant(v) + } + })* + + impl ToTokens for $enum_type { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + $(Self::$Variant(v) => v.to_tokens(tokens),)* + } + } + } + + impl $enum_type { + $vis fn parse_path(path: Path) -> Result { + $(let path = match known_items::$Variant::parse_path(path) { + Ok(v) => return Ok(Self::$Variant(v)), + Err(path) => path, + };)* + Err(path) + } + } + + impl Parse for $enum_type { + fn parse(input: ParseStream) -> syn::Result { + Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { + syn::Error::new_spanned( + path, + format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), + ) + }) + } + } + + #[derive(Clone, Debug, Default)] + #[allow(non_snake_case)] + $vis struct $struct_type { + $($vis $Variant: Option,)* + } + + impl ToTokens for $struct_type { + #[allow(unused_mut, unused_variables, unused_assignments)] + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut separator = None; + $(if let Some(v) = &self.$Variant { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* + } + } + + const _: () = { + #[derive(Clone, Debug)] + $vis struct Iter($vis $struct_type); + + impl IntoIterator for $struct_type { + type Item = $enum_type; + type IntoIter = Iter; + + fn into_iter(self) -> Self::IntoIter { + Iter(self) + } + } + + impl Iterator for Iter { + type Item = $enum_type; + + + fn next(&mut self) -> Option { + $( + if let Some(value) = self.0.$Variant.take() { + return Some($enum_type::$Variant(value)); + } + )* + None + } + + #[allow(unused_mut, unused_variables)] + fn fold B>(mut self, mut init: B, mut f: F) -> B { + $( + if let Some(value) = self.0.$Variant.take() { + init = f(init, $enum_type::$Variant(value)); + } + )* + init + } + } + }; + + impl Extend<$enum_type> for $struct_type { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|v| match v { + $($enum_type::$Variant(v) => { + self.$Variant = Some(v); + })* + }); + } + } + + impl FromIterator<$enum_type> for $struct_type { + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } + } + + impl Extend<$struct_type> for $struct_type { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|v| { + $(if let Some(v) = v.$Variant { + self.$Variant = Some(v); + })* + }); + } + } + + impl FromIterator<$struct_type> for $struct_type { + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } + } + + impl Parse for $struct_type { + fn parse(input: ParseStream) -> syn::Result { + let mut retval = Self::default(); + while !input.is_empty() { + retval.extend([input.parse::<$enum_type>()?]); + if input.is_empty() { + break; + } + input.parse::()?; + } + Ok(retval) + } + } + }; +} + +impl_bounds! { + #[struct = ParsedBounds] + pub(crate) enum ParsedBound { + BundleType, + EnumType, + IntType, + KnownSize, + Size, + StaticType, + Type, + } +} + +impl_bounds! { + #[struct = ParsedTypeBounds] + pub(crate) enum ParsedTypeBound { + BundleType, + EnumType, + IntType, + StaticType, + Type, + } +} + +impl_bounds! { + #[struct = ParsedSizeTypeBounds] + pub(crate) enum ParsedSizeTypeBound { + KnownSize, + Size, + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ParsedBoundsCategory { + Type(ParsedTypeBounds), + SizeType(ParsedSizeTypeBounds), +} + +impl ParsedBounds { + fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { + let mut type_bounds = None; + let mut size_type_bounds = None; + self.into_iter().for_each(|bound| match bound.categorize() { + ParsedBoundCategory::Type(bound) => { + type_bounds + .get_or_insert_with(ParsedTypeBounds::default) + .extend([bound]); + } + ParsedBoundCategory::SizeType(bound) => { + size_type_bounds + .get_or_insert_with(ParsedSizeTypeBounds::default) + .extend([bound]); + } + }); + match (type_bounds, size_type_bounds) { + (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { + Type: Some(known_items::Type(span)), + ..Default::default() + }), + (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), + (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), + (Some(type_bounds), Some(size_type_bounds)) => { + errors.error( + size_type_bounds + .Size + .unwrap_or_else(|| known_items::Size(span)), + "conflicting bounds: can't use `Size` bounds with `Type` bounds", + ); + ParsedBoundsCategory::Type(type_bounds) + } + } + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ParsedBoundCategory { + Type(ParsedTypeBound), + SizeType(ParsedSizeTypeBound), +} + +impl ParsedBound { + fn categorize(self) -> ParsedBoundCategory { + match self { + Self::BundleType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BundleType(v)), + Self::EnumType(v) => ParsedBoundCategory::Type(ParsedTypeBound::EnumType(v)), + Self::IntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::IntType(v)), + Self::KnownSize(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::KnownSize(v)), + Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), + Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), + Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), + } + } + fn implied_bounds(self) -> ParsedBounds { + let span = self.span(); + match self { + Self::BundleType(v) => ParsedBounds::from_iter([ + ParsedBound::from(v), + ParsedBound::Type(known_items::Type(span)), + ]), + Self::EnumType(v) => ParsedBounds::from_iter([ + ParsedBound::from(v), + ParsedBound::Type(known_items::Type(span)), + ]), + Self::IntType(v) => ParsedBounds::from_iter([ + ParsedBound::from(v), + ParsedBound::Type(known_items::Type(span)), + ]), + Self::KnownSize(v) => ParsedBounds::from_iter([ + ParsedBound::from(v), + ParsedBound::Size(known_items::Size(span)), + ]), + Self::Size(v) => ParsedBounds::from_iter([ParsedBound::from(v)]), + Self::StaticType(v) => ParsedBounds::from_iter([ + ParsedBound::from(v), + ParsedBound::Type(known_items::Type(span)), + ]), + Self::Type(v) => ParsedBounds::from_iter([ParsedBound::from(v)]), + } + } +} + +impl ParsedBounds { + fn add_implied_bounds(&mut self) { + let orig_bounds = self.clone(); + self.extend(self.clone().into_iter().map(ParsedBound::implied_bounds)); + self.extend([orig_bounds]); // keep spans of explicitly provided bounds + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypeParam { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) ident: Ident, + pub(crate) colon_token: Token![:], + pub(crate) bounds: ParsedTypeBounds, + pub(crate) default: Option<(Token![=], ParsedType)>, +} + +impl ToTokens for ParsedTypeParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + ident, + colon_token, + bounds, + default, + } = self; + let TypeParamOptions {} = options.body; + for attr in attrs { + attr.to_tokens(tokens); + } + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + bounds.to_tokens(tokens); + if let Some((eq, ty)) = default { + eq.to_tokens(tokens); + ty.to_tokens(tokens); + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedSizeTypeParam { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) ident: Ident, + pub(crate) colon_token: Token![:], + pub(crate) bounds: ParsedSizeTypeBounds, + pub(crate) default: Option<(Token![=], ParsedType)>, +} + +impl ToTokens for ParsedSizeTypeParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + ident, + colon_token, + bounds, + default, + } = self; + let TypeParamOptions {} = options.body; + for attr in attrs { + attr.to_tokens(tokens); + } + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + bounds.to_tokens(tokens); + if let Some((eq, ty)) = default { + eq.to_tokens(tokens); + ty.to_tokens(tokens); + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum ParsedGenericParam { + Type(ParsedTypeParam), + SizeType(ParsedSizeTypeParam), +} + +impl ToTokens for ParsedGenericParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + ParsedGenericParam::Type(v) => v.to_tokens(tokens), + ParsedGenericParam::SizeType(v) => v.to_tokens(tokens), + } + } +} + +impl ParsedGenericParam { + pub(crate) fn ident(&self) -> &Ident { + match self { + ParsedGenericParam::Type(v) => &v.ident, + ParsedGenericParam::SizeType(v) => &v.ident, + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenerics { + pub(crate) lt_token: Option, + pub(crate) params: Punctuated, + pub(crate) gt_token: Option]>, + pub(crate) param_name_to_index_map: HashMap, +} + +impl ParsedGenerics { + pub(crate) fn for_static_type(mut self) -> ParsedGenerics { + for param in self.params.iter_mut() { + match param { + ParsedGenericParam::Type(ParsedTypeParam { ident, bounds, .. }) => { + bounds + .StaticType + .get_or_insert_with(|| known_items::StaticType(ident.span())); + } + ParsedGenericParam::SizeType(ParsedSizeTypeParam { ident, bounds, .. }) => { + bounds + .KnownSize + .get_or_insert_with(|| known_items::KnownSize(ident.span())); + } + } + } + self + } + pub(crate) fn parse(generics: &mut Generics) -> syn::Result { + let Generics { + lt_token, + params, + gt_token, + where_clause, + } = generics; + let mut errors = Errors::new(); + let mut predicates: Vec = Vec::with_capacity(params.len()); + let mut defaults = Vec::with_capacity(params.len()); + let mut params = Punctuated::::from_iter( + params.pairs_mut().filter_map_pair_value_mut(|param| { + let (param, default) = match param { + GenericParam::Lifetime(param) => { + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr(&mut param.attrs), + ); + errors.error(param, "lifetime generics are not supported by #[hdl]"); + return None; + } + GenericParam::Type(TypeParam { + attrs, + ident, + colon_token, + bounds, + eq_token, + default, + }) => { + let span = ident.span(); + let options = errors + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + attrs, + )) + .unwrap_or_default(); + let colon_token = colon_token.unwrap_or_else(|| Token![:](span)); + if !bounds.is_empty() { + predicates.push(WherePredicate::Type(PredicateType { + lifetimes: None, + bounded_ty: parse_quote! { #ident }, + colon_token, + bounds: bounds.clone(), + })); + } + ( + UnparsedGenericParam::Type { + attrs: attrs.clone(), + options, + ident: ident.clone(), + colon_token, + bounds: ParsedBounds::default(), + }, + default + .clone() + .map(|v| (eq_token.unwrap_or_else(|| Token![=](span)), v)), + ) + } + GenericParam::Const(ConstParam { + attrs, + const_token, + ident, + colon_token, + ty, + eq_token, + default, + }) => { + let options = errors + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + attrs, + )) + .unwrap_or_default(); + todo!() + } + }; + defaults.push(default); + Some(param) + }), + ); + let param_name_to_index_map: HashMap = params + .iter() + .enumerate() + .map(|(index, param)| { + let UnparsedGenericParam::Type { ident, .. } = param; + (ident.clone(), index) + }) + .collect(); + if let Some(where_clause) = where_clause { + predicates.extend(where_clause.predicates.iter().cloned()); + } + for predicate in predicates { + let WherePredicate::Type(PredicateType { + lifetimes: None, + bounded_ty: + Type::Path(TypePath { + qself: None, + path: bounded_ty, + }), + colon_token: _, + bounds: unparsed_bounds, + }) = predicate + else { + errors.error(predicate, "unsupported where predicate kind"); + continue; + }; + let Some(&index) = bounded_ty + .get_ident() + .and_then(|bounded_ty| param_name_to_index_map.get(bounded_ty)) + else { + errors.error( + bounded_ty, + "where predicate bounded type must be one of the generic parameters", + ); + continue; + }; + let UnparsedGenericParam::Type { + bounds: parsed_bounds, + .. + } = &mut params[index]; + parsed_bounds.extend(errors.ok(syn::parse2::( + unparsed_bounds.to_token_stream(), + ))); + } + let params = + Punctuated::from_iter(params.into_pairs().map_pair_value(|param| match param { + UnparsedGenericParam::Type { + attrs, + options, + ident, + colon_token, + mut bounds, + } => { + bounds.add_implied_bounds(); + match bounds.categorize(&mut errors, ident.span()) { + ParsedBoundsCategory::Type(bounds) => { + ParsedGenericParam::Type(ParsedTypeParam { + attrs, + options, + ident, + colon_token, + bounds, + default: None, + }) + } + ParsedBoundsCategory::SizeType(bounds) => { + ParsedGenericParam::SizeType(ParsedSizeTypeParam { + attrs, + options, + ident, + colon_token, + bounds, + default: None, + }) + } + } + } + })); + let mut retval = Self { + lt_token: *lt_token, + params, + gt_token: *gt_token, + param_name_to_index_map, + }; + for (cur_param_index, default) in defaults.into_iter().enumerate() { + let Some((eq, mut ty)) = default else { + continue; + }; + let Ok(ty) = TypesParser { + generics: &retval, + cur_param_index: Some(cur_param_index), + errors: &mut errors, + } + .parse(&mut ty) else { + continue; + }; + match &mut retval.params[cur_param_index] { + ParsedGenericParam::Type(ParsedTypeParam { default, .. }) + | ParsedGenericParam::SizeType(ParsedSizeTypeParam { default, .. }) => { + *default = Some((eq, ty)) + } + } + } + errors.finish()?; + Ok(retval) + } +} + +impl ToTokens for ParsedGenerics { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + lt_token, + params, + gt_token, + param_name_to_index_map: _, + } = self; + if params.is_empty() { + return; + } + lt_token.unwrap_or_default().to_tokens(tokens); + params.to_tokens(tokens); + gt_token.unwrap_or_default().to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenericsImplGenerics<'a>(&'a ParsedGenerics); + +impl ToTokens for ParsedGenericsImplGenerics<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ParsedGenerics { + lt_token, + params, + gt_token, + param_name_to_index_map: _, + } = &self.0; + if params.is_empty() { + return; + } + lt_token.unwrap_or_default().to_tokens(tokens); + for param in params.pairs() { + let (param, punct) = param.into_tuple(); + match param { + ParsedGenericParam::Type(ParsedTypeParam { + attrs: _, + options, + ident, + colon_token, + bounds, + default: _, + }) => { + let TypeParamOptions {} = options.body; + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + bounds.to_tokens(tokens); + } + ParsedGenericParam::SizeType(ParsedSizeTypeParam { + attrs: _, + options, + ident, + colon_token, + bounds, + default: _, + }) => { + let TypeParamOptions {} = options.body; + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + bounds.to_tokens(tokens); + } + } + punct.to_tokens(tokens); + } + gt_token.unwrap_or_default().to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenericsTurbofish<'a>(&'a ParsedGenerics); + +impl ToTokens for ParsedGenericsTurbofish<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.0.params.is_empty() { + return; + } + Token![::](self.0.lt_token.unwrap_or_default().span).to_tokens(tokens); + ParsedGenericsTypeGenerics(self.0).to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenericsTypeGenerics<'a>(&'a ParsedGenerics); + +impl ToTokens for ParsedGenericsTypeGenerics<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ParsedGenerics { + lt_token, + params, + gt_token, + param_name_to_index_map: _, + } = &self.0; + if params.is_empty() { + return; + } + lt_token.unwrap_or_default().to_tokens(tokens); + for param in params.pairs() { + let (param, punct) = param.into_tuple(); + param.ident().to_tokens(tokens); + punct.to_tokens(tokens); + } + gt_token.unwrap_or_default().to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub(crate) struct ParsedGenericsWhereClause<'a>(&'a ParsedGenerics); + +impl ToTokens for ParsedGenericsWhereClause<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let _ = tokens; + } +} + +#[allow(dead_code)] +pub(crate) trait AsTurbofish { + type Turbofish<'a>: 'a + ToTokens + Clone + fmt::Debug + where + Self: 'a; + fn as_turbofish(&self) -> Self::Turbofish<'_>; +} + +impl AsTurbofish for TypeGenerics<'_> { + type Turbofish<'a> = Turbofish<'a> where Self: 'a; + + fn as_turbofish(&self) -> Self::Turbofish<'_> { + TypeGenerics::as_turbofish(self) + } +} + +impl AsTurbofish for ParsedGenericsTypeGenerics<'_> { + type Turbofish<'a> = ParsedGenericsTurbofish<'a> + where + Self: 'a; + + fn as_turbofish(&self) -> Self::Turbofish<'_> { + ParsedGenericsTurbofish(self.0) + } +} + +pub(crate) trait SplitForImpl { + type ImplGenerics<'a>: 'a + ToTokens + Clone + fmt::Debug + where + Self: 'a; + type TypeGenerics<'a>: 'a + AsTurbofish + ToTokens + Clone + fmt::Debug + where + Self: 'a; + type WhereClause<'a>: 'a + ToTokens + Clone + fmt::Debug + where + Self: 'a; + fn split_for_impl( + &self, + ) -> ( + Self::ImplGenerics<'_>, + Self::TypeGenerics<'_>, + Self::WhereClause<'_>, + ); +} + +impl SplitForImpl for Generics { + type ImplGenerics<'a> = ImplGenerics<'a>; + type TypeGenerics<'a> = TypeGenerics<'a>; + type WhereClause<'a> = Option<&'a WhereClause>; + fn split_for_impl( + &self, + ) -> ( + Self::ImplGenerics<'_>, + Self::TypeGenerics<'_>, + Self::WhereClause<'_>, + ) { + Generics::split_for_impl(&self) + } +} + +impl SplitForImpl for ParsedGenerics { + type ImplGenerics<'a> = ParsedGenericsImplGenerics<'a> + where + Self: 'a; + + type TypeGenerics<'a> = ParsedGenericsTypeGenerics<'a> + where + Self: 'a; + + type WhereClause<'a> = ParsedGenericsWhereClause<'a> + where + Self: 'a; + + fn split_for_impl( + &self, + ) -> ( + Self::ImplGenerics<'_>, + Self::TypeGenerics<'_>, + Self::WhereClause<'_>, + ) { + ( + ParsedGenericsImplGenerics(self), + ParsedGenericsTypeGenerics(self), + ParsedGenericsWhereClause(self), + ) + } +} + +impl From<&'_ ParsedGenerics> for Generics { + fn from(value: &ParsedGenerics) -> Self { + let mut retval: Generics = parse_quote! { #value }; + let where_clause = ParsedGenericsWhereClause(value).to_token_stream(); + if !where_clause.is_empty() { + retval.where_clause = Some(parse_quote! { #where_clause }); + } + retval + } +} + +impl From for Generics { + fn from(value: ParsedGenerics) -> Self { + From::<&ParsedGenerics>::from(&value) + } +} + +#[derive(Debug, Clone)] +pub(crate) enum MaybeParsed { + Unrecognized(U), + Parsed(P), +} + +impl MaybeParsed { + pub(crate) fn as_ref(&self) -> MaybeParsed<&P, &U> { + match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v), + } + } + pub(crate) fn as_mut(&mut self) -> MaybeParsed<&mut P, &mut U> { + match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v), + } + } + pub(crate) fn map PR, UF: FnOnce(U) -> UR, PR, UR>( + self, + pf: PF, + uf: UF, + ) -> MaybeParsed { + match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(uf(v)), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(pf(v)), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct MaybeParsedIter(pub(crate) MaybeParsed); + +impl Iterator for MaybeParsedIter { + type Item = MaybeParsed; + + fn next(&mut self) -> Option { + match &mut self.0 { + MaybeParsed::Unrecognized(i) => i.next().map(MaybeParsed::Unrecognized), + MaybeParsed::Parsed(i) => i.next().map(MaybeParsed::Parsed), + } + } + + fn size_hint(&self) -> (usize, Option) { + match &self.0 { + MaybeParsed::Unrecognized(i) => i.size_hint(), + MaybeParsed::Parsed(i) => i.size_hint(), + } + } +} + +impl ExactSizeIterator for MaybeParsedIter {} + +impl DoubleEndedIterator for MaybeParsedIter { + fn next_back(&mut self) -> Option { + match &mut self.0 { + MaybeParsed::Unrecognized(i) => i.next_back().map(MaybeParsed::Unrecognized), + MaybeParsed::Parsed(i) => i.next_back().map(MaybeParsed::Parsed), + } + } +} + +impl IntoIterator for MaybeParsed { + type Item = MaybeParsed; + type IntoIter = MaybeParsedIter; + + fn into_iter(self) -> Self::IntoIter { + MaybeParsedIter(match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.into_iter()), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.into_iter()), + }) + } +} + +impl MaybeParsed { + pub(crate) fn iter<'a>( + &'a self, + ) -> MaybeParsedIter<<&'a P as IntoIterator>::IntoIter, <&'a U as IntoIterator>::IntoIter> + where + &'a P: IntoIterator, + &'a U: IntoIterator, + { + self.into_iter() + } + pub(crate) fn iter_mut<'a>( + &'a mut self, + ) -> MaybeParsedIter<<&'a mut P as IntoIterator>::IntoIter, <&'a mut U as IntoIterator>::IntoIter> + where + &'a mut P: IntoIterator, + &'a mut U: IntoIterator, + { + self.into_iter() + } +} + +impl<'a, P, U> IntoIterator for &'a MaybeParsed +where + &'a P: IntoIterator, + &'a U: IntoIterator, +{ + type Item = MaybeParsed<<&'a P as IntoIterator>::Item, <&'a U as IntoIterator>::Item>; + type IntoIter = + MaybeParsedIter<<&'a P as IntoIterator>::IntoIter, <&'a U as IntoIterator>::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + MaybeParsedIter(match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.into_iter()), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.into_iter()), + }) + } +} + +impl<'a, P, U> IntoIterator for &'a mut MaybeParsed +where + &'a mut P: IntoIterator, + &'a mut U: IntoIterator, +{ + type Item = MaybeParsed<<&'a mut P as IntoIterator>::Item, <&'a mut U as IntoIterator>::Item>; + type IntoIter = MaybeParsedIter< + <&'a mut P as IntoIterator>::IntoIter, + <&'a mut U as IntoIterator>::IntoIter, + >; + + fn into_iter(self) -> Self::IntoIter { + MaybeParsedIter(match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.into_iter()), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.into_iter()), + }) + } +} + +impl From> for Generics { + fn from(value: MaybeParsed) -> Self { + match value { + MaybeParsed::Unrecognized(value) => value, + MaybeParsed::Parsed(value) => value.into(), + } + } +} + +impl From<&'_ MaybeParsed> for Generics { + fn from(value: &MaybeParsed) -> Self { + match value { + MaybeParsed::Unrecognized(value) => value.clone(), + MaybeParsed::Parsed(value) => value.into(), + } + } +} + +impl ToTokens for MaybeParsed { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + MaybeParsed::Unrecognized(v) => v.to_tokens(tokens), + MaybeParsed::Parsed(v) => v.to_tokens(tokens), + } + } + + fn to_token_stream(&self) -> TokenStream { + match self { + MaybeParsed::Unrecognized(v) => v.to_token_stream(), + MaybeParsed::Parsed(v) => v.to_token_stream(), + } + } + + fn into_token_stream(self) -> TokenStream { + match self { + MaybeParsed::Unrecognized(v) => v.into_token_stream(), + MaybeParsed::Parsed(v) => v.into_token_stream(), + } + } +} + +impl AsTurbofish for MaybeParsed { + type Turbofish<'a> = MaybeParsed, U::Turbofish<'a>> + where + Self: 'a; + + fn as_turbofish(&self) -> Self::Turbofish<'_> { + match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.as_turbofish()), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.as_turbofish()), + } + } +} + +impl SplitForImpl for MaybeParsed { + type ImplGenerics<'a> = MaybeParsed, U::ImplGenerics<'a>> + where + Self: 'a; + type TypeGenerics<'a> = MaybeParsed, U::TypeGenerics<'a>> + where + Self: 'a; + type WhereClause<'a> = MaybeParsed, U::WhereClause<'a>> + where + Self: 'a; + + fn split_for_impl( + &self, + ) -> ( + Self::ImplGenerics<'_>, + Self::TypeGenerics<'_>, + Self::WhereClause<'_>, + ) { + match self { + MaybeParsed::Unrecognized(v) => { + let (i, t, w) = v.split_for_impl(); + ( + MaybeParsed::Unrecognized(i), + MaybeParsed::Unrecognized(t), + MaybeParsed::Unrecognized(w), + ) + } + MaybeParsed::Parsed(v) => { + let (i, t, w) = v.split_for_impl(); + ( + MaybeParsed::Parsed(i), + MaybeParsed::Parsed(t), + MaybeParsed::Parsed(w), + ) + } + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedField { + pub(crate) attrs: Vec, + pub(crate) vis: Visibility, + pub(crate) ident: Option, + pub(crate) colon_token: Option, + pub(crate) ty: ParsedType, +} + +impl ParseTypes for ParsedField { + fn parse_types(input: &mut Field, parser: &mut TypesParser<'_>) -> Result { + let Field { + attrs, + vis, + mutability, + ident, + colon_token, + ty, + } = input; + if !matches!(mutability, FieldMutability::None) { + // FIXME: use mutability as the spanned tokens, + // blocked on https://github.com/dtolnay/syn/issues/1717 + parser + .errors() + .error(&ident, "field mutability is not supported"); + *mutability = FieldMutability::None; + } + *mutability = FieldMutability::None; + Ok(Self { + attrs: attrs.clone(), + vis: vis.clone(), + ident: ident.clone(), + colon_token: *colon_token, + ty: parser.parse(ty)?, + }) + } +} + +impl From for Field { + fn from(value: ParsedField) -> Self { + Self { + attrs: value.attrs, + vis: value.vis, + mutability: FieldMutability::None, + ident: value.ident, + colon_token: value.colon_token, + ty: value.ty.into(), + } + } +} + +impl MaybeParsed { + pub(crate) fn ident(&self) -> &Option { + match self { + MaybeParsed::Unrecognized(v) => &v.ident, + MaybeParsed::Parsed(v) => &v.ident, + } + } +} + +impl<'a> MaybeParsed<&'a ParsedField, &'a Field> { + pub(crate) fn ident(self) -> &'a Option { + match self { + MaybeParsed::Unrecognized(v) => &v.ident, + MaybeParsed::Parsed(v) => &v.ident, + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedFieldsNamed { + pub(crate) brace_token: Brace, + pub(crate) named: Punctuated, +} + +impl ParseTypes for ParsedFieldsNamed { + fn parse_types( + input: &mut FieldsNamed, + parser: &mut TypesParser<'_>, + ) -> Result { + let FieldsNamed { brace_token, named } = input; + Ok(Self { + brace_token: *brace_token, + named: parser.parse(named)?, + }) + } +} + +impl From for FieldsNamed { + fn from(value: ParsedFieldsNamed) -> Self { + Self { + brace_token: value.brace_token, + named: value + .named + .into_pairs() + .map_pair_value(Into::into) + .collect(), + } + } +} + +impl MaybeParsed { + pub(crate) fn named( + &self, + ) -> MaybeParsed<&Punctuated, &Punctuated> { + self.as_ref().map(|v| &v.named, |v| &v.named) + } +} + +impl From> for FieldsNamed { + fn from(value: MaybeParsed) -> Self { + match value { + MaybeParsed::Unrecognized(value) => value, + MaybeParsed::Parsed(value) => value.into(), + } + } +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 920a27b..49f2e63 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -3,7 +3,10 @@ #![cfg_attr(test, recursion_limit = "512")] use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use std::io::{ErrorKind, Write}; +use std::{ + convert::Infallible, + io::{ErrorKind, Write}, +}; use syn::{ bracketed, parenthesized, parse::{Parse, ParseStream, Parser}, @@ -13,6 +16,9 @@ use syn::{ }; mod fold; +mod hdl_bundle; +mod hdl_enum; +mod hdl_type_common; mod module; mod value_derive_common; mod value_derive_enum; @@ -43,6 +49,7 @@ mod kw { custom_keyword!(clock_domain); custom_keyword!(connect_inexact); + custom_keyword!(custom_bounds); custom_keyword!(flip); custom_keyword!(hdl); custom_keyword!(input); @@ -52,6 +59,7 @@ mod kw { custom_keyword!(memory_array); custom_keyword!(memory_with_init); custom_keyword!(no_reset); + custom_keyword!(no_static); custom_keyword!(outline_generated); custom_keyword!(output); custom_keyword!(reg_builder); @@ -460,6 +468,15 @@ impl Errors { self.push(Error::new_spanned(tokens, message)); self } + pub(crate) fn fatal_error( + mut self, + tokens: impl ToTokens, + message: impl std::fmt::Display, + ) -> syn::Result { + self.push(Error::new_spanned(tokens, message)); + self.finish()?; + unreachable!() + } pub(crate) fn ok(&mut self, v: syn::Result) -> Option { match v { Ok(v) => Some(v), @@ -519,6 +536,26 @@ macro_rules! impl_extra_traits_for_options { ) => { 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 { + 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 { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let _ = f; @@ -554,6 +591,66 @@ pub(crate) use impl_extra_traits_for_options; macro_rules! options { ( #[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>(&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>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } + } + + impl Extend<$options_name> for $options_name { + fn extend>(&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>(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_vis:vis enum $option_enum_name:ident { $($Variant:ident($key:ident $(, $value:ty)?),)* @@ -567,8 +664,11 @@ macro_rules! options { } #[derive(Clone, Debug, Default)] + #[allow(non_snake_case)] $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! { @@ -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 { + $( + if let Some(value) = self.0.$key.take() { + return Some($option_enum_name::$Variant(value)); + } + )* + None + } + + #[allow(unused_mut, unused_variables)] + fn fold 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 { fn parse(input: syn::parse::ParseStream) -> syn::Result { #![allow(unused_mut, unused_variables, unreachable_code)] @@ -585,7 +722,7 @@ macro_rules! options { let old_input = input.fork(); match input.parse::<$option_enum_name>()? { $($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"))); } })* @@ -593,7 +730,7 @@ macro_rules! options { if input.is_empty() { break; } - input.parse::()?; + input.parse::<$Punct>()?; } Ok(retval) } @@ -602,7 +739,7 @@ macro_rules! options { impl quote::ToTokens for $options_name { #[allow(unused_mut, unused_variables, unused_assignments)] fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let mut separator: Option = None; + let mut separator: Option<$Punct> = None; $(if let Some(v) = &self.$key { separator.to_tokens(tokens); 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 { )), } } + +pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result { + let item = syn::parse2::(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", + )), + } +} diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index d2b3e77..1c664c8 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -1290,7 +1290,10 @@ impl Visitor { memory, paren, ty_expr, - } => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())), + } => ( + paren, + unwrap_or_static_type(ty_expr.as_ref(), memory.span()), + ), MemoryFn::MemoryArray { memory_array, paren, diff --git a/crates/fayalite-proc-macros/src/lib.rs b/crates/fayalite-proc-macros/src/lib.rs index 7d0c65d..8335e78 100644 --- a/crates/fayalite-proc-macros/src/lib.rs +++ b/crates/fayalite-proc-macros/src/lib.rs @@ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { Err(err) => err.into_compile_error().into(), } } + +// intentionally not documented here, see `fayalite::hdl` for docs +#[proc_macro_attribute] +pub fn hdl( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) { + Ok(retval) => retval.into(), + Err(err) => err.into_compile_error().into(), + } +} diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 3763cae..28de9eb 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -1,671 +1,185 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ - bundle::{BundleType, BundleValue}, - expr::{ - ops::{ArrayIndex, ArrayLiteral, ExprIndex}, - Expr, ToExpr, - }, - intern::{Intern, Interned, InternedCompare, Memoize}, - module::{ - transform::visit::{Fold, Folder, Visit, Visitor}, - ModuleBuilder, NormalModule, - }, + expr::{ops::ArrayIndex, Expr, ToExpr}, + int::{DynSize, KnownSize, Size}, + intern::{Intern, Interned}, + module::{ModuleBuilder, NormalModule}, source_location::SourceLocation, ty::{ - CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, - DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType, - StaticValue, Type, TypeEnum, Value, ValueEnum, + CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, }, - util::{ConstBool, GenericConstBool, MakeMutSlice}, -}; -use bitvec::{slice::BitSlice, vec::BitVec}; -use std::{ - any::Any, - borrow::{Borrow, BorrowMut}, - fmt, - hash::Hash, - marker::PhantomData, - ops::IndexMut, - sync::Arc, + util::ConstUsize, }; +use std::ops::Index; -mod sealed { - pub trait Sealed {} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct ArrayImpl { + element: T, + len: Len::SizeType, + type_properties: TypeProperties, } -pub trait ValueArrayOrSlice: - sealed::Sealed - + BorrowMut<[::Element]> - + AsRef<[::Element]> - + AsMut<[::Element]> - + Hash - + fmt::Debug - + Eq - + Send - + Sync - + 'static - + IndexMut::Element> - + ToOwned - + InternedCompare -{ - type Element: Value::ElementType>; - type ElementType: Type::Element>; - type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync; - type Match: 'static - + Clone - + Eq - + fmt::Debug - + Hash - + Send - + Sync - + BorrowMut<[Expr]>; - type MaskVA: ValueArrayOrSlice< - Element = ::MaskValue, - ElementType = ::MaskType, - LenType = Self::LenType, - MaskVA = Self::MaskVA, - > + ?Sized; - type IsStaticLen: GenericConstBool; - const FIXED_LEN_TYPE: Option; - fn make_match(array: Expr>) -> 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; - fn len_type(&self) -> Self::LenType; - fn len(&self) -> usize; - fn is_empty(&self) -> bool; - fn iter(&self) -> std::slice::Iter { - Borrow::<[_]>::borrow(self).iter() - } - fn clone_to_arc(&self) -> Arc; - fn arc_make_mut(v: &mut Arc) -> &mut Self; - fn arc_to_arc_slice(self: Arc) -> Arc<[Self::Element]>; +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ArrayType { + impl_: Interned>, } -impl sealed::Sealed for [T] {} - -impl ValueArrayOrSlice for [V] -where - V::Type: Type, -{ - type Element = V; - type ElementType = V::Type; - type LenType = usize; - type Match = Box<[Expr]>; - type MaskVA = [::MaskValue]; - type IsStaticLen = ConstBool; - const FIXED_LEN_TYPE: Option = None; - - fn make_match(array: Expr>) -> Self::Match { - (0..array.canonical_type().len()) - .map(|index| ArrayIndex::::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 { - 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 { - Arc::from(self) - } - - fn arc_make_mut(v: &mut Arc) -> &mut Self { - MakeMutSlice::make_mut_slice(v) - } - - fn arc_to_arc_slice(self: Arc) -> Arc<[Self::Element]> { - self +impl std::fmt::Debug for ArrayType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ArrayImpl { + element, len: _, .. + } = *self.impl_; + write!(f, "Array<{element:?}, {}>", self.len()) } } -impl sealed::Sealed for [T; N] {} +pub type Array< + T = CanonicalType, + const LEN: usize = { ::VALUE }, +> = ArrayType>; -impl ValueArrayOrSlice for [V; N] -where - V::Type: Type, -{ - type Element = V; - type ElementType = V::Type; - type LenType = (); - type Match = [Expr; N]; - type MaskVA = [::MaskValue; N]; - type IsStaticLen = ConstBool; - const FIXED_LEN_TYPE: Option = Some(()); +#[allow(non_upper_case_globals)] +pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics; - fn make_match(array: Expr>) -> Self::Match { - std::array::from_fn(|index| { - ArrayIndex::::new_unchecked(array.canonical(), index).to_expr() - }) - } - - fn len_from_len_type(_v: Self::LenType) -> usize { - N - } - - fn try_len_type_from_len(v: usize) -> Result { - 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 { - Arc::new(self.clone()) - } - - fn arc_make_mut(v: &mut Arc) -> &mut Self { - Arc::make_mut(v) - } - - fn arc_to_arc_slice(self: Arc) -> Arc<[Self::Element]> { - self - } -} - -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct ArrayType { - element: VA::ElementType, - len: VA::LenType, - bit_width: usize, -} - -pub trait ArrayTypeTrait: - Type< - CanonicalType = ArrayType<[DynCanonicalValue]>, - Value = Array<::ValueArrayOrSlice>, - CanonicalValue = Array<[DynCanonicalValue]>, - MaskType = ArrayType< - <::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA, - >, - > + From::ValueArrayOrSlice>> - + Into::ValueArrayOrSlice>> - + BorrowMut::ValueArrayOrSlice>> - + sealed::Sealed - + Connect -{ - type ValueArrayOrSlice: ValueArrayOrSlice - + ?Sized; - type Element: Value; - type ElementType: Type; -} - -impl sealed::Sealed for ArrayType {} - -impl ArrayTypeTrait for ArrayType { - type ValueArrayOrSlice = VA; - type Element = VA::Element; - type ElementType = VA::ElementType; -} - -impl Clone for ArrayType { - fn clone(&self) -> Self { - Self { - element: self.element.clone(), - len: self.len, - bit_width: self.bit_width, - } - } -} - -impl Copy for ArrayType where VA::ElementType: Copy {} - -impl ArrayType { - pub fn element(&self) -> &VA::ElementType { - &self.element - } - pub fn len(&self) -> usize { - VA::len_from_len_type(self.len) - } - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - pub fn bit_width(&self) -> usize { - self.bit_width - } - pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { - ArrayType { - len: self.len(), - element: self.element, - bit_width: self.bit_width, - } - } - #[track_caller] - pub fn new_with_len(element: VA::ElementType, len: usize) -> Self { - Self::new_with_len_type( - element, - VA::try_len_type_from_len(len).expect("length should match"), - ) - } - #[track_caller] - pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self { - let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else { - panic!("array is too big: bit-width overflowed"); +impl ArrayType { + pub fn new(element: T, len: Len::SizeType) -> Self { + let element_props = element.canonical().type_properties(); + let type_properties = TypeProperties { + is_passive: element_props.is_passive, + is_storable: element_props.is_storable, + is_castable_from_bits: element_props.is_castable_from_bits, + bit_width: element_props + .bit_width + .checked_mul(Len::as_usize(len)) + .expect("array too big"), }; - ArrayType { - element, - len, - bit_width, + Self { + impl_: ArrayImpl { + element, + len, + type_properties, + } + .intern_sized(), } } -} - -impl Fold for ArrayType -where - VA::ElementType: Fold, -{ - fn fold(self, state: &mut State) -> Result { - state.fold_array_type(self) + pub fn element(&self) -> &T { + &self.impl_.element } - fn default_fold(self, state: &mut State) -> Result { - Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) + pub fn len(self) -> usize { + Len::as_usize(self.impl_.len) + } + pub fn type_properties(self) -> TypeProperties { + self.impl_.type_properties + } + pub fn as_dyn_array(self) -> Array { + Array::new_dyn(self.element().canonical(), self.len()) } } -impl Visit for ArrayType -where - VA::ElementType: Visit, -{ - 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 ArrayType { + pub fn new_static(element: T) -> Self { + Self::new(element, Len::SizeType::default()) } } -impl>, const N: usize> ArrayType<[V; N]> { - pub fn new_array(element: V::Type) -> Self { - ArrayType::new_with_len_type(element, ()) - } -} - -impl StaticType for ArrayType<[V; N]> { +impl StaticType for ArrayType { fn static_type() -> Self { - Self::new_array(StaticType::static_type()) + Self::new_static(T::static_type()) } } -impl>> ArrayType<[V]> { - pub fn new_slice(element: V::Type, len: usize) -> Self { - ArrayType::new_with_len_type(element, len) +impl Array { + pub fn new_dyn(element: T, len: usize) -> Self { + Self::new(element, len) } } -impl Type for ArrayType { - type CanonicalType = ArrayType<[DynCanonicalValue]>; - type Value = Array; - type CanonicalValue = Array<[DynCanonicalValue]>; - type MaskType = ArrayType; - type MaskValue = Array; - type MatchVariant = VA::Match; +impl Type for ArrayType { + type MaskType = ArrayType; + type MatchVariant = Len::ArrayMatch; type MatchActiveScope = (); - type MatchVariantAndInactiveScope = MatchVariantWithoutScope; + type MatchVariantAndInactiveScope = MatchVariantWithoutScope>; type MatchVariantsIter = std::iter::Once; - fn match_variants( - this: Expr, - module_builder: &mut ModuleBuilder, + fn match_variants( + this: Expr, + module_builder: &mut ModuleBuilder, source_location: SourceLocation, - ) -> Self::MatchVariantsIter - where - IO::Type: BundleType, - { + ) -> Self::MatchVariantsIter { + let base = Expr::as_dyn_array(this); + let base_ty = Expr::ty(base); let _ = module_builder; let _ = source_location; - std::iter::once(MatchVariantWithoutScope(VA::make_match(this))) + let retval = Vec::from_iter( + (0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()), + ); + std::iter::once(MatchVariantWithoutScope( + Len::ArrayMatch::::try_from(retval) + .ok() + .expect("unreachable"), + )) } fn mask_type(&self) -> Self::MaskType { - #[derive(Clone, Hash, Eq, PartialEq)] - struct ArrayMaskTypeMemoize(PhantomData); - impl Copy for ArrayMaskTypeMemoize {} - impl Memoize for ArrayMaskTypeMemoize { - type Input = ArrayType; - type InputOwned = ArrayType; - type Output = as Type>::MaskType; - - fn inner(self, input: &Self::Input) -> Self::Output { - ArrayType::new_with_len_type(input.element.mask_type(), input.len) - } - } - ArrayMaskTypeMemoize::(PhantomData).get(self) + ArrayType::new(self.element().mask_type(), self.impl_.len) } - fn canonical(&self) -> Self::CanonicalType { - ArrayType { - element: self.element.canonical_dyn(), - len: self.len(), - bit_width: self.bit_width, - } + fn canonical(&self) -> CanonicalType { + CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len())) } - fn source_location(&self) -> SourceLocation { + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Array(array) = canonical_type else { + panic!("expected array"); + }; + Self::new( + T::from_canonical(*array.element()), + Len::from_usize(array.len()), + ) + } + fn source_location() -> SourceLocation { SourceLocation::builtin() } +} - fn type_enum(&self) -> TypeEnum { - TypeEnum::ArrayType(self.canonical()) - } - - fn from_canonical_type(t: Self::CanonicalType) -> Self { - Self { - element: VA::ElementType::from_dyn_canonical_type(t.element), - len: VA::try_len_type_from_len(t.len).expect("length should match"), - bit_width: t.bit_width, - } - } - - fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { - Some(::downcast_ref::>( - this, - )?) +impl TypeWithDeref for ArrayType { + fn expr_deref(this: &Expr) -> &Self::MatchVariant { + let base = Expr::as_dyn_array(*this); + let base_ty = Expr::ty(base); + let retval = Vec::from_iter( + (0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()), + ); + Interned::<_>::into_inner(Intern::intern_sized( + Len::ArrayMatch::::try_from(retval) + .ok() + .expect("unreachable"), + )) } } -impl Connect> - for ArrayType -{ -} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct ArrayWithoutGenerics; -impl CanonicalType for ArrayType<[DynCanonicalValue]> { - const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; -} +impl Index for ArrayWithoutGenerics { + type Output = ArrayWithoutLen; -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct Array { - element_ty: VA::ElementType, - value: Arc, -} - -impl Clone for Array { - fn clone(&self) -> Self { - Self { - element_ty: self.element_ty.clone(), - value: self.value.clone(), - } + fn index(&self, element: T) -> &Self::Output { + Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) } } -impl ToExpr for Array { - type Type = ArrayType; - - fn ty(&self) -> Self::Type { - ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::from_value(self) - } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ArrayWithoutLen { + element: T, } -impl Value for Array { - fn to_canonical(&self) -> ::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 { - #[derive(Hash, Eq, PartialEq)] - struct ArrayToBitsMemoize(PhantomData); - impl Clone for ArrayToBitsMemoize { - fn clone(&self) -> Self { - *self - } - } - impl Copy for ArrayToBitsMemoize {} - impl Memoize for ArrayToBitsMemoize { - type Input = Array; - type InputOwned = Array; - type Output = Interned; +impl Index for ArrayWithoutLen { + type Output = Array; - 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::(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 { - Value::to_bits_impl(this) - } -} - -impl Array { - 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 { - &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) -> 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>> From for Array -where - VA::Element: StaticValue, -{ - fn from(value: T) -> Self { - Self::new(StaticType::static_type(), value.into()) - } -} - -impl, T: 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<::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, T: StaticType> ToExpr for Vec { - type Type = ArrayType<[T::Value]>; - - fn ty(&self) -> Self::Type { - <[E]>::ty(self) - } - - fn to_expr(&self) -> Expr<::Value> { - <[E]>::to_expr(self) - } -} - -impl, T: 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<::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 { - array: Arc, - indexes: std::ops::Range, -} - -impl Iterator for ArrayIntoIter { - type Item = VA::Element; - - fn next(&mut self) -> Option { - Some(self.array[self.indexes.next()?].clone()) - } - - fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() - } -} - -impl std::iter::FusedIterator for ArrayIntoIter {} - -impl ExactSizeIterator for ArrayIntoIter {} - -impl DoubleEndedIterator for ArrayIntoIter { - fn next_back(&mut self) -> Option { - Some(self.array[self.indexes.next_back()?].clone()) - } -} - -impl Array { - pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> { - self.value.iter() - } -} - -impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array { - type Item = &'a VA::Element; - type IntoIter = std::slice::Iter<'a, VA::Element>; - - fn into_iter(self) -> Self::IntoIter { - self.value.iter() - } -} - -impl IntoIterator for Array { - type Item = VA::Element; - type IntoIter = ArrayIntoIter; - - fn into_iter(self) -> Self::IntoIter { - ArrayIntoIter { - indexes: 0..self.len(), - array: self.value, - } - } -} - -#[derive(Clone, Debug)] -pub struct ArrayExprIter { - array: Expr>, - indexes: std::ops::Range, -} - -impl Iterator for ArrayExprIter { - type Item = Expr; - - fn next(&mut self) -> Option { - Some(ExprIndex::expr_index(self.array, self.indexes.next()?)) - } - - fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() - } -} - -impl std::iter::FusedIterator for ArrayExprIter {} - -impl ExactSizeIterator for ArrayExprIter {} - -impl DoubleEndedIterator for ArrayExprIter { - fn next_back(&mut self) -> Option { - Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?)) - } -} - -impl IntoIterator for Expr> { - type Item = Expr; - type IntoIter = ArrayExprIter; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl IntoIterator for &'_ Expr> { - type Item = Expr; - type IntoIter = ArrayExprIter; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl Expr> { - 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 { - ArrayExprIter { - indexes: 0..self.len(), - array: self, - } + fn index(&self, len: usize) -> &Self::Output { + Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len))) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 2d43031..909d6c1 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -1,806 +1,351 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ expr::{ops::BundleLiteral, Expr, ToExpr}, - intern::{ - Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId, - }, + intern::{Intern, Interned}, module::{ModuleBuilder, NormalModule}, source_location::SourceLocation, ty::{ - CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, - DynCanonicalValue, DynType, MatchVariantWithoutScope, StaticType, Type, TypeEnum, - TypeWithDeref, Value, ValueEnum, + impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, + TypeProperties, TypeWithDeref, }, }; -use bitvec::{slice::BitSlice, vec::BitVec}; use hashbrown::HashMap; -use std::{ - fmt, - hash::{Hash, Hasher}, - marker::PhantomData, - sync::Arc, -}; +use std::marker::PhantomData; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct FieldType { +pub struct BundleField { pub name: Interned, pub flipped: bool, - pub ty: T, -} - -pub struct FmtDebugInStruct<'a, T> { - field: &'a FieldType, - field_offset: usize, -} - -impl fmt::Debug for FmtDebugInStruct<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - field: - &FieldType { - name, - flipped, - ref ty, - }, - field_offset, - } = *self; - if flipped { - write!(f, "#[hdl(flip)] ")?; - } - if f.alternate() { - writeln!(f, "/* offset = {field_offset} */")?; - } - write!(f, "{name}: ")?; - ty.fmt(f) - } -} - -impl fmt::Display for FmtDebugInStruct<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -impl FieldType { - pub fn map_ty U>(self, f: F) -> FieldType { - let Self { name, flipped, ty } = self; - FieldType { - name, - flipped, - ty: f(ty), - } - } - pub fn as_ref_ty(&self) -> FieldType<&T> { - FieldType { - name: self.name, - flipped: self.flipped, - ty: &self.ty, - } - } - pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> { - FmtDebugInStruct { - field: self, - field_offset, - } - } -} - -impl FieldType { - pub fn canonical(&self) -> FieldType { - FieldType { - name: self.name, - flipped: self.flipped, - ty: self.ty.canonical(), - } - } - pub fn to_dyn(&self) -> FieldType> { - FieldType { - name: self.name, - flipped: self.flipped, - ty: self.ty.to_dyn(), - } - } - pub fn canonical_dyn(&self) -> FieldType> { - FieldType { - name: self.name, - flipped: self.flipped, - ty: self.ty.canonical_dyn(), - } - } -} - -impl FieldType> { - pub fn from_canonical_type_helper( - self, - expected_name: &str, - expected_flipped: bool, - ) -> T { - assert_eq!(&*self.name, expected_name, "field name doesn't match"); - assert_eq!( - self.flipped, expected_flipped, - "field {expected_name} orientation (flipped or not) doesn't match" - ); - let ty = &*self.ty; - if let Ok(ty) = ::downcast(ty) { - return T::from_canonical_type(ty); - } - let type_name = std::any::type_name::(); - panic!("field {expected_name} type doesn't match, expected: {type_name:?}, got: {ty:?}"); - } + pub ty: CanonicalType, } #[derive(Clone, Eq)] -struct DynBundleTypeImpl { - fields: Interned<[FieldType>]>, +struct BundleImpl { + fields: Interned<[BundleField]>, name_indexes: HashMap, usize>, field_offsets: Interned<[usize]>, - is_passive: bool, - is_storable: bool, - is_castable_from_bits: bool, - bit_width: usize, + type_properties: TypeProperties, } -impl fmt::Debug for DynBundleTypeImpl { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DynBundleType ")?; - f.debug_set() - .entries( - self.fields - .iter() - .enumerate() - .map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])), - ) - .finish() +impl std::hash::Hash for BundleImpl { + fn hash(&self, state: &mut H) { + self.fields.hash(state); } } -impl PartialEq for DynBundleTypeImpl { +impl PartialEq for BundleImpl { fn eq(&self, other: &Self) -> bool { self.fields == other.fields } } -impl Hash for DynBundleTypeImpl { - fn hash(&self, state: &mut H) { - self.fields.hash(state); +impl std::fmt::Debug for BundleImpl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Bundle") + .field("fields", &self.fields) + .field("name_indexes", &self.name_indexes) + .field("field_offsets", &self.field_offsets) + .field("type_properties", &self.type_properties) + .finish() } } -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub struct DynBundleType(Interned); +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Bundle(Interned); -impl fmt::Debug for DynBundleType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl std::fmt::Debug for Bundle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } -impl DynBundleType { - pub fn new(fields: Interned<[FieldType>]>) -> Self { - let is_passive = fields - .iter() - .all(|field| !field.flipped && field.ty.is_passive()); - let is_storable = fields - .iter() - .all(|field| !field.flipped && field.ty.is_storable()); - let is_castable_from_bits = fields - .iter() - .all(|field| !field.flipped && field.ty.is_castable_from_bits()); +impl Bundle { + #[track_caller] + pub fn new(fields: Interned<[BundleField]>) -> Self { let mut name_indexes = HashMap::with_capacity(fields.len()); let mut field_offsets = Vec::with_capacity(fields.len()); - let mut bit_width = 0usize; - for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() { + let mut type_properties = TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: 0, + }; + for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { if let Some(old_index) = name_indexes.insert(name, index) { panic!("duplicate field name {name:?}: at both index {old_index} and {index}"); } - field_offsets.push(bit_width); - bit_width = bit_width + field_offsets.push(type_properties.bit_width); + let bit_width = type_properties + .bit_width .checked_add(ty.bit_width()) .unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed")); + type_properties = if flipped { + TypeProperties { + is_passive: false, + is_storable: false, + is_castable_from_bits: false, + bit_width, + } + } else { + let field_props = ty.type_properties(); + TypeProperties { + is_passive: type_properties.is_passive & field_props.is_passive, + is_storable: type_properties.is_storable & field_props.is_storable, + is_castable_from_bits: type_properties.is_castable_from_bits + & field_props.is_castable_from_bits, + bit_width, + } + }; } - Self( - DynBundleTypeImpl { - fields, - name_indexes, - field_offsets: Intern::intern_owned(field_offsets), - is_passive, - is_storable, - is_castable_from_bits, - bit_width, - } - .intern_sized(), - ) - } - pub fn is_passive(self) -> bool { - self.0.is_passive - } - 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 + Self(Intern::intern_sized(BundleImpl { + fields, + name_indexes, + field_offsets: Intern::intern_owned(field_offsets), + type_properties, + })) } pub fn name_indexes(&self) -> &HashMap, usize> { &self.0.name_indexes } - pub fn field_by_name( - &self, - name: Interned, - ) -> Option>> { + pub fn field_by_name(&self, name: Interned) -> Option { Some(self.0.fields[*self.0.name_indexes.get(&name)?]) } pub fn field_offsets(self) -> Interned<[usize]> { self.0.field_offsets } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct DynBundle { - ty: DynBundleType, - fields: Arc<[DynCanonicalValue]>, -} - -impl DynBundle { - pub fn new(ty: DynBundleType, fields: Arc<[DynCanonicalValue]>) -> Self { - assert_eq!( - ty.fields().len(), - fields.len(), - "field values don't match type" - ); - for (field_ty, field) in ty.fields().iter().zip(fields.iter()) { - assert_eq!(field_ty.ty, field.ty(), "field value doesn't match type"); - } - DynBundle { ty, fields } - } - pub fn fields(&self) -> &Arc<[DynCanonicalValue]> { - &self.fields + pub fn type_properties(self) -> TypeProperties { + self.0.type_properties } } -pub trait TypeHintTrait: Send + Sync + fmt::Debug + SupportsPtrEqWithTypeId { - fn matches(&self, ty: &dyn DynType) -> Result<(), String>; -} - -impl InternedCompare for dyn TypeHintTrait { - type InternedCompareKey = PtrEqWithTypeId; - fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { - Self::get_ptr_eq_with_type_id(this) - } - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey { - Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap()) - } -} - -pub struct TypeHint(PhantomData); - -impl TypeHint { - pub fn intern_dyn() -> Interned { - Interned::cast_unchecked( - Self(PhantomData).intern_sized(), - |v| -> &dyn TypeHintTrait { v }, - ) - } -} - -impl fmt::Debug for TypeHint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TypeHint<{}>", std::any::type_name::()) - } -} - -impl Hash for TypeHint { - fn hash(&self, _state: &mut H) {} -} - -impl Eq for TypeHint {} - -impl PartialEq for TypeHint { - fn eq(&self, _other: &Self) -> bool { - true - } -} - -impl Clone for TypeHint { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for TypeHint {} - -impl TypeHintTrait for TypeHint { - fn matches(&self, ty: &dyn DynType) -> Result<(), String> { - match ty.downcast::() { - Ok(_) => Ok(()), - Err(_) => Err(format!("can't cast {ty:?} to {self:?}")), - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub struct FieldsHint { - pub known_fields: Interned<[FieldType>]>, - pub more_fields: bool, -} - -impl FieldsHint { - pub fn new( - known_fields: impl IntoIterator>>, - more_fields: bool, - ) -> Self { - let known_fields = Intern::intern_owned(Vec::from_iter(known_fields)); - Self { - known_fields, - more_fields, - } - } - pub fn check_field(self, index: usize, field: FieldType<&dyn DynType>) -> Result<(), String> { - let Some(&known_field) = self.known_fields.get(index) else { - return if self.more_fields { - Ok(()) - } else { - Err(format!( - "too many fields: name={:?} index={index}", - field.name - )) - }; - }; - let FieldType { - name: known_name, - flipped: known_flipped, - ty: type_hint, - } = known_field; - let FieldType { name, flipped, ty } = field; - if name != known_name { - Err(format!( - "wrong field name {name:?}, expected {known_name:?}" - )) - } else if flipped != known_flipped { - Err(format!( - "wrong field direction: flipped={flipped:?}, expected flipped={known_flipped}" - )) - } else { - type_hint.matches(ty) - } - } -} - -pub trait BundleType: - Type + TypeWithDeref + Connect -where - Self::Value: BundleValue + ToExpr, -{ - type Builder; - fn builder() -> Self::Builder; - fn fields(&self) -> Interned<[FieldType>]>; - fn fields_hint() -> FieldsHint; -} - -pub trait BundleValue: Value -where - ::Type: BundleType, -{ - fn to_bits_impl(this: &Self) -> Interned { - #[derive(Hash, Eq, PartialEq)] - struct ToBitsMemoize(PhantomData); - impl Clone for ToBitsMemoize { - fn clone(&self) -> Self { - *self - } - } - impl Copy for ToBitsMemoize {} - impl>> Memoize for ToBitsMemoize { - type Input = T; - type InputOwned = T; - type Output = Interned; - - fn inner(self, input: &Self::Input) -> Self::Output { - let input = input.to_canonical(); - let mut bits = BitVec::with_capacity(input.ty.bit_width()); - for field in input.fields.iter() { - bits.extend_from_bitslice(&field.to_bits()); - } - Intern::intern_owned(bits) - } - } - ToBitsMemoize::(PhantomData).get(this) - } -} - -pub struct DynBundleMatch; - -impl Type for DynBundleType { - type CanonicalType = DynBundleType; - type Value = DynBundle; - type CanonicalValue = DynBundle; - type MaskType = DynBundleType; - type MaskValue = DynBundle; - type MatchVariant = DynBundleMatch; - type MatchActiveScope = (); - type MatchVariantAndInactiveScope = MatchVariantWithoutScope; - type MatchVariantsIter = std::iter::Once; - - fn match_variants( - this: Expr, - module_builder: &mut ModuleBuilder, - source_location: SourceLocation, - ) -> Self::MatchVariantsIter - where - IO::Type: BundleType, - { - let _ = this; - let _ = module_builder; - let _ = source_location; - std::iter::once(MatchVariantWithoutScope(DynBundleMatch)) - } - +impl Type for Bundle { + type MaskType = Bundle; + impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { - #[derive(Copy, Clone, Eq, PartialEq, Hash)] - struct Impl; - - impl Memoize for Impl { - type Input = DynBundleType; - type InputOwned = DynBundleType; - type Output = DynBundleType; - - fn inner(self, input: &Self::Input) -> Self::Output { - DynBundleType::new(Intern::intern_owned(Vec::from_iter( - input - .fields() - .iter() - .map(|&FieldType { name, flipped, ty }| FieldType { - name, - flipped, - ty: ty.mask_type().canonical(), - }), - ))) - } - } - Impl.get(self) + Self::new(Interned::from_iter(self.0.fields.into_iter().map( + |BundleField { name, flipped, ty }| BundleField { + name, + flipped, + ty: ty.mask_type(), + }, + ))) } - - fn canonical(&self) -> Self::CanonicalType { - *self + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(*self) } - - fn source_location(&self) -> SourceLocation { + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Bundle(bundle) = canonical_type else { + panic!("expected bundle"); + }; + bundle + } + fn source_location() -> SourceLocation { SourceLocation::builtin() } - - fn type_enum(&self) -> TypeEnum { - TypeEnum::BundleType(*self) - } - - fn from_canonical_type(t: Self::CanonicalType) -> Self { - t - } - - fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { - Some(this) - } } +pub trait BundleType: Type { + type Builder: Default; + type FilledBuilder: ToExpr; + fn fields(&self) -> Interned<[BundleField]>; +} + +#[derive(Default)] pub struct NoBuilder; -impl TypeWithDeref for DynBundleType { - fn expr_deref(this: &Expr) -> &Self::MatchVariant { - let _ = this; - &DynBundleMatch +pub struct Unfilled(PhantomData); + +impl Default for Unfilled { + fn default() -> Self { + Self(PhantomData) } } -impl Connect for DynBundleType {} - -impl BundleType for DynBundleType { +impl BundleType for Bundle { type Builder = NoBuilder; - - fn builder() -> Self::Builder { - NoBuilder - } - - fn fields(&self) -> Interned<[FieldType>]> { + type FilledBuilder = Expr; + fn fields(&self) -> Interned<[BundleField]> { self.0.fields } +} - fn fields_hint() -> FieldsHint { - FieldsHint { - known_fields: [][..].intern(), - more_fields: true, +#[derive(Default)] +pub struct TupleBuilder(T); + +macro_rules! impl_tuple_builder_fields { + ( + @impl + { } - } -} - -impl CanonicalType for DynBundleType { - const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::BundleType; -} - -impl ToExpr for DynBundle { - type Type = DynBundleType; - - fn ty(&self) -> Self::Type { - self.ty - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::from_value(self) - } -} - -impl Value for DynBundle { - fn to_canonical(&self) -> ::CanonicalValue { - self.clone() - } - fn to_bits_impl(this: &Self) -> Interned { - BundleValue::to_bits_impl(this) - } -} - -impl BundleValue for DynBundle {} - -impl CanonicalValue for DynBundle { - fn value_enum_impl(this: &Self) -> ValueEnum { - ValueEnum::Bundle(this.clone()) - } - fn to_bits_impl(this: &Self) -> Interned { - BundleValue::to_bits_impl(this) - } -} - -macro_rules! impl_tuple_builder { - ($builder:ident, [ - $(($before_Ts:ident $before_fields:ident $before_members:literal))* - ] [ - ($T:ident $field:ident $m:literal) - $(($after_Ts:ident $after_fields:ident $after_members:literal))* - ]) => { - impl_tuple_builder!($builder, [ - $(($before_Ts $before_fields $before_members))* - ($T $field $m) - ] [ - $(($after_Ts $after_fields $after_members))* - ]); - - impl $builder< - Phantom, - $($before_Ts,)* - (), - $($after_Ts,)* - > { - pub fn $field<$T: ToExpr>(self, $field: $T) -> $builder< - Phantom, - $($before_Ts,)* - Expr<<$T::Type as Type>::Value>, - $($after_Ts,)* - > { - let Self { - $($before_fields,)* - $field: _, - $($after_fields, )* - _phantom: _, - } = self; - let $field = $field.to_expr(); - $builder { - $($before_fields,)* - $field, - $($after_fields,)* - _phantom: PhantomData, - } + [ + $({ + #[type_var($head_type_var:ident)] + #[field($head_field:ident)] + #[var($head_var:ident)] + })* + ] + { + #[type_var($cur_type_var:ident)] + #[field($cur_field:ident)] + #[var($cur_var:ident)] + } + [ + $({ + #[type_var($tail_type_var:ident)] + #[field($tail_field:ident)] + #[var($tail_var:ident)] + })* + ] + ) => { + impl< + $($head_type_var,)* + $cur_type_var: Type, + $($tail_type_var,)* + > TupleBuilder<( + $($head_type_var,)* + Unfilled<$cur_type_var>, + $($tail_type_var,)* + )> + { + pub fn $cur_field(self, $cur_var: impl ToExpr) -> TupleBuilder<( + $($head_type_var,)* + Expr<$cur_type_var>, + $($tail_type_var,)* + )> + { + let ($($head_var,)* _, $($tail_var,)*) = self.0; + TupleBuilder(($($head_var,)* $cur_var.to_expr(), $($tail_var,)*)) } } }; - ($builder:ident, [$($before:tt)*] []) => {}; -} - -macro_rules! into_unit { - ($($tt:tt)*) => { - () + ($global:tt [$($head:tt)*] $cur:tt [$next:tt $($tail:tt)*]) => { + impl_tuple_builder_fields!(@impl $global [$($head)*] $cur [$next $($tail)*]); + impl_tuple_builder_fields!($global [$($head)* $cur] $next [$($tail)*]); }; + ($global:tt [$($head:tt)*] $cur:tt []) => { + impl_tuple_builder_fields!(@impl $global [$($head)*] $cur []); + }; + ($global:tt [$cur:tt $($tail:tt)*]) => { + impl_tuple_builder_fields!($global [] $cur [$($tail)*]); + }; + ($global:tt []) => {}; } -macro_rules! impl_tuple { - ($builder:ident, $(($T:ident $T2:ident $field:ident $m:tt)),*) => { - pub struct $builder { - $($field: $T,)* - _phantom: PhantomData, +macro_rules! impl_tuples { + ([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => { + impl_tuple_builder_fields! { + {} + [$({ + #[type_var($T)] + #[field($field)] + #[var($var)] + })*] } - - impl_tuple_builder!($builder, [] [$(($T $field $m))*]); - - impl<$($T: Value),*> $builder<($($T,)*), $(Expr<$T>,)*> - where - $($T::Type: Type,)* - { - pub fn build(self) -> Expr<($($T,)*)> { - let Self { - $($field,)* - _phantom: _, - } = self; - BundleLiteral::new_unchecked( - [$($field.to_canonical_dyn()),*][..].intern(), - ($($field.ty(),)*), - ).to_expr() - } - } - - impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { - type Type = ($($T::Type,)*); - - #[allow(clippy::unused_unit)] - fn ty(&self) -> Self::Type { - let ($($field,)*) = self; - ($($field.ty(),)*) - } - - fn to_expr(&self) -> Expr<::Value> { - let ($($field,)*) = self; - $(let $field = $field.to_expr();)* - BundleLiteral::new_unchecked( - [$($field.to_canonical_dyn()),*][..].intern(), - ($($field.ty(),)*), - ).to_expr() - } - } - - impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*) - where - $($T: Connect<$T2>,)* - { - } - - impl<$($T: Type,)*> Type for ($($T,)*) - where - $($T::Value: Value,)* - { - type CanonicalType = DynBundleType; - type Value = ($($T::Value,)*); - type CanonicalValue = DynBundle; + impl<$($T: Type,)*> Type for ($($T,)*) { type MaskType = ($($T::MaskType,)*); - type MaskValue = ($($T::MaskValue,)*); - type MatchVariant = ($(Expr<$T::Value>,)*); + type MatchVariant = ($(Expr<$T>,)*); type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; type MatchVariantsIter = std::iter::Once; - - fn match_variants( - this: Expr, - module_builder: &mut ModuleBuilder, + fn match_variants( + this: Expr, + module_builder: &mut ModuleBuilder, source_location: SourceLocation, - ) -> Self::MatchVariantsIter - where - IO::Type: BundleType, - { + ) -> Self::MatchVariantsIter { let _ = this; let _ = module_builder; let _ = source_location; - std::iter::once(MatchVariantWithoutScope(($(this.field(stringify!($m)),)*))) + std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*))) } - - #[allow(clippy::unused_unit)] fn mask_type(&self) -> Self::MaskType { - let ($($field,)*) = self; - ($($field.mask_type(),)*) + let ($($var,)*) = self; + ($($var.mask_type(),)*) } - - fn canonical(&self) -> Self::CanonicalType { - DynBundleType::new(self.fields()) + fn canonical(&self) -> CanonicalType { + Bundle::new(self.fields()).canonical() } - - fn source_location(&self) -> SourceLocation { + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Bundle(bundle) = canonical_type else { + panic!("expected bundle"); + }; + let [$($var,)*] = *bundle.fields() else { + panic!("bundle has wrong number of fields"); + }; + $(let BundleField { name, flipped, ty } = $var; + assert_eq!(&*name, stringify!($num)); + assert_eq!(flipped, false); + let $var = $T::from_canonical(ty);)* + ($($var,)*) + } + fn source_location() -> SourceLocation { SourceLocation::builtin() } - - fn type_enum(&self) -> TypeEnum { - TypeEnum::BundleType(self.canonical()) - } - - #[allow(clippy::unused_unit)] - fn from_canonical_type(t: Self::CanonicalType) -> Self { - let [$($field),*] = *t.fields() else { - panic!("wrong number of fields"); - }; - ($($field.from_canonical_type_helper(stringify!($m), false),)*) + } + impl<$($T: Type,)*> BundleType for ($($T,)*) { + type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; + type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>; + fn fields(&self) -> Interned<[BundleField]> { + let ($($var,)*) = self; + [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern() } } - - impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) - where - $($T::Value: Value,)* - { - fn expr_deref( - this: &::fayalite::expr::Expr<::Value>, - ) -> &::MatchVariant { + impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { + fn expr_deref(this: &Expr) -> &Self::MatchVariant { let _ = this; - Interned::<_>::into_inner( - Intern::intern_sized(( - $(this.field(stringify!($m)),)* - )), - ) + Interned::<_>::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized()) } } - - impl<$($T: Type,)*> BundleType for ($($T,)*) - where - $($T::Value: Value,)* - { - type Builder = $builder<($($T::Value,)*), $(into_unit!($T),)*>; - fn builder() -> Self::Builder { - $builder { - $($field: (),)* - _phantom: PhantomData, - } - } - fn fields( - &self, - ) -> Interned<[FieldType>]> { - [ - $(FieldType { - name: stringify!($m).intern(), - flipped: false, - ty: self.$m.canonical_dyn(), - },)* - ][..].intern() - } - fn fields_hint() -> FieldsHint { - FieldsHint::new([ - $(FieldType { - name: stringify!($m).intern(), - flipped: false, - ty: TypeHint::<$T>::intern_dyn(), - },)* - ], false) - } - } - - impl<$($T: StaticType,)*> StaticType for ($($T,)*) - where - $($T::Value: Value,)* - { - #[allow(clippy::unused_unit)] + impl<$($T: StaticType,)*> StaticType for ($($T,)*) { fn static_type() -> Self { - ($($T::static_type(),)*) + $(let $var = $T::static_type();)* + ($($var,)*) } } + impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { + type Type = ($($T::Type,)*); - impl<$($T: Value,)*> Value for ($($T,)*) - where - $($T::Type: Type,)* - { - fn to_canonical(&self) -> ::CanonicalValue { - let ty = self.ty().canonical(); - DynBundle::new( - ty, - Arc::new([ - $(self.$m.to_canonical_dyn(),)* - ]), - ) - } - fn to_bits_impl(this: &Self) -> Interned { - BundleValue::to_bits_impl(this) + fn to_expr(&self) -> Expr { + let ($($var,)*) = self; + $(let $var = $var.to_expr();)* + let ty = ($(Expr::ty($var),)*); + let field_values = [$(Expr::canonical($var)),*]; + BundleLiteral::new(ty, &field_values).to_expr() } } + impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> { + type Type = ($($T,)*); - impl<$($T: Value,)*> BundleValue for ($($T,)*) - where - $($T::Type: Type,)* - { + fn to_expr(&self) -> Expr { + let ($($var,)*) = self.0; + let ty = ($(Expr::ty($var),)*); + let field_values = [$(Expr::canonical($var)),*]; + BundleLiteral::new(ty, &field_values).to_expr() + } } }; + ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { + impl_tuples!([$($lhs)*] []); + impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]); + }; } -impl_tuple!(TupleBuilder0,); -impl_tuple!(TupleBuilder1, (A A2 field_0 0)); -impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1)); -impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2)); -impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3)); -impl_tuple!(TupleBuilder5, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4)); -impl_tuple!(TupleBuilder6, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5)); -impl_tuple!(TupleBuilder7, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6)); -impl_tuple!(TupleBuilder8, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7)); -impl_tuple!(TupleBuilder9, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8)); -impl_tuple!(TupleBuilder10, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9)); -impl_tuple!(TupleBuilder11, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10)); -impl_tuple!(TupleBuilder12, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10), (L L2 field_11 11)); +impl_tuples! { + [] [ + {#[num = 0, field = field_0] a: A} + {#[num = 1, field = field_1] b: B} + {#[num = 2, field = field_2] c: C} + {#[num = 3, field = field_3] d: D} + {#[num = 4, field = field_4] e: E} + {#[num = 5, field = field_5] f: F} + {#[num = 6, field = field_6] g: G} + {#[num = 7, field = field_7] h: H} + {#[num = 8, field = field_8] i: I} + {#[num = 9, field = field_9] j: J} + {#[num = 10, field = field_10] k: K} + {#[num = 11, field = field_11] l: L} + ] +} diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 6b9c104..c6cd0f8 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -1,150 +1,58 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -#![allow(clippy::type_complexity)] + use crate::{ - bundle::{BundleValue, TypeHintTrait}, expr::{ops::VariantAccess, Expr, ToExpr}, - int::{UInt, UIntType}, - intern::{Intern, Interned, MemoizeGeneric}, + hdl, + int::Bool, + intern::{Intern, Interned}, module::{ EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, NormalModule, Scope, }, source_location::SourceLocation, - ty::{ - CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, - DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum, - }, + ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties}, }; -use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use hashbrown::HashMap; -use std::{ - borrow::Cow, - fmt, - hash::{Hash, Hasher}, - iter::FusedIterator, - marker::PhantomData, -}; +use std::iter::FusedIterator; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct VariantType { +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct EnumVariant { pub name: Interned, - pub ty: Option, -} - -pub struct FmtDebugInEnum<'a, T>(&'a VariantType); - -impl 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 fmt::Display for FmtDebugInEnum<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -impl VariantType { - pub fn map_opt_ty) -> Option>(self, f: F) -> VariantType { - let Self { name, ty } = self; - VariantType { name, ty: f(ty) } - } - pub fn map_ty U>(self, f: F) -> VariantType { - 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 { - FmtDebugInEnum(self) - } -} - -impl VariantType { - pub fn canonical(&self) -> VariantType { - self.as_ref_ty().map_ty(T::canonical) - } - pub fn to_dyn(&self) -> VariantType> { - self.as_ref_ty().map_ty(T::to_dyn) - } - pub fn canonical_dyn(&self) -> VariantType> { - self.as_ref_ty().map_ty(T::canonical_dyn) - } -} - -impl VariantType> { - pub fn from_canonical_type_helper_has_value(self, expected_name: &str) -> T { - assert_eq!(&*self.name, expected_name, "variant name doesn't match"); - let Some(ty) = self.ty else { - panic!("variant {expected_name} has no value but a value is expected"); - }; - T::from_dyn_canonical_type(ty) - } - pub fn from_canonical_type_helper_no_value(self, expected_name: &str) { - assert_eq!(&*self.name, expected_name, "variant name doesn't match"); - assert!( - self.ty.is_none(), - "variant {expected_name} has a value but is expected to have no value" - ); - } + pub ty: Option, } #[derive(Clone, Eq)] -struct DynEnumTypeImpl { - variants: Interned<[VariantType>]>, +struct EnumImpl { + variants: Interned<[EnumVariant]>, name_indexes: HashMap, usize>, - bit_width: usize, - is_storable: bool, - is_castable_from_bits: bool, + type_properties: TypeProperties, } -impl fmt::Debug for DynEnumTypeImpl { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DynEnumType ")?; - f.debug_set() - .entries( - self.variants - .iter() - .map(|variant| variant.fmt_debug_in_enum()), - ) +impl std::fmt::Debug for EnumImpl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Enum") + .field("variants", &self.variants) + .field("name_indexes", &self.name_indexes) + .field("type_properties", &self.type_properties) .finish() } } -impl PartialEq for DynEnumTypeImpl { +impl std::hash::Hash for EnumImpl { + fn hash(&self, state: &mut H) { + self.variants.hash(state); + } +} + +impl PartialEq for EnumImpl { fn eq(&self, other: &Self) -> bool { self.variants == other.variants } } -impl Hash for DynEnumTypeImpl { - fn hash(&self, state: &mut H) { - self.variants.hash(state); - } -} - -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub struct DynEnumType(Interned); - -impl fmt::Debug for DynEnumType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Enum(Interned); fn discriminant_bit_width_impl(variant_count: usize) -> usize { variant_count @@ -153,38 +61,47 @@ fn discriminant_bit_width_impl(variant_count: usize) -> usize { .unwrap_or(0) as usize } -impl DynEnumType { +impl Enum { #[track_caller] - pub fn new(variants: Interned<[VariantType>]>) -> Self { + pub fn new(variants: Interned<[EnumVariant]>) -> Self { assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); let mut name_indexes = HashMap::with_capacity(variants.len()); - let mut body_bit_width = 0usize; - let mut is_storable = true; - let mut is_castable_from_bits = true; - for (index, &VariantType { name, ty }) in variants.iter().enumerate() { - if let Some(old_index) = name_indexes.insert(name, index) { + let mut type_properties = TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: 0, + }; + for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { + if let Some(old_index) = name_indexes.insert(*name, index) { panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); } if let Some(ty) = ty { - assert!( - ty.is_passive(), - "variant type must be a passive type: {ty:?}" - ); - body_bit_width = body_bit_width.max(ty.bit_width()); - is_storable &= ty.is_storable(); - is_castable_from_bits &= ty.is_castable_from_bits(); + let TypeProperties { + is_passive, + is_storable, + is_castable_from_bits, + bit_width, + } = ty.type_properties(); + assert!(is_passive, "variant type must be a passive type: {ty:?}"); + type_properties = TypeProperties { + is_passive: true, + is_storable: type_properties.is_storable & is_storable, + is_castable_from_bits: type_properties.is_castable_from_bits + & is_castable_from_bits, + bit_width: type_properties.bit_width.max(bit_width), + }; } } - let bit_width = body_bit_width + type_properties.bit_width = type_properties + .bit_width .checked_add(discriminant_bit_width_impl(variants.len())) .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); Self( - DynEnumTypeImpl { + EnumImpl { variants, name_indexes, - bit_width, - is_storable, - is_castable_from_bits, + type_properties, } .intern_sized(), ) @@ -192,233 +109,31 @@ impl DynEnumType { pub fn discriminant_bit_width(self) -> usize { discriminant_bit_width_impl(self.variants().len()) } - pub fn is_passive(self) -> bool { - true - } - pub fn is_storable(self) -> bool { - self.0.is_storable - } - pub fn is_castable_from_bits(self) -> bool { - self.0.is_castable_from_bits - } - pub fn bit_width(self) -> usize { - self.0.bit_width + pub fn type_properties(self) -> TypeProperties { + self.0.type_properties } pub fn name_indexes(&self) -> &HashMap, usize> { &self.0.name_indexes } } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct DynEnum { - ty: DynEnumType, - variant_index: usize, - variant_value: Option, -} - -impl DynEnum { - #[track_caller] - pub fn new_by_index( - ty: DynEnumType, - variant_index: usize, - variant_value: Option, - ) -> 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, - variant_value: Option, - ) -> 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 { - &self.variant_value - } - pub fn variant_with_type(&self) -> VariantType> { - self.ty.variants()[self.variant_index] - } - pub fn variant_name(&self) -> Interned { - self.variant_with_type().name - } - pub fn variant_type(&self) -> Option> { - 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>]>, - pub more_variants: bool, -} - -impl VariantsHint { - pub fn new( - known_variants: impl IntoIterator>>, - more_variants: bool, - ) -> Self { - let known_variants = Intern::intern_owned(Vec::from_iter(known_variants)); - Self { - known_variants, - more_variants, - } - } - pub fn check_variant( - self, - index: usize, - variant: VariantType<&dyn DynType>, - ) -> Result<(), String> { - let Some(&known_variant) = self.known_variants.get(index) else { - return if self.more_variants { - Ok(()) - } else { - Err(format!( - "too many variants: name={:?} index={index}", - variant.name - )) - }; - }; - let VariantType { - name: known_name, - ty: type_hint, - } = known_variant; - let VariantType { name, ty } = variant; - if name != known_name { - Err(format!( - "wrong variant name {name:?}, expected {known_name:?}" - )) - } else { - match (ty, type_hint) { - (Some(ty), Some(type_hint)) => type_hint.matches(ty), - (None, None) => Ok(()), - (None, Some(_)) => Err(format!( - "expected variant {name:?} to have type, no type provided" - )), - (Some(_), None) => Err(format!( - "expected variant {name:?} to have no type, but a type was provided" - )), - } - } - } -} - pub trait EnumType: Type< - CanonicalType = DynEnumType, - CanonicalValue = DynEnum, - MaskType = UIntType<1>, - MaskValue = UInt<1>, - MatchActiveScope = Scope, - MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, - MatchVariantsIter = EnumMatchVariantsIter, - > + Connect -where - Self::Value: EnumValue + ToExpr, + MaskType = Bool, + MatchActiveScope = Scope, + MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, + MatchVariantsIter = EnumMatchVariantsIter, +> { - type Builder; + fn variants(&self) -> Interned<[EnumVariant]>; fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, ) -> (Self::MatchVariant, Self::MatchActiveScope); - fn builder() -> Self::Builder; - fn variants(&self) -> Interned<[VariantType>]>; - fn variants_hint() -> VariantsHint; - #[allow(clippy::result_unit_err)] - fn variant_to_bits( - &self, - variant_index: usize, - variant_value: Option<&VariantValue>, - ) -> Result, ()> { - #[derive(Hash, Eq, PartialEq)] - struct VariantToBitsMemoize(PhantomData<(E, V)>); - impl Clone for VariantToBitsMemoize { - fn clone(&self) -> Self { - *self - } - } - impl Copy for VariantToBitsMemoize {} - impl< - E: EnumType>, - V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone, - > MemoizeGeneric for VariantToBitsMemoize - { - type InputRef<'a> = (&'a E, usize, Option<&'a V>); - type InputOwned = (E, usize, Option); - type InputCow<'a> = (Cow<'a, E>, usize, Option>); - type Output = Result, ()>; - - 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::()[..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::(PhantomData).get(( - self, - variant_index, - variant_value, - )) - } } -pub trait EnumValue: Value -where - ::Type: EnumType, -{ -} +pub struct EnumMatchVariantAndInactiveScope(EnumMatchVariantAndInactiveScopeImpl); -pub struct EnumMatchVariantAndInactiveScope(EnumMatchVariantAndInactiveScopeImpl) -where - T::Value: EnumValue; - -impl MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope -where - T::Value: EnumValue, -{ +impl MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope { type MatchVariant = T::MatchVariant; type MatchActiveScope = Scope; @@ -427,36 +142,22 @@ where } } -impl EnumMatchVariantAndInactiveScope -where - T::Value: EnumValue, -{ - pub fn variant_access(&self) -> Interned>> { +impl EnumMatchVariantAndInactiveScope { + pub fn variant_access(&self) -> Interned { self.0.variant_access() } - pub fn activate( - self, - ) -> ( - Interned>>, - Scope, - ) { + pub fn activate(self) -> (Interned, Scope) { self.0.activate() } } #[derive(Clone)] -pub struct EnumMatchVariantsIter -where - T::Value: EnumValue, -{ +pub struct EnumMatchVariantsIter { pub(crate) inner: EnumMatchVariantsIterImpl, pub(crate) variant_index: std::ops::Range, } -impl Iterator for EnumMatchVariantsIter -where - T::Value: EnumValue, -{ +impl Iterator for EnumMatchVariantsIter { type Item = EnumMatchVariantAndInactiveScope; fn next(&mut self) -> Option { @@ -470,21 +171,15 @@ where } } -impl ExactSizeIterator for EnumMatchVariantsIter -where - T::Value: EnumValue, -{ +impl ExactSizeIterator for EnumMatchVariantsIter { fn len(&self) -> usize { self.variant_index.len() } } -impl FusedIterator for EnumMatchVariantsIter where T::Value: EnumValue {} +impl FusedIterator for EnumMatchVariantsIter {} -impl DoubleEndedIterator for EnumMatchVariantsIter -where - T::Value: EnumValue, -{ +impl DoubleEndedIterator for EnumMatchVariantsIter { fn next_back(&mut self) -> Option { self.variant_index.next_back().map(|variant_index| { EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) @@ -492,129 +187,55 @@ where } } -impl Type for DynEnumType { - type CanonicalType = DynEnumType; - type Value = DynEnum; - type CanonicalValue = DynEnum; - type MaskType = UIntType<1>; - type MaskValue = UInt<1>; - type MatchVariant = Option>; - type MatchActiveScope = Scope; - type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope; - type MatchVariantsIter = EnumMatchVariantsIter; - - fn match_variants( - this: Expr, - module_builder: &mut ModuleBuilder, - source_location: SourceLocation, - ) -> Self::MatchVariantsIter - where - IO::Type: crate::bundle::BundleType, - { - 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 for DynEnumType {} - -pub struct NoBuilder; - -impl EnumType for DynEnumType { - type Builder = NoBuilder; - +impl EnumType for Enum { fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, ) -> (Self::MatchVariant, Self::MatchActiveScope) { let (expr, scope) = v.0.activate(); - (expr.variant_type().ty.map(|_| expr.to_expr()), scope) + (expr.variant_type().map(|_| expr.to_expr()), scope) } - - fn builder() -> Self::Builder { - NoBuilder - } - - fn variants(&self) -> Interned<[VariantType>]> { + fn variants(&self) -> Interned<[EnumVariant]> { self.0.variants } +} - fn variants_hint() -> VariantsHint { - VariantsHint { - known_variants: [][..].intern(), - more_variants: true, - } +impl Type for Enum { + type MaskType = Bool; + type MatchVariant = Option>; + type MatchActiveScope = Scope; + type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope; + type MatchVariantsIter = EnumMatchVariantsIter; + + fn match_variants( + this: Expr, + module_builder: &mut ModuleBuilder, + source_location: crate::source_location::SourceLocation, + ) -> Self::MatchVariantsIter { + module_builder.enum_match_variants_helper(this, source_location) + } + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Enum(*self) + } + + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Enum(retval) = canonical_type else { + panic!("expected enum"); + }; + retval + } + fn source_location() -> SourceLocation { + SourceLocation::builtin() } } -impl CanonicalType for DynEnumType { - const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType; -} - -impl ToExpr for DynEnum { - type Type = DynEnumType; - - fn ty(&self) -> Self::Type { - self.ty - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::from_value(self) - } -} - -impl Value for DynEnum { - fn to_canonical(&self) -> ::CanonicalValue { - self.clone() - } - fn to_bits_impl(this: &Self) -> Interned { - 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 { - 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 { - None, - Some(T), - } +#[hdl] +pub enum HdlOption { + None, + Some(T), } diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 2b8f2b2..aec5ec0 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -1,1095 +1,230 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ - array::ArrayType, - bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType}, - enum_::{DynEnumType, EnumType, EnumValue}, - int::{DynSIntType, DynUInt, DynUIntType, IntValue, StaticOrDynIntType, UInt, UIntType}, - intern::{Intern, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId}, - memory::{DynPortType, MemPort, PortType}, - module::{ - transform::visit::{Fold, Folder, Visit, Visitor}, - Instance, ModuleIO, TargetName, - }, - reg::Reg, - source_location::SourceLocation, - ty::{ - DynCanonicalType, DynCanonicalValue, DynType, DynValue, DynValueTrait, Type, TypeWithDeref, - Value, - }, - util::ConstBool, - valueless::Valueless, - wire::Wire, + array::{Array, ArrayType}, + int::{Bool, IntType, SIntType, SIntValue, Size, UIntType, UIntValue}, + intern::{Intern, Interned}, + ty::{CanonicalType, Type}, }; -use bitvec::slice::BitSlice; -use std::{any::Any, convert::Infallible, fmt, hash::Hash, marker::PhantomData, ops::Deref}; pub mod ops; macro_rules! expr_enum { ( pub enum $ExprEnum:ident { - $($Variant:ident($VariantTy:ty),)+ + $($Variant:ident($VariantTy:ty),)* } ) => { - #[derive(Copy, Clone, Eq, PartialEq, Hash)] + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum $ExprEnum { - $($Variant(Interned<$VariantTy>),)+ + $($Variant($VariantTy),)* } - impl fmt::Debug for $ExprEnum { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - $(Self::$Variant(v) => v.fmt(f),)+ - } + $(impl From<$VariantTy> for $ExprEnum { + fn from(v: $VariantTy) -> Self { + Self::$Variant(v) } - } - - impl $ExprEnum { - pub fn target(self) -> Option> { - match self { - $(Self::$Variant(v) => v.target(),)+ - } - } - #[allow(clippy::result_unit_err)] - pub fn to_literal_bits(&self) -> Result, ()> { - match self { - $(Self::$Variant(v) => v.to_literal_bits(),)+ - } - } - } - - impl ToExpr for $ExprEnum { - type Type = Interned; - - fn ty(&self) -> Self::Type { - match self { - $(Self::$Variant(v) => v.ty().canonical_dyn(),)+ - } - } - - fn to_expr(&self) -> Expr { - Expr::new_unchecked(*self) - } - } - - impl Fold for $ExprEnum { - fn fold(self, state: &mut State) -> Result { - state.fold_expr_enum(self) - } - fn default_fold(self, state: &mut State) -> Result { - match self { - $(Self::$Variant(v) => Fold::fold(v, state).map(Self::$Variant),)+ - } - } - } - - impl Visit for $ExprEnum { - fn visit(&self, state: &mut State) -> Result<(), State::Error> { - state.visit_expr_enum(self) - } - fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { - match self { - $(Self::$Variant(v) => Visit::visit(v, state),)+ - } - } - } + })* }; } expr_enum! { pub enum ExprEnum { - Literal(Literal>), - ArrayLiteral(ops::ArrayLiteral>), - BundleLiteral(ops::BundleLiteral), - EnumLiteral(ops::EnumLiteral), - NotU(ops::Not), - NotS(ops::Not), - Neg(ops::Neg), - BitAndU(ops::BitAnd), - BitAndS(ops::BitAnd), - BitOrU(ops::BitOr), - BitOrS(ops::BitOr), - BitXorU(ops::BitXor), - BitXorS(ops::BitXor), - AddU(ops::Add), - AddS(ops::Add), - SubU(ops::Sub), - SubS(ops::Sub), - MulU(ops::Mul), - MulS(ops::Mul), - DynShlU(ops::DynShl), - DynShlS(ops::DynShl), - DynShrU(ops::DynShr), - DynShrS(ops::DynShr), - FixedShlU(ops::FixedShl), - FixedShlS(ops::FixedShl), - FixedShrU(ops::FixedShr), - FixedShrS(ops::FixedShr), - CmpLtU(ops::CmpLt), - CmpLtS(ops::CmpLt), - CmpLeU(ops::CmpLe), - CmpLeS(ops::CmpLe), - CmpGtU(ops::CmpGt), - CmpGtS(ops::CmpGt), - CmpGeU(ops::CmpGe), - CmpGeS(ops::CmpGe), - CmpEqU(ops::CmpEq), - CmpEqS(ops::CmpEq), - CmpNeU(ops::CmpNe), - CmpNeS(ops::CmpNe), - CastUIntToUInt(ops::CastInt), - CastUIntToSInt(ops::CastInt), - CastSIntToUInt(ops::CastInt), - CastSIntToSInt(ops::CastInt), - SliceUInt(ops::Slice), - SliceSInt(ops::Slice), - ReduceBitAnd(ops::ReduceBitAnd>), - ReduceBitOr(ops::ReduceBitOr>), - ReduceBitXor(ops::ReduceBitXor>), - FieldAccess(ops::FieldAccess>), - VariantAccess(ops::VariantAccess>), - ArrayIndex(ops::ArrayIndex>), - DynArrayIndex(ops::DynArrayIndex>), - CastToBits(ops::CastToBits), - CastBitsTo(ops::CastBitsTo>), - CastBitToClock(ops::CastBitToClock), - CastBitToSyncReset(ops::CastBitToSyncReset), - CastBitToAsyncReset(ops::CastBitToAsyncReset), - CastSyncResetToReset(ops::CastSyncResetToReset), - CastAsyncResetToReset(ops::CastAsyncResetToReset), - CastClockToBit(ops::CastClockToBit), - CastSyncResetToBit(ops::CastSyncResetToBit), - CastAsyncResetToBit(ops::CastAsyncResetToBit), - CastResetToBit(ops::CastResetToBit), - ModuleIO(ModuleIO>), - Instance(Instance), - Wire(Wire>), - Reg(Reg>), - MemPort(MemPort), + UIntLiteral(Interned), + SIntLiteral(Interned), + BoolLiteral(bool), + BundleLiteral(ops::BundleLiteral), + EnumLiteral(ops::EnumLiteral), + NotU(ops::NotU), + NotS(ops::NotS), + NotB(ops::NotB), + Neg(ops::Neg), + BitAndU(ops::BitAndU), + BitAndS(ops::BitAndS), + BitAndB(ops::BitAndB), + BitOrU(ops::BitOrU), + BitOrS(ops::BitOrS), + BitOrB(ops::BitOrB), + BitXorU(ops::BitXorU), + BitXorS(ops::BitXorS), + BitXorB(ops::BitXorB), + AddU(ops::AddU), + AddS(ops::AddS), + SubU(ops::SubU), + SubS(ops::SubS), + MulU(ops::MulU), + MulS(ops::MulS), + DivU(ops::DivU), + DivS(ops::DivS), + RemU(ops::RemU), + RemS(ops::RemS), + DynShlU(ops::DynShlU), + DynShlS(ops::DynShlS), + DynShrU(ops::DynShrU), + DynShrS(ops::DynShrS), + FixedShlU(ops::FixedShlU), + FixedShlS(ops::FixedShlS), + FixedShrU(ops::FixedShrU), + FixedShrS(ops::FixedShrS), + CmpLtB(ops::CmpLtB), + CmpLeB(ops::CmpLeB), + CmpGtB(ops::CmpGtB), + CmpGeB(ops::CmpGeB), + CmpEqB(ops::CmpEqB), + CmpNeB(ops::CmpNeB), + CmpLtU(ops::CmpLtU), + CmpLeU(ops::CmpLeU), + CmpGtU(ops::CmpGtU), + CmpGeU(ops::CmpGeU), + CmpEqU(ops::CmpEqU), + CmpNeU(ops::CmpNeU), + CmpLtS(ops::CmpLtS), + CmpLeS(ops::CmpLeS), + CmpGtS(ops::CmpGtS), + CmpGeS(ops::CmpGeS), + CmpEqS(ops::CmpEqS), + CmpNeS(ops::CmpNeS), + CastUIntToUInt(ops::CastUIntToUInt), + CastUIntToSInt(ops::CastUIntToSInt), + CastSIntToUInt(ops::CastSIntToUInt), + CastSIntToSInt(ops::CastSIntToSInt), + CastBoolToUInt(ops::CastBoolToUInt), + CastBoolToSInt(ops::CastBoolToSInt), + FieldAccess(ops::FieldAccess), + VariantAccess(ops::VariantAccess), + ArrayIndex(ops::ArrayIndex), + DynArrayIndex(ops::DynArrayIndex), + IntSliceU(ops::IntSliceU), + IntSliceS(ops::IntSliceS), } } -pub struct Expr { - /// use weird names to help work around rust-analyzer bug - __enum: ExprEnum, - __phantom: PhantomData, +impl From for ExprEnum { + fn from(value: UIntValue) -> Self { + ExprEnum::UIntLiteral(Intern::intern_sized(value)) + } } -impl Expr { - pub fn expr_enum(self) -> ExprEnum { - self.__enum +impl From for ExprEnum { + fn from(value: SIntValue) -> Self { + ExprEnum::SIntLiteral(Intern::intern_sized(value)) } - pub fn new_unchecked(expr_enum: ExprEnum) -> Self { - Self { - __enum: expr_enum, - __phantom: PhantomData, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Expr { + __enum: Interned, + __ty: T, +} + +impl Expr { + pub fn expr_enum(this: Self) -> Interned { + this.__enum + } + pub fn ty(this: Self) -> T { + this.__ty + } + pub fn canonical(this: Self) -> Expr { + Expr { + __enum: this.__enum, + __ty: this.__ty.canonical(), } } - pub fn canonical(self) -> Expr<::CanonicalValue> + pub fn as_dyn_int(this: Self) -> Expr where - T: Value, + T: IntType, { Expr { - __enum: self.expr_enum(), - __phantom: PhantomData, - } - } - pub fn dyn_canonical_type(self) -> Interned { - self.expr_enum().ty() - } - pub fn canonical_type(self) -> ::CanonicalType - where - T: Value, - { - ::CanonicalType::from_dyn_canonical_type(self.dyn_canonical_type()) - } - pub fn valueless(self) -> Valueless - where - T: Value>, - { - Valueless { ty: self.ty() } - } - #[allow(clippy::result_unit_err)] - pub fn to_literal_bits(&self) -> Result, ()> { - self.expr_enum().to_literal_bits() - } - #[track_caller] - pub fn with_type>>(self) -> Expr - where - T: Value>, - { - let retval = Expr::::new_unchecked(self.expr_enum()); - let _ = retval.ty(); // check that the type is correct - retval - } - pub fn to_dyn(self) -> Expr { - Expr::new_unchecked(self.expr_enum()) - } - pub fn to_canonical_dyn(self) -> Expr { - Expr::new_unchecked(self.expr_enum()) - } - #[track_caller] - pub fn from_value(value: &T) -> Self - where - T: Value>, - { - Literal::::new_unchecked(value.to_canonical()).to_expr() - } - pub fn target(self) -> Option> { - self.expr_enum().target() - } - pub fn flow(self) -> Flow { - self.target().map(|v| v.flow()).unwrap_or(Flow::Source) - } -} - -impl Copy for Expr {} - -impl Clone for Expr { - fn clone(&self) -> Self { - *self - } -} - -impl fmt::Debug for Expr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - __enum, - __phantom: _, - } = self; - __enum.fmt(f) - } -} - -impl>> sealed::Sealed for Expr {} - -impl PartialEq for Expr { - fn eq(&self, other: &Self) -> bool { - let Self { - __enum, - __phantom: _, - } = self; - *__enum == other.__enum - } -} - -impl Eq for Expr {} - -impl Hash for Expr { - fn hash(&self, state: &mut H) { - let Self { - __enum, - __phantom: _, - } = self; - __enum.hash(state); - } -} - -impl Expr> -where - T: StaticOrDynIntType< - 1, - Signed = ConstBool, - CanonicalType = DynUIntType, - CanonicalValue = DynUInt, - >, -{ - pub fn as_bool(self) -> Expr> { - assert_eq!(self.canonical_type().width, 1); - Expr::new_unchecked(self.expr_enum()) - } -} - -impl>> Expr { - pub fn field>>( - self, - name: &str, - ) -> Expr { - ops::FieldAccess::::new_unchecked( - self.canonical(), - name.intern(), - ) - .to_expr() - } -} - -impl>> ToExpr for Expr { - type Type = T::Type; - - fn ty(&self) -> T::Type { - T::Type::from_dyn_canonical_type(self.dyn_canonical_type()) - } - - fn to_expr(&self) -> Expr { - *self - } -} - -impl, T> Deref for Expr -where - T: TypeWithDeref, -{ - type Target = T::MatchVariant; - - fn deref(&self) -> &Self::Target { - T::expr_deref(self) - } -} - -impl>, State: ?Sized + Folder> Fold for Expr { - fn fold(self, state: &mut State) -> Result { - state.fold_expr(self) - } - fn default_fold(self, state: &mut State) -> Result { - Ok(Expr::::new_unchecked(self.expr_enum().fold(state)?).with_type()) - } -} - -impl>, State: ?Sized + Visitor> Visit for Expr { - fn visit(&self, state: &mut State) -> Result<(), State::Error> { - state.visit_expr(self) - } - fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { - self.expr_enum().visit(state) - } -} - -pub trait ExprTraitBase: - fmt::Debug + Any + SupportsPtrEqWithTypeId + Send + Sync + sealed::Sealed + ToExpr -{ -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Flow { - Source, - Sink, - Duplex, -} - -impl Flow { - pub const fn flip(self) -> Flow { - match self { - Flow::Source => Flow::Sink, - Flow::Sink => Flow::Source, - Flow::Duplex => Flow::Duplex, - } - } - pub const fn flip_if(self, flipped: bool) -> Flow { - if flipped { - self.flip() - } else { - self + __enum: this.__enum, + __ty: this.__ty.as_dyn_int(), } } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TargetPathBundleField { - pub name: Interned, -} - -impl fmt::Display for TargetPathBundleField { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, ".{}", self.name) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TargetPathArrayElement { - pub index: usize, -} - -impl fmt::Display for TargetPathArrayElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{}]", self.index) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TargetPathDynArrayElement {} - -impl fmt::Display for TargetPathDynArrayElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[]") - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum TargetPathElement { - BundleField(TargetPathBundleField), - ArrayElement(TargetPathArrayElement), - DynArrayElement(TargetPathDynArrayElement), -} - -impl From for TargetPathElement { - fn from(value: TargetPathBundleField) -> Self { - Self::BundleField(value) - } -} - -impl From for TargetPathElement { - fn from(value: TargetPathArrayElement) -> Self { - Self::ArrayElement(value) - } -} - -impl From for TargetPathElement { - fn from(value: TargetPathDynArrayElement) -> Self { - Self::DynArrayElement(value) - } -} - -impl fmt::Display for TargetPathElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::BundleField(v) => v.fmt(f), - Self::ArrayElement(v) => v.fmt(f), - Self::DynArrayElement(v) => v.fmt(f), +impl Expr> { + pub fn as_dyn_array(this: Self) -> Expr { + Expr { + __enum: this.__enum, + __ty: this.__ty.as_dyn_array(), } } } -impl TargetPathElement { - pub fn canonical_ty(&self, parent: Interned) -> Interned { - match self { - &Self::BundleField(TargetPathBundleField { name }) => { - let parent_ty = parent - .canonical_ty() - .type_enum() - .bundle_type() - .expect("parent type is known to be a bundle"); - let field = parent_ty - .field_by_name(name) - .expect("field name is known to be a valid field of parent type"); - field.ty - } - &Self::ArrayElement(TargetPathArrayElement { index }) => { - let parent_ty = parent - .canonical_ty() - .type_enum() - .array_type() - .expect("parent type is known to be an array"); - assert!(index < parent_ty.len()); - *parent_ty.element() - } - Self::DynArrayElement(_) => { - let parent_ty = parent - .canonical_ty() - .type_enum() - .array_type() - .expect("parent type is known to be an array"); - *parent_ty.element() - } - } - } - pub fn flow(&self, parent: Interned) -> Flow { - match self { - Self::BundleField(v) => { - let parent_ty = parent - .canonical_ty() - .type_enum() - .bundle_type() - .expect("parent type is known to be a bundle"); - let field = parent_ty - .field_by_name(v.name) - .expect("field name is known to be a valid field of parent type"); - parent.flow().flip_if(field.flipped) - } - Self::ArrayElement(_) => parent.flow(), - Self::DynArrayElement(_) => parent.flow(), - } - } - pub fn is_static(&self) -> bool { - match self { - Self::BundleField(_) | Self::ArrayElement(_) => true, - Self::DynArrayElement(_) => false, - } - } -} - -macro_rules! impl_target_base { - ( - $(#[$enum_meta:meta])* - $enum_vis:vis enum $TargetBase:ident { - $( - #[is = $is_fn:ident] - #[to = $to_fn:ident] - $(#[$variant_meta:meta])* - $Variant:ident($VariantTy:ty), - )* - } - ) => { - $(#[$enum_meta])* - $enum_vis enum $TargetBase { - $( - $(#[$variant_meta])* - $Variant($VariantTy), - )* - } - - impl fmt::Debug for $TargetBase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - $(Self::$Variant(v) => v.fmt(f),)* - } - } - } - - $( - impl From<$VariantTy> for $TargetBase { - fn from(value: $VariantTy) -> Self { - Self::$Variant(value) - } - } - - impl From<$VariantTy> for Target { - fn from(value: $VariantTy) -> Self { - $TargetBase::$Variant(value).into() - } - } - )* - - impl $TargetBase { - $( - $enum_vis fn $is_fn(&self) -> bool { - self.$to_fn().is_some() - } - $enum_vis fn $to_fn(&self) -> Option<&$VariantTy> { - if let Self::$Variant(retval) = self { - Some(retval) - } else { - None - } - } - )* - $enum_vis fn must_connect_to(&self) -> bool { - match self { - $(Self::$Variant(v) => v.must_connect_to(),)* - } - } - $enum_vis fn flow(&self) -> Flow { - match self { - $(Self::$Variant(v) => v.flow(),)* - } - } - $enum_vis fn source_location(&self) -> SourceLocation { - match self { - $(Self::$Variant(v) => v.source_location(),)* - } - } - } - }; -} - -impl_target_base! { - #[derive(Clone, PartialEq, Eq, Hash)] - pub enum TargetBase { - #[is = is_module_io] - #[to = module_io] - ModuleIO(ModuleIO>), - #[is = is_mem_port] - #[to = mem_port] - MemPort(MemPort), - #[is = is_reg] - #[to = reg] - Reg(Reg>), - #[is = is_wire] - #[to = wire] - Wire(Wire>), - #[is = is_instance] - #[to = instance] - Instance(Instance), - } -} - -impl fmt::Display for TargetBase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.target_name()) - } -} - -impl TargetBase { - pub fn target_name(&self) -> TargetName { - match self { - TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None), - TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())), - TargetBase::Reg(v) => TargetName(v.scoped_name(), None), - TargetBase::Wire(v) => TargetName(v.scoped_name(), None), - TargetBase::Instance(v) => TargetName(v.scoped_name(), None), - } - } - pub fn canonical_ty(&self) -> Interned { - match self { - TargetBase::ModuleIO(v) => v.ty(), - TargetBase::MemPort(v) => v.ty().canonical_dyn(), - TargetBase::Reg(v) => v.ty(), - TargetBase::Wire(v) => v.ty(), - TargetBase::Instance(v) => v.ty().canonical_dyn(), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct TargetChild { - parent: Interned, - path_element: Interned, - canonical_ty: Interned, - flow: Flow, -} - -impl fmt::Debug for TargetChild { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - parent, - path_element, - canonical_ty: _, - flow: _, - } = self; - parent.fmt(f)?; - fmt::Display::fmt(path_element, f) - } -} - -impl fmt::Display for TargetChild { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - parent, - path_element, - canonical_ty: _, - flow: _, - } = self; - parent.fmt(f)?; - path_element.fmt(f) - } -} - -impl TargetChild { - pub fn new(parent: Interned, path_element: Interned) -> Self { - Self { - parent, - path_element, - canonical_ty: path_element.canonical_ty(parent), - flow: path_element.flow(parent), - } - } - pub fn parent(self) -> Interned { - self.parent - } - pub fn path_element(self) -> Interned { - self.path_element - } - pub fn canonical_ty(self) -> Interned { - self.canonical_ty - } - pub fn flow(self) -> Flow { - self.flow - } - pub fn bundle_field(self) -> Option>> { - if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element { - let parent_ty = self - .parent - .canonical_ty() - .type_enum() - .bundle_type() - .expect("parent known to be bundle"); - Some( - parent_ty - .field_by_name(name) - .expect("field name known to be a valid field of parent"), - ) - } else { - None - } - } -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Target { - Base(Interned), - Child(TargetChild), -} - -impl From for Target { - fn from(value: TargetBase) -> Self { - Self::Base(Intern::intern_sized(value)) - } -} - -impl From for Target { - fn from(value: TargetChild) -> Self { - Self::Child(value) - } -} - -impl From> for Target { - fn from(value: Interned) -> Self { - Self::Base(value) - } -} - -impl Target { - pub fn base(&self) -> Interned { - let mut target = self; - loop { - match target { - Self::Base(v) => break *v, - Self::Child(v) => target = &v.parent, - } - } - } - pub fn child(&self) -> Option { - match *self { - Target::Base(_) => None, - Target::Child(v) => Some(v), - } - } - pub fn is_static(&self) -> bool { - let mut target = self; - loop { - match target { - Self::Base(_) => return true, - Self::Child(v) if !v.path_element().is_static() => return false, - Self::Child(v) => target = &v.parent, - } - } - } - #[must_use] - pub fn join(&self, path_element: Interned) -> Self { - TargetChild::new(self.intern(), path_element).into() - } - pub fn flow(&self) -> Flow { - match self { - Self::Base(v) => v.flow(), - Self::Child(v) => v.flow(), - } - } - pub fn canonical_ty(&self) -> Interned { - match self { - Target::Base(v) => v.canonical_ty(), - Target::Child(v) => v.canonical_ty(), - } - } -} - -impl fmt::Display for Target { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Base(v) => v.fmt(f), - Self::Child(v) => v.fmt(f), - } - } -} - -impl fmt::Debug for Target { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Base(v) => v.fmt(f), - Self::Child(v) => v.fmt(f), - } - } -} - -pub trait ExprTrait: ExprTraitBase { - fn expr_enum(&self) -> ExprEnum; - fn target(&self) -> Option>; - fn valueless(&self) -> Valueless { - Valueless { ty: self.ty() } - } - #[allow(clippy::result_unit_err)] - fn to_literal_bits(&self) -> Result, ()>; -} - -impl ExprTraitBase for T {} - -impl InternedCompare for dyn ExprTrait { - type InternedCompareKey = PtrEqWithTypeId; - fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { - Self::get_ptr_eq_with_type_id(this) - } - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey { - Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap()) - } -} - -pub struct SimState {} - -mod sealed { - pub trait Sealed {} -} - pub trait ToExpr { type Type: Type; - fn ty(&self) -> Self::Type; - fn to_expr(&self) -> Expr<::Value>; + fn to_expr(&self) -> Expr; +} + +impl ToExpr for Expr { + type Type = T; + + fn to_expr(&self) -> Expr { + *self + } } impl ToExpr for &'_ T { type Type = T::Type; - fn ty(&self) -> Self::Type { - (**self).ty() - } - - fn to_expr(&self) -> Expr<::Value> { - (**self).to_expr() + fn to_expr(&self) -> Expr { + T::to_expr(self) } } impl ToExpr for &'_ mut T { type Type = T::Type; - fn ty(&self) -> Self::Type { - (**self).ty() - } - - fn to_expr(&self) -> Expr<::Value> { - (**self).to_expr() + fn to_expr(&self) -> Expr { + T::to_expr(self) } } impl ToExpr for Box { type Type = T::Type; - fn ty(&self) -> Self::Type { - (**self).ty() - } - - fn to_expr(&self) -> Expr<::Value> { - (**self).to_expr() + fn to_expr(&self) -> Expr { + T::to_expr(self) } } -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct Literal { - value: T::CanonicalValue, -} +impl ToExpr for Interned { + type Type = T::Type; -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.value.fmt(f) + fn to_expr(&self) -> Expr { + T::to_expr(self) } } -impl Literal { - #[track_caller] - pub fn new_unchecked(value: T::CanonicalValue) -> Self { - assert!( - value.ty().is_passive(), - "can't have a literal with flipped fields" - ); - Self { value } - } - pub fn value(&self) -> &T::CanonicalValue { - &self.value - } - pub fn canonical(&self) -> Literal { - Literal { - value: self.value.clone(), +impl ToExpr for UIntValue { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::UIntLiteral(self.clone().as_dyn_int().intern()).intern(), + __ty: self.ty(), } } } -impl sealed::Sealed for Literal {} +impl ToExpr for SIntValue { + type Type = SIntType; -impl ToExpr for Literal { - type Type = T; - - fn ty(&self) -> Self::Type { - Self::Type::from_canonical_type(self.value.ty()) - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::new_unchecked(self.expr_enum()) + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::SIntLiteral(self.clone().as_dyn_int().intern()).intern(), + __ty: self.ty(), + } } } -impl ExprTrait for Literal { - fn expr_enum(&self) -> ExprEnum { - ExprEnum::Literal( - Literal { - value: self.value.to_canonical_dyn(), - } - .intern_sized(), - ) - } +impl ToExpr for bool { + type Type = Bool; - fn target(&self) -> Option> { - None - } - - fn to_literal_bits(&self) -> Result, ()> { - Ok(self.value.to_bits()) - } -} - -impl Fold for Literal -where - T::CanonicalValue: Fold, -{ - fn fold(self, state: &mut State) -> Result { - state.fold_literal(self) - } - fn default_fold(self, state: &mut State) -> Result { - Ok(Literal { - value: self.value.fold(state)?, - }) - } -} - -impl Visit for Literal -where - T::CanonicalValue: Visit, -{ - fn visit(&self, state: &mut State) -> Result<(), ::Error> { - state.visit_literal(self) - } - fn default_visit(&self, state: &mut State) -> Result<(), ::Error> { - self.value.visit(state) - } -} - -impl sealed::Sealed for ModuleIO {} - -impl ToExpr for ModuleIO { - type Type = T; - - fn ty(&self) -> Self::Type { - self.field_type().ty.clone() - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::new_unchecked(self.expr_enum()) - } -} - -impl ExprTrait for ModuleIO { - fn expr_enum(&self) -> ExprEnum { - ExprEnum::ModuleIO(self.to_canonical_dyn_module_io().intern_sized()) - } - - fn target(&self) -> Option> { - Some(Intern::intern_sized( - self.to_canonical_dyn_module_io().into(), - )) - } - - fn to_literal_bits(&self) -> Result, ()> { - Err(()) - } -} - -impl sealed::Sealed for Instance where T::Type: BundleType {} - -impl ToExpr for Instance -where - T::Type: BundleType, -{ - type Type = T::Type; - - fn ty(&self) -> Self::Type { - (*self.instantiated().io_ty()).clone() - } - - fn to_expr(&self) -> Expr<::Value> { - Expr::new_unchecked(self.expr_enum()) - } -} - -impl ExprTrait for Instance -where - T::Type: BundleType, -{ - fn expr_enum(&self) -> ExprEnum { - ExprEnum::Instance(self.canonical().intern_sized()) - } - - fn target(&self) -> Option> { - Some(Intern::intern_sized(self.canonical().into())) - } - - fn to_literal_bits(&self) -> Result, ()> { - Err(()) - } -} - -impl sealed::Sealed for Wire {} - -impl ExprTrait for Wire { - fn expr_enum(&self) -> ExprEnum { - ExprEnum::Wire(self.to_dyn_canonical_wire().intern_sized()) - } - - fn target(&self) -> Option> { - Some(Intern::intern_sized(self.to_dyn_canonical_wire().into())) - } - - fn to_literal_bits(&self) -> Result, ()> { - Err(()) - } -} - -impl sealed::Sealed for Reg {} - -impl ExprTrait for Reg { - fn expr_enum(&self) -> ExprEnum { - ExprEnum::Reg(self.to_dyn_canonical_reg().intern_sized()) - } - - fn target(&self) -> Option> { - Some(Intern::intern_sized(self.to_dyn_canonical_reg().into())) - } - - fn to_literal_bits(&self) -> Result, ()> { - Err(()) - } -} - -#[doc(hidden)] -pub fn value_from_expr_type(_expr: Expr, infallible: Infallible) -> V { - match infallible {} -} - -#[doc(hidden)] -pub fn check_match_expr(_expr: Expr, _check_fn: impl FnOnce(V, Infallible)) {} - -#[doc(hidden)] -#[inline] -pub fn make_enum_expr( - _check_fn: impl FnOnce(Infallible) -> V, - build: impl FnOnce(::Builder) -> Expr, -) -> Expr -where - V::Type: EnumType, -{ - build(V::Type::builder()) -} - -#[doc(hidden)] -#[inline] -pub fn make_bundle_expr( - _check_fn: impl FnOnce(Infallible) -> V, - build: impl FnOnce(::Builder) -> Expr, -) -> Expr -where - V::Type: BundleType, -{ - build(V::Type::builder()) -} - -impl sealed::Sealed for MemPort where Self: ToExpr {} - -impl ExprTrait for MemPort -where - Self: ToExpr, -{ - fn expr_enum(&self) -> ExprEnum { - ExprEnum::MemPort(self.canonical().intern_sized()) - } - fn target(&self) -> Option> { - Some(Intern::intern_sized(self.canonical().into())) - } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::BoolLiteral(*self).intern(), + __ty: Bool, + } } } diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index c1a16f1..f4f6055 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -1,1058 +1,1250 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ - array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, - bundle::{BundleType, BundleValue, DynBundleType, FieldType}, - clock::{Clock, ClockType, ToClock}, - enum_::{DynEnumType, EnumType, EnumValue, VariantType}, - expr::{ - sealed, Expr, ExprEnum, ExprTrait, Target, TargetPathArrayElement, TargetPathBundleField, - TargetPathDynArrayElement, TargetPathElement, ToExpr, - }, + array::Array, + bundle::{Bundle, BundleField, BundleType}, + enum_::{Enum, EnumType, EnumVariant}, + expr::{Expr, ExprEnum, ToExpr}, int::{ - DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, Int, - IntCmp, IntType, IntTypeTrait, IntValue, UInt, UIntType, + Bool, DynSize, IntCmp, IntType, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue, }, intern::{Intern, Interned}, - reset::{ - AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType, ToAsyncReset, - ToReset, ToSyncReset, - }, - ty::{ - CanonicalType, CanonicalValue, DynCanonicalType, DynCanonicalValue, DynType, DynValueTrait, - Type, Value, - }, - util::{interned_bit, ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}, - valueless::{Valueless, ValuelessTr}, + ty::{CanonicalType, StaticType, Type}, }; -use bitvec::{slice::BitSlice, vec::BitVec}; -use num_traits::ToPrimitive; use std::{ fmt, - hash::{Hash, Hasher}, - ops::{self, Index, Range, RangeBounds}, + ops::{ + Add, BitAnd, BitOr, BitXor, Div, Index, Mul, Neg as StdNeg, Not, Range, RangeBounds, Rem, + Shl, Shr, Sub, + }, }; -macro_rules! fixed_ary_op { +macro_rules! forward_value_to_expr_unary_op_trait { ( - pub struct $name:ident<$($T:ident,)* $(#[const] $C:ident: $CTy:ty,)*> + #[generics($($generics:tt)*)] + #[value($Value:ty)] + $Trait:ident::$method:ident + ) => { + impl<$($generics)*> $Trait for $Value where - ($($where:tt)*) + Expr<<$Value as ToExpr>::Type>: $Trait, { - $($arg_vis:vis $arg:ident: $Arg:ty,)+ - $(#[cache] - $cache_before_ty:ident: $CacheBeforeTy:ty = $cache_before_ty_expr:expr,)* - #[type$(($ty_arg:ident))?] - $ty_vis:vis $ty_name:ident: $Ty:ty = $ty_expr:expr, - $(#[cache] - $cache_after_ty:ident: $CacheAfterTy:ty = $cache_after_ty_expr:expr,)* - $(#[target] - $target_name:ident: Option> = $target_expr:expr,)? - fn simulate(&$simulate_self:ident, $sim_state:ident: &mut SimState) -> _ { - $($simulate_body:tt)+ - } + type Output = ::Type> as $Trait>::Output; - fn expr_enum(&$expr_enum_self:ident) -> _ { - $($expr_enum_body:tt)+ + fn $method(self) -> Self::Output { + $Trait::$method(self.to_expr()) } + } + }; +} - fn to_literal_bits(&$to_literal_bits_self:ident) -> Result, ()> { - $($to_literal_bits_body:tt)+ - } +macro_rules! impl_unary_op_trait { + ( + #[generics($($generics:tt)*)] + fn $Trait:ident::$method:ident($arg:ident: $Arg:ty) -> $Output:ty { + $($body:tt)* } ) => { - pub struct $name<$($T,)* $(const $C: $CTy,)*> - where - $($where)* + impl<$($generics)*> $Trait for Expr<$Arg> { - $($arg_vis $arg: $Arg,)+ - $($cache_before_ty: $CacheBeforeTy,)* - $ty_vis $ty_name: $Ty, - $($cache_after_ty: $CacheAfterTy,)* - $($target_name: Option>,)? + type Output = Expr<$Output>; + + fn $method(self) -> Self::Output { + let $arg = self; + $($body)* + } + } + }; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct NotU { + pub arg: Expr>, +} + +impl ToExpr for NotU { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotU(NotU { + arg: Expr::as_dyn_int(self.arg), + }) + .intern(), + __ty: self.arg.__ty, + } + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn Not::not(arg: UIntType) -> UIntType { + NotU { arg }.to_expr() + } +} + +forward_value_to_expr_unary_op_trait! { + #[generics(Width: Size)] + #[value(UIntValue)] + Not::not +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct NotS { + pub arg: Expr>, +} + +impl ToExpr for NotS { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotS(NotS { + arg: Expr::as_dyn_int(self.arg), + }) + .intern(), + __ty: self.arg.__ty.as_same_width_uint(), + } + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn Not::not(arg: SIntType) -> UIntType { + NotS { arg }.to_expr() + } +} + +forward_value_to_expr_unary_op_trait! { + #[generics(Width: Size)] + #[value(SIntValue)] + Not::not +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct NotB { + pub arg: Expr, +} + +impl ToExpr for NotB { + type Type = Bool; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotB(NotB { arg: self.arg }).intern(), + __ty: self.arg.__ty, + } + } +} + +impl_unary_op_trait! { + #[generics()] + fn Not::not(arg: Bool) -> Bool { + NotB { arg }.to_expr() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Neg { + pub arg: Expr, +} + +impl ToExpr for Neg { + type Type = SInt; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Neg(*self).intern(), + __ty: SInt::new_dyn( + Expr::ty(self.arg) + .width() + .checked_add(1) + .expect("width too big"), + ), + } + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn StdNeg::neg(arg: SIntType) -> SInt { + Neg { arg: Expr::as_dyn_int(arg) }.to_expr() + } +} + +forward_value_to_expr_unary_op_trait! { + #[generics(Width: Size)] + #[value(SIntValue)] + StdNeg::neg +} + +macro_rules! impl_binary_op_trait { + ( + #[generics($($generics:tt)*)] + fn $Trait:ident::$method:ident($lhs:ident: $Lhs:ty, $rhs:ident: $Rhs:ty) -> $Output:ty { + $($body:tt)* + } + ) => { + impl< + Rhs: ToExpr, + $($generics)* + > $Trait for Expr<$Lhs> + { + type Output = Expr<$Output>; + + fn $method(self, rhs: Rhs) -> Self::Output { + let $lhs = self; + let $rhs = rhs.to_expr(); + $($body)* + } + } + }; +} + +macro_rules! forward_value_to_expr_binary_op_trait { + ( + #[generics($($generics:tt)*)] + #[lhs_value($LhsValue:ty)] + $Trait:ident::$method:ident + ) => { + impl< + Rhs, + $($generics)* + > $Trait for $LhsValue + where + Expr<<$LhsValue as ToExpr>::Type>: $Trait, + { + type Output = ::Type> as $Trait>::Output; + + fn $method(self, rhs: Rhs) -> Self::Output { + $Trait::$method(self.to_expr(), rhs) + } + } + }; +} + +macro_rules! binary_op_bitwise { + ($name:ident, $ty:ident, bool, $Trait:ident::$method:ident) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + pub lhs: Expr<$ty>, + pub rhs: Expr<$ty>, } - impl<$($T,)* $(const $C: $CTy,)*> $name<$($T,)* $($C,)*> - where - $($where)* - { - pub fn new_unchecked($($arg: $Arg,)* $($ty_arg: $Ty,)?) -> Self { - $(let $cache_before_ty: $CacheBeforeTy = $cache_before_ty_expr;)* - let $ty_name: $Ty = $ty_expr; - $(let $cache_after_ty: $CacheAfterTy = $cache_after_ty_expr;)* - $(let $target_name: Option> = $target_expr;)? - Self { - $($arg,)* - $($cache_before_ty,)* - $ty_name, - $($cache_after_ty,)* - $($target_name,)? + impl ToExpr for $name { + type Type = $ty; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: $ty, } } - pub fn canonical(&self) -> $name<$($T::CanonicalType,)* $($C,)*> { + } + + /// intentionally only implemented for expressions, not rust's bool type, + /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do + /// what is usually wanted. + impl $Trait for Expr { + type Output = Expr; + + fn $method(self, rhs: Expr) -> Expr { + $name { lhs: self, rhs }.to_expr() + } + } + }; + ($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + pub lhs: Expr<$ty>, + pub rhs: Expr<$ty>, + } + + impl ToExpr for $name { + type Type = UInt; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: UInt::new_dyn(Expr::ty(self.lhs).width().max(Expr::ty(self.lhs).width())), + } + } + } + + impl_binary_op_trait! { + #[generics(LhsWidth: Size, RhsWidth: Size)] + fn $Trait::$method(lhs: $ty, rhs: $ty) -> UInt { $name { - $($arg: self.$arg.clone(),)* - $($cache_before_ty: self.$cache_before_ty.clone(),)* - $ty_name: self.$ty_name.canonical(), - $($cache_after_ty: self.$cache_after_ty.clone(),)* - $($target_name: self.$target_name,)? + lhs: Expr::as_dyn_int(lhs), + rhs: Expr::as_dyn_int(rhs), } + .to_expr() } } - impl<$($T,)* $(const $C: $CTy,)*> Clone for $name<$($T,)* $($C,)*> - where - $($where)* - { - fn clone(&self) -> Self { - Self { - $($arg: self.$arg.clone(),)* - $($cache_before_ty: self.$cache_before_ty.clone(),)* - $ty_name: self.$ty_name.clone(), - $($cache_after_ty: self.$cache_after_ty.clone(),)* - $($target_name: self.$target_name,)? - } - } - } - - impl<$($T,)* $(const $C: $CTy,)*> PartialEq for $name<$($T,)* $($C,)*> - where - $($where)* - { - fn eq(&self, rhs: &Self) -> bool { - $(self.$arg == rhs.$arg &&)* self.$ty_name == rhs.$ty_name - } - } - - impl<$($T,)* $(const $C: $CTy,)*> Eq for $name<$($T,)* $($C,)*> - where - $($where)* - { - } - - impl<$($T,)* $(const $C: $CTy,)*> Hash for $name<$($T,)* $($C,)*> - where - $($where)* - { - fn hash(&self, state: &mut H) { - $(self.$arg.hash(state);)* - self.$ty_name.hash(state); - } - } - - impl<$($T,)* $(const $C: $CTy,)*> fmt::Debug for $name<$($T,)* $($C,)*> - where - $($where)* - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - f.debug_tuple(stringify!($name))$(.field(&self.$arg))*.finish() - } - } - - impl<$($T,)* $(const $C: $CTy,)*> ToExpr for $name<$($T,)* $($C,)*> - where - $($where)* - { - type Type = $Ty; - - fn ty(&self) -> Self::Type { - self.$ty_name.clone() - } - fn to_expr(&self) -> Expr<::Value> { - Expr::new_unchecked(self.expr_enum()) - } - } - - impl<$($T,)* $(const $C: $CTy,)*> ExprTrait for $name<$($T,)* $($C,)*> - where - $($where)* - { - fn expr_enum(&$expr_enum_self) -> ExprEnum { - $($expr_enum_body)+ - } - fn target(&self) -> Option> { - ($(self.$target_name,)? None::>,).0 - } - fn to_literal_bits(&$to_literal_bits_self) -> Result, ()> { - $($to_literal_bits_body)+ - } - } - - impl<$($T,)* $(const $C: $CTy,)*> sealed::Sealed for $name<$($T,)* $($C,)*> - where - $($where)* - { + forward_value_to_expr_binary_op_trait! { + #[generics(LhsWidth: Size)] + #[lhs_value($value)] + $Trait::$method } }; } -macro_rules! unary_op { - ( - #[method = $op:ident] - impl<$T:ident> $Op:ident for _ where ($($where:tt)*) { - fn expr_enum(&$expr_enum_self:ident) -> _ { - $($expr_enum_body:tt)+ - } +binary_op_bitwise!(BitAndU, UIntType, UIntValue, BitAnd::bitand); +binary_op_bitwise!(BitAndS, SIntType, SIntValue, BitAnd::bitand); +binary_op_bitwise!(BitAndB, Bool, bool, BitAnd::bitand); +binary_op_bitwise!(BitOrU, UIntType, UIntValue, BitOr::bitor); +binary_op_bitwise!(BitOrS, SIntType, SIntValue, BitOr::bitor); +binary_op_bitwise!(BitOrB, Bool, bool, BitOr::bitor); +binary_op_bitwise!(BitXorU, UIntType, UIntValue, BitXor::bitxor); +binary_op_bitwise!(BitXorS, SIntType, SIntValue, BitXor::bitxor); +binary_op_bitwise!(BitXorB, Bool, bool, BitXor::bitxor); + +macro_rules! binary_op_add_sub { + ($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + pub lhs: Expr<$ty>, + pub rhs: Expr<$ty>, } - ) => { - fixed_ary_op! { - pub struct $Op<$T,> - where ( - $($where)* - ) - { - pub arg: Expr<$T::CanonicalValue>, - #[type] - ty: < as ops::$Op>::Output as ValuelessTr>::Type = ops::$Op::$op( - Valueless::<$T>::from_canonical(arg.valueless()), - ).ty, - #[cache] - literal_bits: Result, ()> = { - arg.to_literal_bits() - .map(|v| ops::$Op::$op($T::CanonicalValue::from_bit_slice(&v)).to_bits()) - }, - fn simulate(&self, sim_state: &mut SimState) -> _ { - ops::$Op::$op(self.arg.simulate(sim_state)) - } + impl ToExpr for $name { + type Type = $ty; - fn expr_enum(&$expr_enum_self) -> _ { - $($expr_enum_body)+ - } - - fn to_literal_bits(&self) -> Result, ()> { - self.literal_bits + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: $ty::new_dyn( + Expr::ty(self.lhs) + .width() + .max(Expr::ty(self.rhs).width()) + .checked_add(1) + .expect("width too big"), + ), } } } - impl<$T, V: Value> ops::$Op for Expr - where - $($where)* - { - type Output = Expr<< as ops::$Op>::Output as ValuelessTr>::Value>; - - fn $op(self) -> Self::Output { - $Op::<$T>::new_unchecked(self.canonical()).to_expr() + impl_binary_op_trait! { + #[generics(LhsWidth: Size, RhsWidth: Size)] + fn $Trait::$method(lhs: $ty, rhs: $ty) -> $ty { + $name { + lhs: Expr::as_dyn_int(lhs), + rhs: Expr::as_dyn_int(rhs), + } + .to_expr() } } + + forward_value_to_expr_binary_op_trait! { + #[generics(LhsWidth: Size)] + #[lhs_value($value)] + $Trait::$method + } }; } -unary_op! { - #[method = not] - impl Not for _ - where ( - T: IntTypeTrait< - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - ) - { - fn expr_enum(&self) -> _ { - struct Tag; - impl ConstBoolDispatchTag for Tag { - type Type = Not>; +binary_op_add_sub!(AddU, UIntType, UIntValue, Add::add); +binary_op_add_sub!(AddS, SIntType, SIntValue, Add::add); +binary_op_add_sub!(SubU, UIntType, UIntValue, Sub::sub); +binary_op_add_sub!(SubS, SIntType, SIntValue, Sub::sub); + +macro_rules! binary_op_mul { + ($name:ident, $ty:ident, $value:ident) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + pub lhs: Expr<$ty>, + pub rhs: Expr<$ty>, + } + + impl ToExpr for $name { + type Type = $ty; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: $ty::new_dyn( + Expr::ty(self.lhs) + .width() + .checked_add(Expr::ty(self.rhs).width()) + .expect("width too big"), + ), + } } - match ConstBoolDispatch::new::(self.canonical()) { - ConstBoolDispatch::False(v) => ExprEnum::NotU(v.intern_sized()), - ConstBoolDispatch::True(v) => ExprEnum::NotS(v.intern_sized()), + } + + impl_binary_op_trait! { + #[generics(LhsWidth: Size, RhsWidth: Size)] + fn Mul::mul(lhs: $ty, rhs: $ty) -> $ty { + $name { + lhs: Expr::as_dyn_int(lhs), + rhs: Expr::as_dyn_int(rhs), + } + .to_expr() } } + + forward_value_to_expr_binary_op_trait! { + #[generics(LhsWidth: Size)] + #[lhs_value($value)] + Mul::mul + } + }; +} + +binary_op_mul!(MulU, UIntType, UIntValue); +binary_op_mul!(MulS, SIntType, SIntValue); + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct DivU { + pub lhs: Expr>, + pub rhs: Expr, +} + +impl ToExpr for DivU { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::DivU(DivU { + lhs: Expr::as_dyn_int(self.lhs), + rhs: self.rhs, + }) + .intern(), + __ty: Expr::ty(self.lhs), + } } } -unary_op! { - #[method = neg] - impl Neg for _ - where ( - T: IntTypeTrait< - Signed = ConstBool, - CanonicalType = DynSIntType, - CanonicalValue = DynSInt, - >, - ) - { - fn expr_enum(&self) -> _ { - ExprEnum::Neg(self.canonical().intern_sized()) +impl_binary_op_trait! { + #[generics(LhsWidth: Size, RhsWidth: Size)] + fn Div::div(lhs: UIntType, rhs: UIntType) -> UIntType { + DivU { + lhs, + rhs: Expr::as_dyn_int(rhs), + } + .to_expr() + } +} + +forward_value_to_expr_binary_op_trait! { + #[generics(LhsWidth: Size)] + #[lhs_value(UIntValue)] + Div::div +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct DivS { + pub lhs: Expr, + pub rhs: Expr, +} + +impl ToExpr for DivS { + type Type = SInt; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::DivS(*self).intern(), + __ty: SInt::new_dyn( + Expr::ty(self.lhs) + .width() + .checked_add(1) + .expect("width too big"), + ), } } } -macro_rules! binary_op { - ( - #[ - method = $op:ident, - rhs_to_canonical_dyn = $rhs_to_canonical_dyn:ident, - expr_enum_u = $expr_enum_u:ident, - expr_enum_s = $expr_enum_s:ident - ] - impl<$LhsType:ident, $RhsType:ident> $Op:ident for _ where $($where:tt)* - ) => { - fixed_ary_op! { - pub struct $Op<$LhsType, $RhsType,> - where ( - $($where)* - ) - { - pub lhs: Expr<$LhsType::CanonicalValue>, - pub rhs: Expr<$RhsType::CanonicalValue>, - #[type] - ty: < - as ops::$Op>>::Output - as ValuelessTr - >::Type = ops::$Op::$op( - Valueless::<$LhsType>::from_canonical(lhs.valueless()), - Valueless::<$RhsType>::from_canonical(rhs.valueless()), - ).ty, - #[cache] - literal_bits: Result, ()> = { - lhs.to_literal_bits() - .ok() - .zip(rhs.to_literal_bits().ok()) - .map(|(lhs, rhs)| { - ops::$Op::$op( - $LhsType::CanonicalValue::from_bit_slice(&lhs), - $RhsType::CanonicalValue::from_bit_slice(&rhs), - ) - .to_bits() - }) - .ok_or(()) - }, - fn simulate(&self, sim_state: &mut SimState) -> _ { - ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs.simulate(sim_state)) - } - fn expr_enum(&self) -> _ { - struct Tag; - impl ConstBoolDispatchTag for Tag { - type Type = - $Op, DynIntType, DynUIntType>; - } - match ConstBoolDispatch::new::(self.canonical()) { - ConstBoolDispatch::False(v) => ExprEnum::$CmpOpU(v.intern_sized()), - ConstBoolDispatch::True(v) => ExprEnum::$CmpOpS(v.intern_sized()), - } - } +impl IntCmp for Lhs +where + Lhs::Type: IntCmpExpr, +{ + fn cmp_eq(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_eq(self.to_expr(), rhs.to_expr()) + } + fn cmp_ne(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_ne(self.to_expr(), rhs.to_expr()) + } + fn cmp_lt(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_lt(self.to_expr(), rhs.to_expr()) + } + fn cmp_le(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_le(self.to_expr(), rhs.to_expr()) + } + fn cmp_gt(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_gt(self.to_expr(), rhs.to_expr()) + } + fn cmp_ge(self, rhs: Rhs) -> Expr { + IntCmpExpr::cmp_ge(self.to_expr(), rhs.to_expr()) + } +} - fn to_literal_bits(&self) -> Result, ()> { - self.literal_bits +macro_rules! impl_compare_op { + ( + $(#[width($LhsWidth:ident, $RhsWidth:ident)])? + #[dyn_type($DynTy:ident)] + #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] + #[type($Lhs:ty, $Rhs:ty)] + $( + struct $name:ident; + fn $method:ident(); + )* + ) => { + $(#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $name { + pub lhs: Expr<$DynTy>, + pub rhs: Expr<$DynTy>, + } + + impl ToExpr for $name { + type Type = Bool; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: Bool, } } })* - impl< - LhsType: IntTypeTrait< - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - RhsType: IntTypeTrait< - Signed = LhsType::Signed, - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - Rhs: ToExpr, - Lhs: Value, - > IntCmp for Expr - { - type Output = Expr>; - - $(fn $fn(self, rhs: Rhs) -> Self::Output { - $CmpOp::>::new_unchecked( - self.canonical(), - rhs.to_expr().canonical(), - ).to_expr() - })* - } - - impl< - LhsType: IntTypeTrait< - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - RhsType: IntTypeTrait< - Signed = LhsType::Signed, - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - Rhs: ToExpr, - > IntCmp for IntValue { - type Output = Expr>; - - $(fn $fn(self, rhs: Rhs) -> Self::Output { - self.to_expr().$fn(rhs) - })* - } - - impl< - LhsType: IntTypeTrait, - RhsType: IntTypeTrait, - > IntCmp> for Valueless { - type Output = Valueless>; - - $(fn $fn(self, _rhs: Valueless) -> Self::Output { - Valueless { ty: UIntType::new() } + impl$(<$LhsWidth: Size, $RhsWidth: Size>)? IntCmpExpr<$Rhs> for $Lhs { + $(fn $method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { + $name { + lhs: $dyn_lhs, + rhs: $dyn_rhs, + }.to_expr() })* } }; } -cmp_op! { - CmpLt, CmpLtU, CmpLtS, cmp_lt, PartialOrd::lt; - CmpLe, CmpLeU, CmpLeS, cmp_le, PartialOrd::le; - CmpGt, CmpGtU, CmpGtS, cmp_gt, PartialOrd::gt; - CmpGe, CmpGeU, CmpGeS, cmp_ge, PartialOrd::ge; - CmpEq, CmpEqU, CmpEqS, cmp_eq, PartialEq::eq; - CmpNe, CmpNeU, CmpNeS, cmp_ne, PartialEq::ne; +impl_compare_op! { + #[dyn_type(Bool)] + #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[type(Bool, Bool)] + struct CmpEqB; fn cmp_eq(); + struct CmpNeB; fn cmp_ne(); + struct CmpLtB; fn cmp_lt(); + struct CmpLeB; fn cmp_le(); + struct CmpGtB; fn cmp_gt(); + struct CmpGeB; fn cmp_ge(); } -fixed_ary_op! { - pub struct CastInt - where ( - FromType: IntTypeTrait< - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - ToType: IntTypeTrait< - CanonicalType = DynIntType<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - ) - { - pub value: Expr, - #[type(ty)] - pub ty: ToType = ty, - #[cache] - literal_bits: Result, ()> = { - value.to_literal_bits().map(|literal_bits| { - let mut bits = literal_bits.to_bitvec(); - let fill = FromType::Signed::VALUE - && bits.len().checked_sub(1).map(|i| bits[i]).unwrap_or(false); - bits.resize(ty.width(), fill); - Intern::intern_owned(bits) - }) - }, - - fn simulate(&self, sim_state: &mut SimState) -> _ { - self.value.simulate(sim_state).cast_as_type(self.ty.canonical()) - } - - fn expr_enum(&self) -> _ { - struct Tag1; - impl ConstBoolDispatchTag for Tag1 { - type Type = ConstBoolDispatch< - CastInt, DynUIntType>, - CastInt, DynSIntType>, - >; - } - struct Tag2(FromSigned); - impl ConstBoolDispatchTag for Tag2 { - type Type = - CastInt, DynIntType