From 883668583d9966c83806375d244e06b7f23e36c8 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 | 841 ++++ .../fayalite-proc-macros-impl/src/hdl_enum.rs | 656 +++ .../src/hdl_type_common.rs | 2981 +++++++++++++ crates/fayalite-proc-macros-impl/src/lib.rs | 182 +- .../src/module/transform_body.rs | 5 +- crates/fayalite-proc-macros/src/lib.rs | 12 + crates/fayalite/src/annotations.rs | 2 +- crates/fayalite/src/array.rs | 754 +--- crates/fayalite/src/bundle.rs | 969 ++--- crates/fayalite/src/clock.rs | 126 +- crates/fayalite/src/enum_.rs | 665 +-- crates/fayalite/src/expr.rs | 1342 ++---- crates/fayalite/src/expr/ops.rs | 3759 +++++++++-------- crates/fayalite/src/expr/target.rs | 398 ++ crates/fayalite/src/int.rs | 1769 ++------ crates/fayalite/src/intern.rs | 179 + crates/fayalite/src/lib.rs | 130 +- crates/fayalite/src/memory.rs | 340 +- crates/fayalite/src/reg.rs | 68 +- crates/fayalite/src/reset.rs | 391 +- crates/fayalite/src/ty.rs | 1073 +---- crates/fayalite/src/util.rs | 3 + crates/fayalite/src/util/const_usize.rs | 29 + crates/fayalite/src/wire.rs | 45 +- crates/fayalite/tests/value_derive.rs | 24 +- 26 files changed, 9207 insertions(+), 7557 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/expr/target.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..32a2a45 --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -0,0 +1,841 @@ +use crate::{ + hdl_type_common::{ + common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, + ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, + }, + kw, Errors, HdlAttr, PairsIterExt, +}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote_spanned, ToTokens}; +use syn::{ + parse_quote, parse_quote_spanned, + punctuated::{Pair, Punctuated}, + spanned::Spanned, + token::Brace, + AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed, + GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility, +}; + +#[derive(Clone, Debug)] +pub(crate) struct ParsedBundle { + pub(crate) attrs: Vec, + 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 param in retval.params.iter_mut() { + match param { + GenericParam::Lifetime(_) => {} + GenericParam::Type(param) => param.default = None, + GenericParam::Const(param) => param.default = None, + } + } + for (field_index, field) in self.fields.named.iter().enumerate() { + match get_field_state(field_index) { + BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue, + BuilderFieldState::Generic => {} + } + if !retval.params.empty_or_trailing() { + retval.params.push_punct(Token![,](self.ident.span())); + } + retval.params.push_value(GenericParam::Type( + type_var_for_field_name(field.ident.as_ref().unwrap()).into(), + )); + } + retval + } + fn builder_struct_ty( + &self, + mut get_field_state: impl FnMut(usize) -> BuilderFieldState, + ) -> Type { + let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() { + parse_quote_spanned! {self.ident.span()=> + <> + } + } else { + let builder_type_generics = self.generics.split_for_impl().1; + parse_quote! { #builder_type_generics } + }; + for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() { + let ident = ident.as_ref().unwrap(); + if !ty_arguments.args.empty_or_trailing() { + ty_arguments.args.push_punct(Token![,](self.ident.span())); + } + ty_arguments + .args + .push_value(match get_field_state(field_index) { + BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=> + ::fayalite::bundle::Unfilled<#ty> + }, + BuilderFieldState::Generic => { + let type_var = type_var_for_field_name(ident); + parse_quote_spanned! {self.ident.span()=> + #type_var + } + } + BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=> + ::fayalite::expr::Expr<#ty> + }, + }); + } + let ident = &self.ident; + parse_quote_spanned! {ident.span()=> + #ident #ty_arguments + } + } +} + +fn type_var_for_field_name(ident: &Ident) -> Ident { + format_ident!("__T_{}", ident) +} + +fn field_fn_for_field_name(ident: &Ident) -> Ident { + format_ident!("field_{}", ident) +} + +impl ToTokens for Builder { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + vis, + struct_token, + ident, + target, + generics: _, + fields, + } = self; + let phantom_field_name = self.phantom_field_name(); + let builder_struct = ItemStruct { + attrs: vec![parse_quote_spanned! {ident.span()=> + #[allow(non_camel_case_types, dead_code)] + }], + vis: vis.clone(), + struct_token: *struct_token, + ident: ident.clone(), + generics: self.builder_struct_generics(|_| BuilderFieldState::Generic), + fields: Fields::Named(FieldsNamed { + brace_token: fields.brace_token, + named: Punctuated::from_iter( + [Pair::Punctuated( + self.phantom_field(), + Token![,](self.ident.span()), + )] + .into_iter() + .chain(fields.named.pairs().map_pair_value_ref(|field| { + let ident = field.ident.as_ref().unwrap(); + let type_var = type_var_for_field_name(ident); + Field { + vis: Visibility::Inherited, + ty: parse_quote_spanned! {ident.span()=> + #type_var + }, + ..field.clone() + } + })), + ), + }), + semi_token: None, + }; + builder_struct.to_tokens(tokens); + let field_idents = Vec::from_iter( + self.fields + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()), + ); + for ( + field_index, + Field { + vis, + ident: field_ident, + ty, + .. + }, + ) in self.fields.named.iter().enumerate() + { + let field_ident = field_ident.as_ref().unwrap(); + let fn_ident = field_fn_for_field_name(field_ident); + let fn_generics = self.builder_struct_generics(|i| { + if i == field_index { + BuilderFieldState::Unfilled + } else { + BuilderFieldState::Generic + } + }); + let (impl_generics, _, where_clause) = fn_generics.split_for_impl(); + let unfilled_ty = self.builder_struct_ty(|i| { + if i == field_index { + BuilderFieldState::Unfilled + } else { + BuilderFieldState::Generic + } + }); + let filled_ty = self.builder_struct_ty(|i| { + if i == field_index { + BuilderFieldState::Filled + } else { + BuilderFieldState::Generic + } + }); + let pat_fields = + Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| { + let field_ident = field.ident.as_ref().unwrap(); + if field_index == i { + quote_spanned! {self.ident.span()=> + #field_ident: _, + } + } else { + quote_spanned! {self.ident.span()=> + #field_ident, + } + } + })); + quote_spanned! {self.ident.span()=> + #[automatically_derived] + #[allow(non_camel_case_types, dead_code)] + impl #impl_generics #unfilled_ty + #where_clause + { + #vis fn #fn_ident( + self, + #field_ident: ::fayalite::expr::Expr<#ty>, + ) -> #filled_ty { + let Self { + #phantom_field_name: _, + #(#pat_fields)* + } = self; + #ident { + #phantom_field_name: ::fayalite::__std::marker::PhantomData, + #(#field_idents,)* + } + } + } + } + .to_tokens(tokens); + } + let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled); + let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled); + let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl(); + quote_spanned! {self.ident.span()=> + #[automatically_derived] + #[allow(non_camel_case_types, dead_code)] + impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty + #unfilled_where_clause + { + fn default() -> Self { + #ident { + #phantom_field_name: ::fayalite::__std::marker::PhantomData, + #(#field_idents: ::fayalite::__std::default::Default::default(),)* + } + } + } + } + .to_tokens(tokens); + let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled); + let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled); + let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl(); + let type_generics = self.generics.split_for_impl().1; + quote_spanned! {self.ident.span()=> + #[automatically_derived] + #[allow(non_camel_case_types, dead_code)] + impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty + #filled_where_clause + { + type Type = #target #type_generics; + fn to_expr( + &self, + ) -> ::fayalite::expr::Expr<::Type> { + let __ty = #target { + #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* + }; + let __field_values = [ + #(::fayalite::expr::Expr::canonical(self.#field_idents),)* + ]; + ::fayalite::expr::ToExpr::to_expr( + &::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values), + ) + } + } + } + .to_tokens(tokens); + } +} + +impl ToTokens for ParsedBundle { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + vis, + struct_token, + ident, + generics, + fields, + field_flips, + mask_type_ident, + mask_type_match_variant_ident, + match_variant_ident, + builder_ident, + mask_type_builder_ident, + } = self; + let ItemOptions { + outline_generated: _, + connect_inexact, + target, + custom_bounds: _, + no_static, + } = &options.body; + let target = get_target(target, ident); + let mut item_attrs = attrs.clone(); + item_attrs.push(common_derives(ident.span())); + ItemStruct { + attrs: item_attrs, + vis: vis.clone(), + struct_token: *struct_token, + ident: ident.clone(), + generics: generics.into(), + fields: Fields::Named(fields.clone().into()), + semi_token: None, + } + .to_tokens(tokens); + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields)) = (generics, fields) { + generics.make_runtime_generics(tokens, vis, ident, &target, |context| { + let fields: Vec<_> = fields + .named + .iter() + .map(|ParsedField { ident, ty, .. }| { + let ident = ident.as_ref().unwrap(); + let expr = ty.make_hdl_type_expr(context); + quote_spanned! {ident.span()=> + #ident: #expr, + } + }) + .collect(); + parse_quote_spanned! {ident.span()=> + #target { + #(#fields)* + } + } + }) + } + let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let tokens = wrapped_in_const.inner(); + let builder = Builder { + vis: vis.clone(), + struct_token: *struct_token, + ident: builder_ident.clone(), + target: target.clone(), + generics: generics.into(), + fields: fields.clone().into(), + }; + builder.to_tokens(tokens); + let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); + let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled); + let mut mask_type_fields = FieldsNamed::from(fields.clone()); + for Field { ident, ty, .. } in &mut mask_type_fields.named { + let ident = ident.as_ref().unwrap(); + *ty = parse_quote_spanned! {ident.span()=> + <#ty as ::fayalite::ty::Type>::MaskType + }; + } + let mask_type_builder = Builder { + vis: vis.clone(), + struct_token: *struct_token, + ident: mask_type_builder_ident.clone(), + target: mask_type_ident.clone().into(), + generics: generics.into(), + fields: mask_type_fields.clone(), + }; + mask_type_builder.to_tokens(tokens); + let unfilled_mask_type_builder_ty = + mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); + let filled_mask_type_builder_ty = + mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); + ItemStruct { + attrs: vec![ + common_derives(ident.span()), + parse_quote_spanned! {ident.span()=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: mask_type_ident.clone(), + generics: generics.into(), + fields: Fields::Named(mask_type_fields.clone()), + semi_token: None, + } + .to_tokens(tokens); + let mut mask_type_match_variant_fields = mask_type_fields; + for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named { + let ident = ident.as_ref().unwrap(); + *ty = parse_quote_spanned! {ident.span()=> + ::fayalite::expr::Expr<#ty> + }; + } + ItemStruct { + attrs: vec![ + common_derives(ident.span()), + parse_quote_spanned! {ident.span()=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: mask_type_match_variant_ident.clone(), + generics: generics.into(), + fields: Fields::Named(mask_type_match_variant_fields), + semi_token: None, + } + .to_tokens(tokens); + let mut match_variant_fields = FieldsNamed::from(fields.clone()); + for Field { ident, ty, .. } in &mut match_variant_fields.named { + let ident = ident.as_ref().unwrap(); + *ty = parse_quote_spanned! {ident.span()=> + ::fayalite::expr::Expr<#ty> + }; + } + ItemStruct { + attrs: vec![ + common_derives(ident.span()), + parse_quote_spanned! {ident.span()=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: match_variant_ident.clone(), + generics: generics.into(), + fields: Fields::Named(match_variant_fields), + semi_token: None, + } + .to_tokens(tokens); + let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let ident_str = ident.to_string(); + quote_spanned! {ident.span()=> + #ident: ::fayalite::expr::Expr::field(__this, #ident_str), + } + })); + let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {ident.span()=> + #ident: ::fayalite::ty::Type::mask_type(&self.#ident), + } + })); + let from_canonical_body_fields = + Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map( + |((index, field), flip)| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let ident_str = ident.to_string(); + let flipped = flip.is_some(); + quote_spanned! {ident.span()=> + #ident: { + let ::fayalite::bundle::BundleField { + name: __name, + flipped: __flipped, + ty: __ty, + } = __fields[#index]; + ::fayalite::__std::assert_eq!(&*__name, #ident_str); + ::fayalite::__std::assert_eq!(__flipped, #flipped); + ::fayalite::ty::Type::from_canonical(__ty) + }, + } + }, + )); + let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map( + |(field, flip)| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let ident_str = ident.to_string(); + let flipped = flip.is_some(); + quote_spanned! {ident.span()=> + ::fayalite::bundle::BundleField { + name: ::fayalite::intern::Intern::intern(#ident_str), + flipped: #flipped, + ty: ::fayalite::ty::Type::canonical(&self.#ident), + }, + } + }, + )); + let fields_len = fields.named().into_iter().len(); + quote_spanned! {ident.span()=> + #[automatically_derived] + impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics + #where_clause + { + type MaskType = #mask_type_ident #type_generics; + type MatchVariant = #mask_type_match_variant_ident #type_generics; + type MatchActiveScope = (); + type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< + ::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(); + let ty = field.ty(); + quote_spanned! {ident.span()=> + #ident: <#ty as ::fayalite::ty::StaticType>::TYPE, + } + })); + let static_mask_type_body_fields = + Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let ty = field.ty(); + quote_spanned! {ident.span()=> + #ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE, + } + })); + let type_properties = format_ident!("__type_properties", span = ident.span()); + let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let flipped = field_flip.is_some(); + let ty = field.ty(); + quote_spanned! {ident.span()=> + let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES); + } + })); + let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { + let ident: &Ident = field.ident().as_ref().unwrap(); + let flipped = field_flip.is_some(); + let ty = field.ty(); + quote_spanned! {ident.span()=> + let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES); + } + })); + quote_spanned! {ident.span()=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics + #static_where_clause + { + const TYPE: Self = Self { + #(#static_mask_type_body_fields)* + }; + const MASK_TYPE: ::MaskType = Self { + #(#static_mask_type_body_fields)* + }; + const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { + let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); + #(#type_properties_mask_fields)* + #type_properties.finish() + }; + const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { + let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); + #(#type_properties_mask_fields)* + #type_properties.finish() + }; + } + #[automatically_derived] + impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics + #static_where_clause + { + const TYPE: Self = Self { + #(#static_type_body_fields)* + }; + const MASK_TYPE: ::MaskType = #mask_type_ident { + #(#static_mask_type_body_fields)* + }; + const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { + let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); + #(#type_properties_fields)* + #type_properties.finish() + }; + const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { + let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); + #(#type_properties_mask_fields)* + #type_properties.finish() + }; + } + } + .to_tokens(tokens); + } + } +} + +pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result { + 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..a5f82b0 --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -0,0 +1,656 @@ +use crate::{ + hdl_type_common::{ + common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, + ParsedType, SplitForImpl, TypesParser, WrappedInConst, + }, + Errors, HdlAttr, PairsIterExt, +}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote_spanned, ToTokens}; +use syn::{ + parse_quote_spanned, + punctuated::{Pair, Punctuated}, + token::{Brace, Paren}, + Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, + ItemEnum, ItemStruct, Token, Type, Variant, Visibility, +}; + +crate::options! { + #[options = VariantOptions] + pub(crate) enum VariantOption {} +} + +crate::options! { + #[options = FieldOptions] + pub(crate) enum FieldOption {} +} + +#[derive(Clone, Debug)] +pub(crate) struct ParsedVariantField { + pub(crate) paren_token: Paren, + pub(crate) attrs: Vec, + 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(); + if let MaybeParsed::Parsed(generics) = generics { + generics.make_runtime_generics(tokens, vis, ident, &target, |context| { + let fields: Vec<_> = variants + .iter() + .map(|ParsedVariant { ident, field, .. }| { + if let Some(ParsedVariantField { + ty: MaybeParsed::Parsed(ty), + .. + }) = field + { + let expr = ty.make_hdl_type_expr(context); + quote_spanned! {ident.span()=> + #ident: #expr, + } + } else { + quote_spanned! {ident.span()=> + #ident: (), + } + } + }) + .collect(); + parse_quote_spanned! {ident.span()=> + #target { + #(#fields)* + } + } + }) + } + let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let tokens = wrapped_in_const.inner(); + { + let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let tokens = wrapped_in_const.inner(); + let mut enum_attrs = attrs.clone(); + enum_attrs.push(parse_quote_spanned! {ident.span()=> + #[allow(dead_code)] + }); + ItemEnum { + attrs: enum_attrs, + vis: vis.clone(), + enum_token: *enum_token, + ident: ident.clone(), + generics: generics.into(), + brace_token: *brace_token, + variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref( + |ParsedVariant { + attrs, + options: _, + ident, + field, + }| Variant { + attrs: attrs.clone(), + ident: ident.clone(), + fields: match field { + Some(ParsedVariantField { + paren_token, + attrs, + options: _, + ty, + comma_token, + }) => Fields::Unnamed(FieldsUnnamed { + paren_token: *paren_token, + unnamed: Punctuated::from_iter([Pair::new( + Field { + attrs: attrs.clone(), + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: ty.clone().into(), + }, + *comma_token, + )]), + }), + None => Fields::Unit, + }, + discriminant: None, + }, + )), + } + .to_tokens(tokens); + } + let mut enum_attrs = attrs.clone(); + enum_attrs.push(parse_quote_spanned! {ident.span()=> + #[allow(dead_code, non_camel_case_types)] + }); + ItemEnum { + attrs: enum_attrs, + vis: vis.clone(), + enum_token: *enum_token, + ident: match_variant_ident.clone(), + generics: generics.into(), + brace_token: *brace_token, + variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref( + |ParsedVariant { + attrs, + options: _, + ident, + field, + }| Variant { + attrs: attrs.clone(), + ident: ident.clone(), + fields: match field { + Some(ParsedVariantField { + paren_token, + attrs, + options: _, + ty, + comma_token, + }) => Fields::Unnamed(FieldsUnnamed { + paren_token: *paren_token, + unnamed: Punctuated::from_iter([Pair::new( + Field { + attrs: attrs.clone(), + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: parse_quote_spanned! {ident.span()=> + ::fayalite::expr::Expr<#ty> + }, + }, + *comma_token, + )]), + }), + None => Fields::Unit, + }, + discriminant: None, + }, + )), + } + .to_tokens(tokens); + for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { + if let Some(ParsedVariantField { ty, .. }) = field { + quote_spanned! {ident.span()=> + #[automatically_derived] + impl #impl_generics #target #type_generics + #where_clause + { + #[allow(non_snake_case, dead_code)] + #vis fn #ident<__V: ::fayalite::expr::ToExpr>( + 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::TYPE, + } + } else { + quote_spanned! {ident.span()=> + #ident: (), + } + } + })); + let type_properties = format_ident!("__type_properties", span = ident.span()); + let type_properties_variants = + Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { + let variant = if let Some(ParsedVariantField { ty, .. }) = field { + quote_spanned! {ident.span()=> + ::fayalite::__std::option::Option::Some( + <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES, + ) + } + } else { + quote_spanned! {ident.span()=> + ::fayalite::__std::option::Option::None + } + }; + quote_spanned! {ident.span()=> + let #type_properties = #type_properties.variant(#variant); + } + })); + quote_spanned! {ident.span()=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::ty::StaticType + for #target #static_type_generics + #static_where_clause + { + const TYPE: Self = Self { + #(#static_type_body_variants)* + }; + const MASK_TYPE: ::MaskType = + ::fayalite::int::Bool; + const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { + let #type_properties = ::fayalite::enum_::EnumTypePropertiesBuilder::new(); + #(#type_properties_variants)* + #type_properties.finish() + }; + const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = + <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; + } + } + .to_tokens(tokens); + } + } +} + +pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result { + 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..a60cb58 --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -0,0 +1,2981 @@ +use crate::{kw, Errors, HdlAttr, PairsIterExt}; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote_spanned, ToTokens}; +use std::{collections::HashMap, fmt, mem}; +use syn::{ + parse::{Parse, ParseStream}, + parse_quote, parse_quote_spanned, + punctuated::Punctuated, + spanned::Spanned, + token::{Brace, Bracket, Paren}, + AngleBracketedGenericArguments, Attribute, ConstParam, Expr, ExprIndex, ExprPath, ExprTuple, + Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, + Generics, Ident, ImplGenerics, Index, ItemStruct, 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 From for Type { + fn from(value: ParsedTypeDelimited) -> Self { + let ParsedTypeDelimited { delim, elem } = value; + let elem = Box::new(elem.into()); + match delim { + TypeDelimiter::Group(group_token) => Type::Group(TypeGroup { group_token, elem }), + TypeDelimiter::Paren(paren_token) => Type::Paren(TypeParen { paren_token, elem }), + } + } +} + +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 From for Type { + fn from(value: ParsedTypeTuple) -> Self { + let ParsedTypeTuple { paren_token, elems } = value; + Type::Tuple(TypeTuple { + paren_token, + elems: Punctuated::from_iter(elems.into_pairs().map_pair_value(Into::into)), + }) + } +} + +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) path: Path, + pub(crate) args: Option, +} + +impl From for Type { + fn from(value: ParsedTypeNamed) -> Self { + let ParsedTypeNamed { path, args } = value; + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: path.leading_colon, + segments: { + let mut segments = path.segments.clone(); + if let Some(args) = args { + segments.last_mut().unwrap().arguments = + PathArguments::AngleBracketed(args.into()); + } + segments + }, + }, + }) + } +} + +impl ToTokens for ParsedTypeNamed { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { path, args } = self; + path.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 From for Type { + fn from(value: ParsedTypeNamedParam) -> Self { + Type::Path(TypePath { + qself: None, + path: value.into_ident().into(), + }) + } +} + +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) struct ParsedTypeConstUsize { + pub(crate) const_usize: known_items::ConstUsize, + pub(crate) lt_token: Token![<], + pub(crate) value: Box, + pub(crate) gt_token: Token![>], +} + +impl From for Path { + fn from(value: ParsedTypeConstUsize) -> Self { + let ParsedTypeConstUsize { + const_usize, + lt_token, + value, + gt_token, + } = value; + let path = const_usize.path; + let args = Punctuated::from_iter([GenericArgument::Const(*value)]); + let args = AngleBracketedGenericArguments { + colon2_token: Some(Token![::](lt_token.span)), + lt_token, + args, + gt_token, + }; + let mut segments = path.segments.clone(); + segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(args); + Path { + leading_colon: path.leading_colon, + segments, + } + } +} + +impl From for Type { + fn from(value: ParsedTypeConstUsize) -> Self { + Type::Path(TypePath { + qself: None, + path: value.into(), + }) + } +} + +impl MakeHdlTypeExpr for ParsedTypeConstUsize { + fn make_hdl_type_expr(&self, _context: &MakeHdlTypeExprContext) -> Expr { + Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: self.clone().into(), + }) + } +} + +impl ToTokens for ParsedTypeConstUsize { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + const_usize, + lt_token, + value, + gt_token, + } = self; + const_usize.to_tokens(tokens); + lt_token.to_tokens(tokens); + value.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + +impl ParsedTypeConstUsize { + fn try_from_named( + named: ParsedTypeNamed, + parser: &mut TypesParser<'_>, + ) -> Result, ParseFailed> { + let ParsedTypeNamed { path, args } = named; + let const_usize = match known_items::ConstUsize::parse_path(path) { + Ok(const_usize) => const_usize, + Err(path) => return Ok(Err(ParsedTypeNamed { path, args })), + }; + let Some(ParsedGenericArguments { + colon2_token: _, + lt_token, + args, + gt_token, + }) = args + else { + parser + .errors() + .error(const_usize, "ConstUsize type is missing generic arguments"); + return Err(ParseFailed); + }; + let args_len = args.len(); + let mut args = args.into_iter(); + let (Some(value), None) = (args.next(), args.next()) else { + parser.errors().error( + const_usize, + format_args!("ConstUsize type takes 1 generic argument, but got {args_len}"), + ); + return Err(ParseFailed); + }; + let ParsedGenericArgument::Const(value) = value else { + parser.errors().error(value, "expected a constant"); + return Err(ParseFailed); + }; + Ok(Ok(Self { + const_usize, + lt_token, + value: Box::new(value), + gt_token, + })) + } +} + +macro_rules! make_parsed_type_or_const { + ( + @parse_arg($is_const:ident, $parser:ident, $span:expr) + #[type] + $type_arg:ident: $type_arg_ty:ty, + ) => { + let $type_arg = $type_arg.into_value(); + let ParsedGenericArgument::Type($type_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a type"); + return Err(ParseFailed); + }; + let $type_arg = Box::new($type_arg); + }; + ( + @parse_arg($is_const:ident, $parser:ident, $span:expr) + #[type] + $type_arg:ident: $type_arg_ty:ty, + #[separator] + $separator:ident: $separator_ty:ty, + ) => { + let ($type_arg, $separator) = $type_arg.into_tuple(); + let $separator = $separator.unwrap_or_default(); + let ParsedGenericArgument::Type($type_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a type"); + return Err(ParseFailed); + }; + let $type_arg = Box::new($type_arg); + }; + ( + @parse_arg($is_const:ident, $parser:ident, $span:expr) + #[const] + $const_arg:ident: $const_arg_ty:ty, + #[type] + $type_arg:ident: $type_arg_ty:ty, + ) => { + let $type_arg = $type_arg.into_value(); + let ($type_arg, $const_arg) = if $is_const { + let ParsedGenericArgument::Const($const_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a constant"); + return Err(ParseFailed); + }; + let $type_arg = Box::new(ParsedType::ConstUsize(ParsedTypeConstUsize { + const_usize: known_items::ConstUsize($span), + lt_token: Token![<]($span), + value: Box::new($const_arg.clone()), + gt_token: Token![>]($span), + })); + ($type_arg, Some(Box::new($const_arg))) + } else { + let ParsedGenericArgument::Type($type_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a type"); + return Err(ParseFailed); + }; + (Box::new($type_arg), None) + }; + }; + ( + @parse_arg($is_const:ident, $parser:ident, $span:expr) + #[const] + $const_arg:ident: $const_arg_ty:ty, + #[type] + $type_arg:ident: $type_arg_ty:ty, + #[separator] + $separator:ident: $separator_ty:ty, + ) => { + let ($type_arg, $separator) = $type_arg.into_tuple(); + let $separator = $separator.unwrap_or_default(); + let ($type_arg, $const_arg) = if $is_const { + let ParsedGenericArgument::Const($const_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a constant"); + return Err(ParseFailed); + }; + let $type_arg = Box::new(ParsedType::ConstUsize(ParsedTypeConstUsize { + const_usize: known_items::ConstUsize($span), + lt_token: Token![<]($span), + value: Box::new($const_arg.clone()), + gt_token: Token![>]($span), + })); + ($type_arg, Some(Box::new($const_arg))) + } else { + let ParsedGenericArgument::Type($type_arg) = $type_arg else { + $parser.errors().error($type_arg, "expected a type"); + return Err(ParseFailed); + }; + (Box::new($type_arg), None) + }; + }; + ( + $(#[$struct_meta:meta])* + $vis:vis struct $struct_name:ident { + $const_name_field:ident: known_items::$const_name:ident, + $type_name_field:ident: known_items::$type_name:ident, + $lt_token:ident: $lt_token_ty:ty, + $( + $( + #[const] + $const_arg:ident: $const_arg_ty:ty, + )? + #[type] + $type_arg:ident: $type_arg_ty:ty, + $( + #[separator] + $separator:ident: $separator_ty:ty, + )? + )* + $gt_token:ident: $gt_token_ty:ty, + } + ) => { + $(#[$struct_meta])* + $vis struct $struct_name { + $vis $const_name_field: known_items::$const_name, + $vis $type_name_field: known_items::$type_name, + $vis $lt_token: $lt_token_ty, + $( + $($vis $const_arg: $const_arg_ty,)? + $vis $type_arg: $type_arg_ty, + $($vis $separator: $separator_ty,)? + )* + $vis $gt_token: $gt_token_ty, + } + + impl $struct_name { + $vis fn try_from_named( + named: ParsedTypeNamed, + parser: &mut TypesParser<'_>, + ) -> Result, ParseFailed> { + let ParsedTypeNamed { path, args } = named; + let $const_name_field; + let $type_name_field; + let parsed_path = known_items::$const_name::parse_path(path); + let parsed_path = parsed_path.map_err(known_items::$type_name::parse_path); + let is_const = match parsed_path { + Ok(const_path) => { + $type_name_field = known_items::$type_name(const_path.span); + $const_name_field = const_path; + true + } + Err(Ok(type_path)) => { + $const_name_field = known_items::$const_name(type_path.span); + $type_name_field = type_path; + false + } + Err(Err(path)) => return Ok(Err(ParsedTypeNamed { path, args })), + }; + let Some(ParsedGenericArguments { + colon2_token: _, + $lt_token, + args, + $gt_token, + }) = args + else { + parser + .errors() + .error($const_name_field, "type requires generic arguments"); + return Err(ParseFailed); + }; + let args_len = args.len(); + let mut args = args.into_pairs(); + $(let $type_arg = args.next();)* + let args = [$($type_arg,)* args.next()]; + let [$(Some($type_arg),)* None] = args else { + parser.errors().error( + $const_name_field, + format_args!("wrong number of generic arguments supplied: got {args_len}, expected {}", args.len() - 1), + ); + return Err(ParseFailed); + }; + $(make_parsed_type_or_const! { + @parse_arg(is_const, parser, $const_name_field.span) + $( + #[const] + $const_arg: $const_arg_ty, + )? + #[type] + $type_arg: $type_arg_ty, + $( + #[separator] + $separator: $separator_ty, + )? + })* + Ok(Ok(Self { + $const_name_field, + $type_name_field, + $lt_token, + $( + $($const_arg,)? + $type_arg, + $($separator,)? + )* + $gt_token, + })) + } + $vis fn is_const(&self) -> bool { + matches!(self, Self { + $($($const_arg: Some(_),)?)* + .. + }) + } + } + + impl From<$struct_name> for Type { + fn from(value: $struct_name) -> Type { + let $struct_name { + $const_name_field, + $type_name_field, + $lt_token, + $( + $($const_arg,)? + $type_arg, + $($separator,)? + )* + $gt_token, + } = value; + let (path, args) = if let ($($(Some($const_arg),)?)*) = ($($($const_arg,)?)*) { + let path = $const_name_field.path; + let mut args = Punctuated::new(); + $( + args.push(( + ($(|| GenericArgument::Const(*$const_arg),)? + || GenericArgument::Type($type_arg.into()), + ).0)()); + $(args.push_punct($separator);)? + )* + (path, args) + } else { + let path = $type_name_field.path; + let mut args = Punctuated::new(); + $( + args.push(GenericArgument::Type($type_arg.into())); + $(args.push_punct($separator);)? + )* + (path, args) + }; + let args = AngleBracketedGenericArguments { + colon2_token: Some(Token![::]($lt_token.span)), + $lt_token, + args, + $gt_token, + }; + let mut segments = path.segments.clone(); + segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(args); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: path.leading_colon, + segments, + }, + }) + } + } + + impl MakeHdlTypeExpr for $struct_name { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + let $struct_name { + $const_name_field, + $type_name_field: _, + $lt_token: _, + $( + $($const_arg: _,)? + $type_arg, + $($separator: _,)? + )* + $gt_token: _, + } = self; + let span = $const_name_field.span; + if context.is_const { + return parse_quote_spanned! {span=> + ::fayalite::ty::StaticType::TYPE + }; + } + let mut retval = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: $const_name_field.path.clone(), + }); + $( + retval = Expr::Index(ExprIndex { + attrs: vec![], + expr: Box::new(retval), + bracket_token: Bracket(span), + index: Box::new($type_arg.make_hdl_type_expr(context)), + }); + )* + retval + } + } + + impl ToTokens for $struct_name { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + $const_name_field, + $type_name_field, + $lt_token, + $( + $($const_arg,)? + $type_arg, + $($separator,)? + )* + $gt_token, + } = self; + if let ($($(Some($const_arg),)?)*) = ($($($const_arg,)?)*) { + $const_name_field.to_tokens(tokens); + $lt_token.to_tokens(tokens); + $( + if (( + $(|| { + $const_arg.to_tokens(tokens); + false + },)? + || true, + ).0)() { + $type_arg.to_tokens(tokens); + } + $($separator.to_tokens(tokens);)? + )* + $gt_token.to_tokens(tokens); + } else { + $type_name_field.to_tokens(tokens); + $lt_token.to_tokens(tokens); + $( + $type_arg.to_tokens(tokens); + $($separator.to_tokens(tokens);)? + )* + $gt_token.to_tokens(tokens); + } + } + } + }; +} + +make_parsed_type_or_const! { + #[derive(Debug, Clone)] + pub(crate) struct ParsedTypeArray { + array: known_items::Array, + array_type: known_items::ArrayType, + lt_token: Token![<], + #[type] + element: Box, + #[separator] + comma_token: Token![,], + #[const] + const_len: Option>, + #[type] + type_len: Box, + gt_token: Token![>], + } +} + +make_parsed_type_or_const! { + #[derive(Debug, Clone)] + pub(crate) struct ParsedTypeUInt { + uint: known_items::UInt, + uint_type: known_items::UIntType, + lt_token: Token![<], + #[const] + const_width: Option>, + #[type] + type_width: Box, + gt_token: Token![>], + } +} + +make_parsed_type_or_const! { + #[derive(Debug, Clone)] + pub(crate) struct ParsedTypeSInt { + uint: known_items::SInt, + uint_type: known_items::SIntType, + lt_token: Token![<], + #[const] + const_width: Option>, + #[type] + type_width: Box, + gt_token: Token![>], + } +} + +#[derive(Debug, Clone)] +pub(crate) enum ParsedType { + Delimited(ParsedTypeDelimited), + Named(ParsedTypeNamed), + NamedParam(ParsedTypeNamedParam), + Tuple(ParsedTypeTuple), + ConstUsize(ParsedTypeConstUsize), + Array(ParsedTypeArray), + UInt(ParsedTypeUInt), + SInt(ParsedTypeSInt), +} + +impl From for Type { + fn from(value: ParsedType) -> Self { + match value { + ParsedType::Delimited(v) => v.into(), + ParsedType::Named(v) => v.into(), + ParsedType::NamedParam(v) => v.into(), + ParsedType::Tuple(v) => v.into(), + ParsedType::ConstUsize(v) => v.into(), + ParsedType::Array(v) => v.into(), + ParsedType::UInt(v) => v.into(), + ParsedType::SInt(v) => v.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), + ParsedType::ConstUsize(ty) => ty.to_tokens(tokens), + ParsedType::Array(ty) => ty.to_tokens(tokens), + ParsedType::UInt(ty) => ty.to_tokens(tokens), + ParsedType::SInt(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"); + None + } + }; + PathSegment::from(segment.ident.clone()) + })); + let named = ParsedTypeNamed { + path: Path { + leading_colon, + segments, + }, + args, + }; + if let Some(ident) = named.path.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, + }) + } + }, + )); + } + } + let named = match ParsedTypeArray::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::Array(v)), + Err(named) => named, + }; + let named = match ParsedTypeConstUsize::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::ConstUsize(v)), + Err(named) => named, + }; + let named = match ParsedTypeUInt::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::UInt(v)), + Err(named) => named, + }; + let named = match ParsedTypeSInt::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::SInt(v)), + Err(named) => named, + }; + 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)] + #[allow(dead_code)] + pub(crate) struct $known_item { + pub(crate) path: Path, + pub(crate) span: Span, + } + + #[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, + }, + span, + } + } + + 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 let Some(ident) = path.get_ident() { + if ident == stringify!($known_item) { + return Ok(Self { span: ident.span(), path }); + } + } + 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 { span: path.segments.last().unwrap().ident.span(), 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::util::ConstUsize); + impl_known_item!(::fayalite::int::Size); + impl_known_item!(::fayalite::int::UInt); + impl_known_item!(::fayalite::int::SInt); + impl_known_item!(::fayalite::int::UIntType); + impl_known_item!(::fayalite::int::SIntType); + impl_known_item!(::fayalite::array::Array); + impl_known_item!(::fayalite::array::ArrayType); + 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 to_generics(&self, param_count: Option) -> Generics { + let param_count = param_count.unwrap_or(self.params.len()); + if param_count == 0 || self.params.is_empty() { + return Generics::default(); + } + let where_clause = ParsedGenericsWhereClause { + generics: self, + param_count, + } + .to_token_stream(); + let where_clause = if where_clause.is_empty() { + None + } else { + Some(parse_quote! { #where_clause }) + }; + Generics { + lt_token: self.lt_token, + params: self + .params + .pairs() + .take(param_count) + .map_pair_value_ref(|param| parse_quote! { #param }) + .collect(), + gt_token: self.gt_token, + where_clause, + } + } + pub(crate) fn generics_accumulation_type( + &self, + vis: Visibility, + ident: Ident, + param_count: usize, + ) -> ItemStruct { + let span = ident.span(); + ItemStruct { + attrs: vec![ + common_derives(span), + parse_quote_spanned! {span=> + #[allow(non_camel_case_types)] + }, + ], + vis, + struct_token: Token![struct](span), + ident: format_ident!("__{}__GenericsAccumulation{}", ident, param_count), + generics: self.to_generics(Some(param_count)), + fields: if self.params.is_empty() || param_count == 0 { + Fields::Unnamed(parse_quote_spanned! {span=> + (()) + }) + } else { + FieldsUnnamed { + paren_token: Paren(span), + unnamed: self + .params + .pairs() + .take(param_count) + .map_pair_value_ref(|param| match param { + ParsedGenericParam::Type(param) => { + let ident = ¶m.ident; + parse_quote! { #ident } + } + ParsedGenericParam::SizeType(param) => { + let ident = ¶m.ident; + parse_quote_spanned! {span=> + <#ident as ::fayalite::int::Size>::SizeType + } + } + }) + .collect(), + } + .into() + }, + semi_token: Some(Token![;](span)), + } + } + pub(crate) fn make_runtime_generics( + &self, + tokens: &mut TokenStream, + vis: &Visibility, + ident: &Ident, + target: &Path, + mut target_expr: impl FnMut(&MakeHdlTypeExprContext) -> Expr, + ) { + if self.params.is_empty() { + let target_expr = target_expr(&MakeHdlTypeExprContext { + type_arg_values: vec![], + is_const: true, + }); + quote_spanned! {ident.span()=> + #[allow(non_upper_case_globals)] + #vis const #ident: #target = #target_expr; + } + .to_tokens(tokens); + return; + } + let generics_accumulation_types = + Vec::from_iter((0..self.params.len()).map(|param_count| { + self.generics_accumulation_type(vis.clone(), ident.clone(), param_count) + })); + generics_accumulation_types[0].to_tokens(tokens); + let generics_accumulation_ident_0 = &generics_accumulation_types[0].ident; + quote_spanned! {ident.span()=> + #[allow(non_upper_case_globals)] + #vis const #ident: #generics_accumulation_ident_0 = #generics_accumulation_ident_0(()); + } + .to_tokens(tokens); + let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let tokens = wrapped_in_const.inner(); + for i in &generics_accumulation_types[1..] { + i.to_tokens(tokens) + } + let final_generics = Generics::from(self); + let self_members: Vec = (0..self.params.len()) + .map(|index| { + let mut member = Index::from(index); + member.span = ident.span(); + parse_quote_spanned! {ident.span()=> + self.#member + } + }) + .collect(); + for (param_count, (generics_accumulation_type, next_param)) in generics_accumulation_types + .iter() + .zip(&self.params) + .enumerate() + { + let cur_generics = &generics_accumulation_type.generics; + let cur_target = &generics_accumulation_type.ident; + let next_param_count = param_count + 1; + let is_last = next_param_count == self.params.len(); + let next_target = if is_last { + target.clone() + } else { + generics_accumulation_types[next_param_count] + .ident + .clone() + .into() + }; + let next_generics = if is_last { + &final_generics + } else { + &generics_accumulation_types[next_param_count].generics + }; + let (cur_impl_generics, cur_type_generics, cur_where_clause) = + cur_generics.split_for_impl(); + let (next_impl_generics, next_type_generics, next_where_clause) = + next_generics.split_for_impl(); + let next_turbofish = next_type_generics.as_turbofish(); + let mut next_target_expr = |context: &MakeHdlTypeExprContext| -> Expr { + if is_last { + target_expr(context) + } else { + let args = &context.type_arg_values[..next_param_count]; + parse_quote_spanned! {ident.span()=> + #next_target #next_turbofish(#(#args),*) + } + } + }; + match next_param { + ParsedGenericParam::Type(ParsedTypeParam { + ident: param_ident, + default, + .. + }) => { + let mut generics = next_generics.clone(); + let mut param: Expr = parse_quote_spanned! {ident.span()=> + __param + }; + let mut index_type = param_ident.clone(); + if let Some((_, default)) = default { + index_type = format_ident!("__Param", span = ident.span()); + generics.params.push(parse_quote! { #index_type }); + generics.make_where_clause().predicates.push(parse_quote_spanned! {ident.span()=> + #index_type: ::fayalite::ty::TypeOrDefault<#default, Type = #param_ident> + }); + let default_expr = default.make_hdl_type_expr(&MakeHdlTypeExprContext { + type_arg_values: self_members[..param_count].to_vec(), + is_const: false, + }); + param = parse_quote_spanned! {ident.span()=> + ::fayalite::ty::TypeOrDefault::get(__param, || #default_expr) + }; + let output_expr = next_target_expr(&MakeHdlTypeExprContext { + type_arg_values: self_members[..param_count] + .iter() + .cloned() + .chain([default_expr]) + .collect(), + is_const: false, + }); + let mut next_target_args: AngleBracketedGenericArguments = + parse_quote! { #next_type_generics }; + *next_target_args.args.last_mut().unwrap() = + GenericArgument::Type(default.clone().into()); + quote_spanned! {ident.span()=> + impl #cur_impl_generics ::fayalite::ty::FillInDefaultedGenerics + for #cur_target #cur_type_generics + #cur_where_clause + { + type Type = #next_target #next_target_args; + fn fill_in_defaulted_generics( + self, + ) -> ::Type { + #output_expr + } + } + } + .to_tokens(tokens); + } + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + let output_expr = next_target_expr(&MakeHdlTypeExprContext { + type_arg_values: self_members[..param_count] + .iter() + .cloned() + .chain([param]) + .collect(), + is_const: false, + }); + quote_spanned! {ident.span()=> + #[allow(non_upper_case_globals)] + #[automatically_derived] + impl #impl_generics ::fayalite::__std::ops::Index<#index_type> + for #cur_target #cur_type_generics + #where_clause + { + type Output = #next_target #next_type_generics; + + fn index(&self, __param: #index_type) -> &Self::Output { + ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Intern::intern_sized(#output_expr), + ) + } + } + } + .to_tokens(tokens); + } + ParsedGenericParam::SizeType(ParsedSizeTypeParam { + ident: param_ident, + bounds, + default, + .. + }) => { + if bounds.KnownSize.is_none() { + let output_expr = next_target_expr(&MakeHdlTypeExprContext { + type_arg_values: self_members[..param_count] + .iter() + .cloned() + .chain([parse_quote_spanned! {ident.span()=> + __param + }]) + .collect(), + is_const: false, + }); + let mut next_target_args: AngleBracketedGenericArguments = + parse_quote! { #next_type_generics }; + *next_target_args.args.last_mut().unwrap() = parse_quote_spanned! {ident.span()=> + ::fayalite::int::DynSize + }; + quote_spanned! {ident.span()=> + #[allow(non_upper_case_globals)] + #[automatically_derived] + impl #cur_impl_generics ::fayalite::__std::ops::Index<::fayalite::__std::primitive::usize> + for #cur_target #cur_type_generics + #cur_where_clause + { + type Output = #next_target #next_target_args; + + fn index( + &self, + __param: ::fayalite::__std::primitive::usize, + ) -> &Self::Output { + ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Intern::intern_sized(#output_expr), + ) + } + } + } + .to_tokens(tokens); + } + { + let output_expr = next_target_expr(&MakeHdlTypeExprContext { + type_arg_values: self_members[..param_count] + .iter() + .cloned() + .chain([parse_quote_spanned! {ident.span()=> + __param + }]) + .collect(), + is_const: false, + }); + let mut next_generics = cur_generics.clone(); + next_generics + .params + .push(parse_quote_spanned! {ident.span()=> + const #param_ident: ::fayalite::__std::primitive::usize + }); + let known_size = bounds + .KnownSize + .clone() + .unwrap_or_else(|| known_items::KnownSize(ident.span())); + next_generics.make_where_clause().predicates.push( + parse_quote_spanned! {ident.span()=> + ::fayalite::util::ConstUsize<#param_ident>: #known_size + }, + ); + let (next_impl_generics, next_type_generics, next_where_clause) = + next_generics.split_for_impl(); + let mut next_target_args: AngleBracketedGenericArguments = + parse_quote! { #next_type_generics }; + *next_target_args.args.last_mut().unwrap() = parse_quote_spanned! {ident.span()=> + ::fayalite::util::ConstUsize<#param_ident> + }; + quote_spanned! {ident.span()=> + #[allow(non_upper_case_globals)] + #[automatically_derived] + impl #next_impl_generics ::fayalite::__std::ops::Index< + ::fayalite::util::ConstUsize<#param_ident>, + > for #cur_target #cur_type_generics + #next_where_clause + { + type Output = #next_target #next_target_args; + + fn index( + &self, + __param: ::fayalite::util::ConstUsize<#param_ident>, + ) -> &Self::Output { + ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Intern::intern_sized(#output_expr), + ) + } + } + } + .to_tokens(tokens); + } + if let Some((_, default)) = default { + todo!(); + } + } + }; + } + } + 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> { + generics: &'a ParsedGenerics, + param_count: usize, +} + +impl ToTokens for ParsedGenericsImplGenerics<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + generics, + param_count, + } = *self; + let ParsedGenerics { + lt_token, + params, + gt_token, + param_name_to_index_map: _, + } = generics; + if params.is_empty() || param_count == 0 { + return; + } + lt_token.unwrap_or_default().to_tokens(tokens); + for param in params.pairs().take(param_count) { + 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> { + generics: &'a ParsedGenerics, + param_count: usize, +} + +impl ToTokens for ParsedGenericsTurbofish<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + generics, + param_count, + } = *self; + if generics.params.is_empty() || param_count == 0 { + return; + } + Token![::](generics.lt_token.unwrap_or_default().span).to_tokens(tokens); + ParsedGenericsTypeGenerics { + generics, + param_count, + } + .to_tokens(tokens); + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedGenericsTypeGenerics<'a> { + generics: &'a ParsedGenerics, + param_count: usize, +} + +impl ToTokens for ParsedGenericsTypeGenerics<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + generics, + param_count, + } = *self; + let ParsedGenerics { + lt_token, + params, + gt_token, + param_name_to_index_map: _, + } = generics; + if params.is_empty() || param_count == 0 { + return; + } + lt_token.unwrap_or_default().to_tokens(tokens); + for param in params.pairs().take(param_count) { + 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> { + generics: &'a ParsedGenerics, + param_count: usize, +} + +impl ToTokens for ParsedGenericsWhereClause<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut need_where_token = true; + let mut where_token = |span: Span| -> Option { + mem::replace(&mut need_where_token, false).then(|| Token![where](span)) + }; + for param in self.generics.params.iter().take(self.param_count) { + match param { + ParsedGenericParam::Type(ParsedTypeParam { + ident, + colon_token, + bounds, + .. + }) => { + if let Some(static_type) = &bounds.StaticType { + where_token(static_type.span).to_tokens(tokens); + quote_spanned! {static_type.span=> + <#ident as ::fayalite::ty::Type>::MaskType #colon_token #static_type, + } + .to_tokens(tokens); + } + } + ParsedGenericParam::SizeType(_) => {} + } + } + } +} + +#[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<'_> { + let Self { + generics, + param_count, + } = *self; + ParsedGenericsTurbofish { + generics, + param_count, + } + } +} + +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 { + generics: self, + param_count: self.params.len(), + }, + ParsedGenericsTypeGenerics { + generics: self, + param_count: self.params.len(), + }, + ParsedGenericsWhereClause { + generics: self, + param_count: self.params.len(), + }, + ) + } +} + +impl From<&'_ ParsedGenerics> for Generics { + fn from(value: &ParsedGenerics) -> Self { + value.to_generics(None) + } +} + +impl From for Generics { + fn from(value: ParsedGenerics) -> Self { + From::<&ParsedGenerics>::from(&value) + } +} + +#[derive(Debug, Copy, 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, + } + } + pub(crate) fn ty(self) -> MaybeParsed<&'a ParsedType, &'a Type> { + match self { + MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(&v.ty), + MaybeParsed::Parsed(v) => MaybeParsed::Parsed(&v.ty), + } + } +} + +#[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(), + } + } +} + +#[derive(Debug)] +pub(crate) struct MakeHdlTypeExprContext { + pub(crate) type_arg_values: Vec, + pub(crate) is_const: bool, +} + +pub(crate) trait MakeHdlTypeExpr { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr; +} + +impl MakeHdlTypeExpr for ParsedType { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + match self { + Self::Delimited(v) => v.make_hdl_type_expr(context), + Self::Named(v) => v.make_hdl_type_expr(context), + Self::NamedParam(v) => v.make_hdl_type_expr(context), + Self::Tuple(v) => v.make_hdl_type_expr(context), + Self::ConstUsize(v) => v.make_hdl_type_expr(context), + Self::Array(v) => v.make_hdl_type_expr(context), + Self::UInt(v) => v.make_hdl_type_expr(context), + Self::SInt(v) => v.make_hdl_type_expr(context), + } + } +} + +impl MakeHdlTypeExpr for ParsedTypeDelimited { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + self.elem.make_hdl_type_expr(context) + } +} + +impl MakeHdlTypeExpr for ParsedTypeNamed { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + let Self { path, args } = self; + let span = path + .segments + .last() + .map(|v| v.ident.span()) + .unwrap_or_else(Span::call_site); + if context.is_const { + return parse_quote_spanned! {span=> + ::fayalite::ty::StaticType::TYPE + }; + } + let mut retval = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: path.clone(), + }); + if let Some(ParsedGenericArguments { lt_token, args, .. }) = args { + for arg in args { + retval = Expr::Index(ExprIndex { + attrs: vec![], + expr: Box::new(retval), + bracket_token: Bracket(lt_token.span), + index: Box::new(arg.make_hdl_type_expr(context)), + }); + } + } + Expr::Call(syn::ExprCall { + attrs: vec![], + func: parse_quote_spanned! {span=> + ::fayalite::ty::FillInDefaultedGenerics::fill_in_defaulted_generics + }, + paren_token: Paren(span), + args: Punctuated::from_iter([retval]), + }) + } +} + +impl MakeHdlTypeExpr for ParsedGenericArgument { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + match self { + ParsedGenericArgument::Type(v) => v.make_hdl_type_expr(context), + ParsedGenericArgument::Const(v) => v.clone(), + } + } +} + +impl MakeHdlTypeExpr for ParsedTypeNamedParam { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + match self { + Self::Type(v) => v.make_hdl_type_expr(context), + Self::SizeType(v) => v.make_hdl_type_expr(context), + } + } +} + +impl MakeHdlTypeExpr for ParsedTypeNamedParamType { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + context.type_arg_values[self.param_index].clone() + } +} + +impl MakeHdlTypeExpr for ParsedTypeNamedParamSizeType { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + context.type_arg_values[self.param_index].clone() + } +} + +impl MakeHdlTypeExpr for ParsedTypeTuple { + fn make_hdl_type_expr(&self, context: &MakeHdlTypeExprContext) -> Expr { + let Self { paren_token, elems } = self; + Expr::Tuple(ExprTuple { + attrs: vec![], + paren_token: *paren_token, + elems: elems + .pairs() + .map_pair_value_ref(|v| v.make_hdl_type_expr(context)) + .collect(), + }) + } +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 920a27b..e52e351 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,)* + } + } + } }; } @@ -686,6 +837,15 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr .suffix(".tmp.rs") .tempfile_in(out_dir) .unwrap(); + struct PrintOnPanic<'a>(&'a TokenStream); + impl Drop for PrintOnPanic<'_> { + fn drop(&mut self) { + if std::thread::panicking() { + println!("{}", self.0); + } + } + } + let _print_on_panic = PrintOnPanic(&contents); let contents = prettyplease::unparse(&parse_quote! { #contents }); let hash = ::digest(&contents); let hash = base16ct::HexDisplay(&hash[..5]); @@ -728,3 +888,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/annotations.rs b/crates/fayalite/src/annotations.rs index 5e2ba95..8e645c3 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::Target, + expr::target::Target, intern::{Intern, Interned}, }; use serde::{Deserialize, Serialize}; diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 3763cae..83673cf 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -1,671 +1,207 @@ // 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, LazyInterned}, + 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)] +pub struct ArrayType { + element: LazyInterned, + 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]>; -} - -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 { + write!(f, "Array<{:?}, {}>", self.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; +#[allow(non_upper_case_globals)] +pub const ArrayType: 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 { + const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties { + let TypeProperties { + is_passive, + is_storable, + is_castable_from_bits, + bit_width, + } = element; + let Some(bit_width) = bit_width.checked_mul(len) else { + panic!("array too big"); }; - ArrayType { - element, - len, + TypeProperties { + is_passive, + is_storable, + is_castable_from_bits, bit_width, } } -} - -impl Fold for ArrayType -where - VA::ElementType: Fold, -{ - fn fold(self, state: &mut State) -> Result { - state.fold_array_type(self) + pub fn new(element: T, len: Len::SizeType) -> Self { + let type_properties = + Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len)); + Self { + element: LazyInterned::Interned(element.intern_sized()), + len, + type_properties, + } } - fn default_fold(self, state: &mut State) -> Result { - Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) + pub fn element(&self) -> T { + *self.element + } + pub fn len(self) -> usize { + Len::as_usize(self.len) + } + pub fn type_properties(self) -> TypeProperties { + self.type_properties + } + pub fn as_dyn_array(self) -> Array { + Array::new_dyn(self.element().canonical(), self.len()) } } -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 { + const TYPE: Self = Self { + element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), + len: Len::SIZE, + type_properties: Self::TYPE_PROPERTIES, + }; + const MASK_TYPE: Self::MaskType = ArrayType:: { + element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()), + len: Len::SIZE, + type_properties: Self::MASK_TYPE_PROPERTIES, + }; + const TYPE_PROPERTIES: TypeProperties = + Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE); + const MASK_TYPE_PROPERTIES: TypeProperties = + Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE); +} + +impl Array { + pub fn new_dyn(element: T, len: usize) -> Self { + Self::new(element, len) } } -impl StaticType for ArrayType<[V; N]> { - fn static_type() -> Self { - Self::new_array(StaticType::static_type()) - } -} - -impl>> ArrayType<[V]> { - pub fn new_slice(element: V::Type, len: usize) -> Self { - ArrayType::new_with_len_type(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.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; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ArrayWithoutLen { + element: T, +} - fn ty(&self) -> Self::Type { - ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) - } +impl Index for ArrayWithoutLen { + type Output = Array; - fn to_expr(&self) -> Expr<::Value> { - Expr::from_value(self) + fn index(&self, len: usize) -> &Self::Output { + Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len))) } } -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; - - 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 +impl Index> for ArrayWithoutLen where - VA::Element: StaticValue, + ConstUsize: KnownSize, { - 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, - } + type Output = Array; + + fn index(&self, len: ConstUsize) -> &Self::Output { + Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len))) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 2d43031..9723002 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -1,806 +1,377 @@ // 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()); +#[derive(Clone)] +pub struct BundleTypePropertiesBuilder(TypeProperties); + +impl BundleTypePropertiesBuilder { + #[must_use] + pub const fn new() -> Self { + Self(TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: 0, + }) + } + pub const fn clone(&self) -> Self { + Self(self.0) + } + #[must_use] + pub const fn field(self, flipped: bool, field_props: TypeProperties) -> Self { + let Some(bit_width) = self.0.bit_width.checked_add(field_props.bit_width) else { + panic!("bundle is too big: bit-width overflowed"); + }; + if flipped { + Self(TypeProperties { + is_passive: false, + is_storable: false, + is_castable_from_bits: false, + bit_width, + }) + } else { + Self(TypeProperties { + is_passive: self.0.is_passive & field_props.is_passive, + is_storable: self.0.is_storable & field_props.is_storable, + is_castable_from_bits: self.0.is_castable_from_bits + & field_props.is_castable_from_bits, + bit_width, + }) + } + } + pub const fn finish(self) -> TypeProperties { + self.0 + } +} + +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_props_builder = BundleTypePropertiesBuilder::new(); + 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 - .checked_add(ty.bit_width()) - .unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed")); + field_offsets.push(type_props_builder.0.bit_width); + type_props_builder = type_props_builder.field(flipped, ty.type_properties()); } - 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: type_props_builder.finish(), + })) } 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: StaticType,)*> StaticType for ($($T,)*) { + const TYPE: Self = ($($T::TYPE,)*); + const MASK_TYPE: Self::MaskType = ($($T::MASK_TYPE,)*); + const TYPE_PROPERTIES: TypeProperties = { + let builder = BundleTypePropertiesBuilder::new(); + $(let builder = builder.field(false, $T::TYPE_PROPERTIES);)* + builder.finish() + }; + const MASK_TYPE_PROPERTIES: TypeProperties = { + let builder = BundleTypePropertiesBuilder::new(); + $(let builder = builder.field(false, $T::MASK_TYPE_PROPERTIES);)* + builder.finish() + }; + } + impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { + type Type = ($($T::Type,)*); - 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) + 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: StaticType,)*> StaticType for ($($T,)*) - where - $($T::Value: Value,)* - { - #[allow(clippy::unused_unit)] - fn static_type() -> Self { - ($($T::static_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() } } - - 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) - } - } - - impl<$($T: Value,)*> BundleValue for ($($T,)*) - where - $($T::Type: Type,)* - { - } + }; + ([$($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] v0: T0} + {#[num = 1, field = field_1] v1: T1} + {#[num = 2, field = field_2] v2: T2} + {#[num = 3, field = field_3] v3: T3} + {#[num = 4, field = field_4] v4: T4} + {#[num = 5, field = field_5] v5: T5} + {#[num = 6, field = field_6] v6: T6} + {#[num = 7, field = field_7] v7: T7} + {#[num = 8, field = field_8] v8: T8} + {#[num = 9, field = field_9] v9: T9} + {#[num = 10, field = field_10] v10: T10} + {#[num = 11, field = field_11] v11: T11} + ] +} diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 415d7b6..aa75a87 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -2,111 +2,57 @@ // See Notices.txt for copyright information use crate::{ expr::{Expr, ToExpr}, - int::{UInt, UIntType}, - intern::Interned, + hdl, + int::Bool, reset::Reset, source_location::SourceLocation, - ty::{ - impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, - DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, - }, - util::interned_bit, + ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, }; -use bitvec::slice::BitSlice; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] -pub struct ClockType; +pub struct Clock; -impl ClockType { - pub const fn new() -> Self { - Self - } -} +impl Type for Clock { + type MaskType = Bool; -impl Type for ClockType { - type Value = Clock; - type CanonicalType = ClockType; - type CanonicalValue = Clock; - type MaskType = UIntType<1>; - type MaskValue = UInt<1>; - - impl_match_values_as_self!(); + impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { - UIntType::new() + Bool } - fn type_enum(&self) -> TypeEnum { - TypeEnum::Clock(*self) + fn canonical(&self) -> CanonicalType { + CanonicalType::Clock(*self) } - fn from_canonical_type(t: Self::CanonicalType) -> Self { - t - } - - fn canonical(&self) -> Self::CanonicalType { - *self - } - - fn source_location(&self) -> SourceLocation { + fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { - Some(this) + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Clock(retval) = canonical_type else { + panic!("expected Clock"); + }; + retval } } -impl Connect for ClockType {} - -impl CanonicalType for ClockType { - const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock; -} - -impl StaticType for ClockType { - fn static_type() -> Self { - Self +impl Clock { + pub fn type_properties(self) -> TypeProperties { + Self::TYPE_PROPERTIES } } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub struct Clock(pub bool); - -impl ToExpr for Clock { - type Type = ClockType; - - fn ty(&self) -> Self::Type { - ClockType - } - - fn to_expr(&self) -> Expr { - Expr::from_value(self) - } -} - -impl Value for Clock { - fn to_canonical(&self) -> ::CanonicalValue { - *self - } - fn to_bits_impl(this: &Self) -> Interned { - interned_bit(this.0) - } -} - -impl CanonicalValue for Clock { - fn value_enum_impl(this: &Self) -> ValueEnum { - ValueEnum::Clock(*this) - } - fn to_bits_impl(this: &Self) -> Interned { - interned_bit(this.0) - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)] -#[hdl(static, outline_generated)] -pub struct ClockDomain { - pub clk: Clock, - pub rst: Reset, +impl StaticType for Clock { + const TYPE: Self = Self; + const MASK_TYPE: Self::MaskType = Bool; + const TYPE_PROPERTIES: TypeProperties = TypeProperties { + is_passive: true, + is_storable: false, + is_castable_from_bits: true, + bit_width: 1, + }; + const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } pub trait ToClock { @@ -137,10 +83,10 @@ impl ToClock for Expr { } } -impl ToClock for Clock { - fn to_clock(&self) -> Expr { - self.to_expr() - } +#[hdl] +pub struct ClockDomain { + pub clk: Clock, + pub rst: Reset, } impl ToClock for bool { @@ -148,9 +94,3 @@ impl ToClock for bool { self.to_expr().to_clock() } } - -impl ToClock for UInt<1> { - fn to_clock(&self) -> Expr { - self.to_expr().to_clock() - } -} diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 6b9c104..0e3d8d4 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -1,190 +1,155 @@ // 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, PartialEq, Eq, Hash, Debug)] +pub struct Enum(Interned); + +const fn discriminant_bit_width_impl(variant_count: usize) -> usize { + match variant_count.next_power_of_two().checked_ilog2() { + Some(x) => x as usize, + None => 0, } } -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub struct DynEnumType(Interned); +#[derive(Clone)] +pub struct EnumTypePropertiesBuilder { + type_properties: TypeProperties, + variant_count: usize, +} -impl fmt::Debug for DynEnumType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) +impl EnumTypePropertiesBuilder { + #[must_use] + pub const fn new() -> Self { + Self { + type_properties: TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: 0, + }, + variant_count: 0, + } + } + pub const fn clone(&self) -> Self { + Self { ..*self } + } + #[must_use] + pub const fn variant(self, field_props: Option) -> Self { + let Self { + mut type_properties, + variant_count, + } = self; + if let Some(TypeProperties { + is_passive, + is_storable, + is_castable_from_bits, + bit_width, + }) = field_props + { + assert!(is_passive, "variant type must be a passive type"); + type_properties = TypeProperties { + is_passive: true, + is_storable: type_properties.is_storable & is_storable, + is_castable_from_bits: type_properties.is_castable_from_bits + & is_castable_from_bits, + bit_width: if type_properties.bit_width < bit_width { + bit_width + } else { + type_properties.bit_width + }, + }; + } + Self { + type_properties, + variant_count: variant_count + 1, + } + } + pub const fn finish(self) -> TypeProperties { + assert!( + self.variant_count != 0, + "zero-variant enums aren't yet supported: \ + https://github.com/chipsalliance/firrtl-spec/issues/208", + ); + let Some(bit_width) = self + .type_properties + .bit_width + .checked_add(discriminant_bit_width_impl(self.variant_count)) + else { + panic!("enum is too big: bit-width overflowed"); + }; + TypeProperties { + bit_width, + ..self.type_properties + } } } -fn discriminant_bit_width_impl(variant_count: usize) -> usize { - variant_count - .next_power_of_two() - .checked_ilog2() - .unwrap_or(0) as usize -} - -impl DynEnumType { +impl Enum { #[track_caller] - pub fn new(variants: Interned<[VariantType>]>) -> Self { - assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); + pub fn new(variants: Interned<[EnumVariant]>) -> Self { 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_props_builder = EnumTypePropertiesBuilder::new(); + for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { + if let Some(old_index) = name_indexes.insert(*name, index) { panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); } - 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(); - } + type_props_builder = type_props_builder.variant(ty.map(CanonicalType::type_properties)); } - let bit_width = body_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: type_props_builder.finish(), } .intern_sized(), ) @@ -192,233 +157,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 +190,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 +219,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 +235,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(outline_generated)] +pub enum HdlOption { + None, + Some(T), } diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 2b8f2b2..5868a22 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -1,96 +1,44 @@ // 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}, + array::{Array, ArrayType}, + bundle::{Bundle, BundleType}, + int::{Bool, IntType, SIntType, SIntValue, Size, UIntType, UIntValue}, + intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, - module::{ - transform::visit::{Fold, Folder, Visit, Visitor}, - Instance, ModuleIO, TargetName, - }, + module::{Instance, ModuleIO}, reg::Reg, - source_location::SourceLocation, - ty::{ - DynCanonicalType, DynCanonicalValue, DynType, DynValue, DynValueTrait, Type, TypeWithDeref, - Value, - }, - util::ConstBool, - valueless::Valueless, + ty::{CanonicalType, Type, TypeWithDeref}, wire::Wire, }; use bitvec::slice::BitSlice; -use std::{any::Any, convert::Infallible, fmt, hash::Hash, marker::PhantomData, ops::Deref}; +use std::{fmt, ops::Deref}; pub mod ops; +pub mod target; 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> { + impl ToLiteralBits for $ExprEnum { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { 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),)+ + $(Self::$Variant(v) => v.to_literal_bits(),)* } } } @@ -99,237 +47,217 @@ macro_rules! expr_enum { 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), + 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), + CastUIntToBool(ops::CastUIntToBool), + CastSIntToBool(ops::CastSIntToBool), + CastBoolToSyncReset(ops::CastBoolToSyncReset), + CastUIntToSyncReset(ops::CastUIntToSyncReset), + CastSIntToSyncReset(ops::CastSIntToSyncReset), + CastBoolToAsyncReset(ops::CastBoolToAsyncReset), + CastUIntToAsyncReset(ops::CastUIntToAsyncReset), + CastSIntToAsyncReset(ops::CastSIntToAsyncReset), + CastSyncResetToBool(ops::CastSyncResetToBool), + CastSyncResetToUInt(ops::CastSyncResetToUInt), + CastSyncResetToSInt(ops::CastSyncResetToSInt), CastSyncResetToReset(ops::CastSyncResetToReset), + CastAsyncResetToBool(ops::CastAsyncResetToBool), + CastAsyncResetToUInt(ops::CastAsyncResetToUInt), + CastAsyncResetToSInt(ops::CastAsyncResetToSInt), CastAsyncResetToReset(ops::CastAsyncResetToReset), - CastClockToBit(ops::CastClockToBit), - CastSyncResetToBit(ops::CastSyncResetToBit), - CastAsyncResetToBit(ops::CastAsyncResetToBit), - CastResetToBit(ops::CastResetToBit), - ModuleIO(ModuleIO>), - Instance(Instance), - Wire(Wire>), - Reg(Reg>), + CastResetToBool(ops::CastResetToBool), + CastResetToUInt(ops::CastResetToUInt), + CastResetToSInt(ops::CastResetToSInt), + CastBoolToClock(ops::CastBoolToClock), + CastUIntToClock(ops::CastUIntToClock), + CastSIntToClock(ops::CastSIntToClock), + CastClockToBool(ops::CastClockToBool), + CastClockToUInt(ops::CastClockToUInt), + CastClockToSInt(ops::CastClockToSInt), + FieldAccess(ops::FieldAccess), + VariantAccess(ops::VariantAccess), + ArrayIndex(ops::ArrayIndex), + DynArrayIndex(ops::DynArrayIndex), + ReduceBitAndU(ops::ReduceBitAndU), + ReduceBitAndS(ops::ReduceBitAndS), + ReduceBitOrU(ops::ReduceBitOrU), + ReduceBitOrS(ops::ReduceBitOrS), + ReduceBitXorU(ops::ReduceBitXorU), + ReduceBitXorS(ops::ReduceBitXorS), + IntSliceU(ops::IntSliceU), + IntSliceS(ops::IntSliceS), + ModuleIO(ModuleIO), + Instance(Instance), + Wire(Wire), + Reg(Reg), MemPort(MemPort), } } -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(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub struct NotALiteralExpr; + +impl fmt::Display for NotALiteralExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Not a literal expression") + } +} + +impl std::error::Error for NotALiteralExpr {} + +pub trait ToLiteralBits { + fn to_literal_bits(&self) -> Result, NotALiteralExpr>; +} + +impl ToLiteralBits for Result, NotALiteralExpr> { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + *self + } +} + +impl ToLiteralBits for Interned { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Ok(*self) + } +} + +impl ToLiteralBits for NotALiteralExpr { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(*self) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Expr { + __enum: Interned, + __ty: T, + __flow: Flow, +} + +impl Expr { + pub fn expr_enum(this: Self) -> Interned { + this.__enum + } + pub fn ty(this: Self) -> T { + this.__ty + } + pub fn flow(this: Self) -> Flow { + this.__flow + } + pub fn canonical(this: Self) -> Expr { + Expr { + __enum: this.__enum, + __ty: this.__ty.canonical(), + __flow: this.__flow, } } - pub fn canonical(self) -> Expr<::CanonicalValue> + pub fn from_canonical(this: Expr) -> Self { + Expr { + __enum: this.__enum, + __ty: T::from_canonical(this.__ty), + __flow: this.__flow, + } + } + pub fn from_dyn_int(this: Expr) -> Self where - T: Value, + T: IntType, { Expr { - __enum: self.expr_enum(), - __phantom: PhantomData, + __enum: this.__enum, + __ty: T::from_dyn_int(this.__ty), + __flow: this.__flow, } } - pub fn dyn_canonical_type(self) -> Interned { - self.expr_enum().ty() - } - pub fn canonical_type(self) -> ::CanonicalType + pub fn as_dyn_int(this: Self) -> Expr where - T: Value, + T: IntType, { - ::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) + Expr { + __enum: this.__enum, + __ty: this.__ty.as_dyn_int(), + __flow: this.__flow, + } } } -impl Copy for Expr {} - -impl Clone for Expr { - fn clone(&self) -> Self { - *self +impl ToLiteralBits for Expr { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.__enum.to_literal_bits() } } -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, -{ +impl Deref for Expr { type Target = T::MatchVariant; fn deref(&self) -> &Self::Target { @@ -337,27 +265,95 @@ where } } -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 Expr> { + pub fn as_dyn_array(this: Self) -> Expr { + Expr { + __enum: this.__enum, + __ty: this.__ty.as_dyn_array(), + __flow: this.__flow, + } } } -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 ToExpr { + type Type: Type; + fn to_expr(&self) -> Expr; +} + +impl ToExpr for Expr { + type Type = T; + + fn to_expr(&self) -> Expr { + *self } } -pub trait ExprTraitBase: - fmt::Debug + Any + SupportsPtrEqWithTypeId + Send + Sync + sealed::Sealed + ToExpr -{ +impl ToExpr for &'_ T { + type Type = T::Type; + + fn to_expr(&self) -> Expr { + T::to_expr(self) + } +} + +impl ToExpr for &'_ mut T { + type Type = T::Type; + + fn to_expr(&self) -> Expr { + T::to_expr(self) + } +} + +impl ToExpr for Box { + type Type = T::Type; + + fn to_expr(&self) -> Expr { + T::to_expr(self) + } +} + +impl ToExpr for Interned { + type Type = T::Type; + + fn to_expr(&self) -> Expr { + T::to_expr(self) + } +} + +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(), + __flow: Flow::Source, + } + } +} + +impl ToExpr for SIntValue { + type Type = SIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::SIntLiteral(self.clone().as_dyn_int().intern()).intern(), + __ty: self.ty(), + __flow: Flow::Source, + } + } +} + +impl ToExpr for bool { + type Type = Bool; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::BoolLiteral(*self).intern(), + __ty: Bool, + __flow: Flow::Source, + } + } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -384,712 +380,106 @@ impl Flow { } } -#[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 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>; -} - -impl ToExpr for &'_ T { - type Type = T::Type; - - fn ty(&self) -> Self::Type { - (**self).ty() - } - - fn to_expr(&self) -> Expr<::Value> { - (**self).to_expr() - } -} - -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() - } -} - -impl ToExpr for Box { - type Type = T::Type; - - fn ty(&self) -> Self::Type { - (**self).ty() - } - - fn to_expr(&self) -> Expr<::Value> { - (**self).to_expr() - } -} - -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct Literal { - value: T::CanonicalValue, -} - -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.value.fmt(f) - } -} - -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 sealed::Sealed for Literal {} - -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()) - } -} - -impl ExprTrait for Literal { - fn expr_enum(&self) -> ExprEnum { - ExprEnum::Literal( - Literal { - value: self.value.to_canonical_dyn(), - } - .intern_sized(), - ) - } - - 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()) + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::ModuleIO(self.canonical()).intern_sized(), + __ty: self.ty(), + __flow: self.flow(), + } } } -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 ToLiteralBits for ModuleIO { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) } } -impl sealed::Sealed for Instance where T::Type: BundleType {} +impl ToExpr for Instance { + type Type = T; -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()) + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Instance(self.canonical()).intern_sized(), + __ty: self.ty(), + __flow: self.flow(), + } } } -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 ToLiteralBits for Instance { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) } } -impl sealed::Sealed for Wire {} +impl ToExpr for Wire { + type Type = T; -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(()) + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Wire(self.canonical()).intern_sized(), + __ty: self.ty(), + __flow: self.flow(), + } } } -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(()) +impl ToLiteralBits for Wire { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) } } -#[doc(hidden)] -pub fn value_from_expr_type(_expr: Expr, infallible: Infallible) -> V { - match infallible {} -} +impl ToExpr for Reg { + type Type = T; -#[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::Reg(self.canonical()).intern_sized(), + __ty: self.ty(), + __flow: self.flow(), + } } } + +impl ToLiteralBits for Reg { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) + } +} + +impl ToExpr for MemPort { + type Type = T::Port; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::MemPort(self.canonical()).intern_sized(), + __ty: self.ty(), + __flow: self.flow(), + } + } +} + +impl ToLiteralBits for MemPort { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) + } +} + +pub trait ReduceBits { + type UIntOutput; + type BoolOutput; + fn reduce_bitand(self) -> Self::UIntOutput; + fn reduce_bitor(self) -> Self::UIntOutput; + fn reduce_bitxor(self) -> Self::UIntOutput; + fn any_one_bits(self) -> Self::BoolOutput; + fn any_zero_bits(self) -> Self::BoolOutput; + fn all_one_bits(self) -> Self::BoolOutput; + fn all_zero_bits(self) -> Self::BoolOutput; + fn parity_odd(self) -> Self::BoolOutput; + fn parity_even(self) -> Self::BoolOutput; +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index c1a16f1..1d18465 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -1,1058 +1,1906 @@ // 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}, + clock::{Clock, ToClock}, + enum_::{Enum, EnumType, EnumVariant}, + expr::{Expr, ExprEnum, Flow, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits}, int::{ - DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, Int, - IntCmp, IntType, IntTypeTrait, IntValue, UInt, UIntType, + Bool, BoolOrIntType, 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}, + reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, + ty::{CanonicalType, StaticType, Type}, }; -use bitvec::{slice::BitSlice, vec::BitVec}; -use num_traits::ToPrimitive; +use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; +use num_bigint::BigInt; +use num_traits::{ToPrimitive, Zero}; 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,)*> - where - ($($where:tt)*) - { - $($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)+ - } - - fn expr_enum(&$expr_enum_self:ident) -> _ { - $($expr_enum_body:tt)+ - } - - fn to_literal_bits(&$to_literal_bits_self:ident) -> Result, ()> { - $($to_literal_bits_body:tt)+ - } - } + #[generics($($generics:tt)*)] + #[value($Value:ty)] + $Trait:ident::$method:ident ) => { - pub struct $name<$($T,)* $(const $C: $CTy,)*> + impl<$($generics)*> $Trait for $Value where - $($where)* + Expr<<$Value as ToExpr>::Type>: $Trait, { - $($arg_vis $arg: $Arg,)+ - $($cache_before_ty: $CacheBeforeTy,)* - $ty_vis $ty_name: $Ty, - $($cache_after_ty: $CacheAfterTy,)* - $($target_name: Option>,)? - } + type Output = ::Type> as $Trait>::Output; - 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,)? - } - } - pub fn canonical(&self) -> $name<$($T::CanonicalType,)* $($C,)*> { - $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,)? - } - } - } - - 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)* - { - } - }; -} - -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)+ - } - } - ) => { - 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)) - } - - fn expr_enum(&$expr_enum_self) -> _ { - $($expr_enum_body)+ - } - - fn to_literal_bits(&self) -> Result, ()> { - self.literal_bits - } - } - } - - 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() + fn $method(self) -> Self::Output { + $Trait::$method(self.to_expr()) } } }; } -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>; - } - match ConstBoolDispatch::new::(self.canonical()) { - ConstBoolDispatch::False(v) => ExprEnum::NotU(v.intern_sized()), - ConstBoolDispatch::True(v) => ExprEnum::NotS(v.intern_sized()), +macro_rules! impl_unary_op_trait { + ( + #[generics($($generics:tt)*)] + fn $Trait:ident::$method:ident($arg:ident: $Arg:ty) -> $Output:ty { + $($body:tt)* + } + ) => { + impl<$($generics)*> $Trait for Expr<$Arg> + { + type Output = Expr<$Output>; + + fn $method(self) -> Self::Output { + let $arg = self; + $($body)* } } + }; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct NotU { + arg: Expr>, + literal_bits: Result, NotALiteralExpr>, +} + +impl NotU { + pub fn new(arg: Expr>) -> Self { + Self { + arg, + literal_bits: arg + .to_literal_bits() + .map(|bits| Intern::intern_owned(bits.to_bitvec().not())), + } + } + pub fn arg(self) -> Expr> { + self.arg + } +} + +impl ToExpr for NotU { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotU(NotU { + arg: Expr::as_dyn_int(self.arg), + literal_bits: self.literal_bits, + }) + .intern(), + __ty: self.arg.__ty, + __flow: Flow::Source, + } } } -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 ToLiteralBits for NotU { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.literal_bits + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn Not::not(arg: UIntType) -> UIntType { + NotU::new(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 { + arg: Expr>, + literal_bits: Result, NotALiteralExpr>, +} + +impl NotS { + pub fn new(arg: Expr>) -> Self { + Self { + arg, + literal_bits: arg + .to_literal_bits() + .map(|bits| Intern::intern_owned(bits.to_bitvec().not())), + } + } + pub fn arg(self) -> Expr> { + self.arg + } +} + +impl ToExpr for NotS { + type Type = UIntType; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotS(NotS { + arg: Expr::as_dyn_int(self.arg), + literal_bits: self.literal_bits, + }) + .intern(), + __ty: self.arg.__ty.as_same_width_uint(), + __flow: Flow::Source, } } } -macro_rules! binary_op { +impl ToLiteralBits for NotS { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.literal_bits + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn Not::not(arg: SIntType) -> UIntType { + NotS::new(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 { + arg: Expr, + literal_bits: Result, NotALiteralExpr>, +} + +impl NotB { + pub fn new(arg: Expr) -> Self { + Self { + arg, + literal_bits: arg + .to_literal_bits() + .map(|bits| Intern::intern_owned(bits.to_bitvec().not())), + } + } + pub fn arg(self) -> Expr { + self.arg + } +} + +impl ToExpr for NotB { + type Type = Bool; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::NotB(NotB { + arg: self.arg, + literal_bits: self.literal_bits, + }) + .intern(), + __ty: self.arg.__ty, + __flow: Flow::Source, + } + } +} + +impl ToLiteralBits for NotB { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.literal_bits + } +} + +impl_unary_op_trait! { + #[generics()] + fn Not::not(arg: Bool) -> Bool { + NotB::new(arg).to_expr() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Neg { + arg: Expr, + literal_bits: Result, NotALiteralExpr>, +} + +impl Neg { + pub fn new(arg: Expr) -> Self { + let mut retval = Self { + arg, + literal_bits: Err(NotALiteralExpr), + }; + let result_ty = retval.ty(); + retval.literal_bits = arg.to_literal_bits().map(|bits| { + Intern::intern_owned(result_ty.bits_from_bigint_wrapping(-SInt::bits_to_bigint(&bits))) + }); + retval + } + pub fn ty(self) -> SInt { + SInt::new_dyn( + Expr::ty(self.arg) + .width() + .checked_add(1) + .expect("width too big"), + ) + } + pub fn arg(self) -> Expr { + self.arg + } +} + +impl ToExpr for Neg { + type Type = SInt; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Neg(*self).intern(), + __ty: self.ty(), + __flow: Flow::Source, + } + } +} + +impl ToLiteralBits for Neg { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.literal_bits + } +} + +impl_unary_op_trait! { + #[generics(Width: Size)] + fn StdNeg::neg(arg: SIntType) -> SInt { + Neg::new(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 { ( - #[ - 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)* + #[generics($($generics:tt)*)] + fn $Trait:ident::$method:ident($lhs:ident: $Lhs:ty, $rhs:ident: $Rhs:ty) -> $Output:ty { + $($body: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()), - } - } - - fn to_literal_bits(&self) -> Result, ()> { - self.literal_bits + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::$name(*self).intern(), + __ty: Bool, + __flow: Flow::Source, } } })* - 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::new($dyn_lhs, $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(); PartialEq::eq(); + struct CmpNeB; fn cmp_ne(); PartialEq::ne(); + struct CmpLtB; fn cmp_lt(); PartialOrd::lt(); + struct CmpLeB; fn cmp_le(); PartialOrd::le(); + struct CmpGtB; fn cmp_gt(); PartialOrd::gt(); + struct CmpGeB; fn cmp_ge(); PartialOrd::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