WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				/ test (push) Successful in 13s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	/ test (push) Successful in 13s
				
			This commit is contained in:
		
							parent
							
								
									cd99dbc849
								
							
						
					
					
						commit
						883668583d
					
				
					 26 changed files with 9207 additions and 7557 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										841
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										841
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,841 @@ | |||
| use crate::{ | ||||
|     hdl_type_common::{ | ||||
|         common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, | ||||
|         ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, | ||||
|     }, | ||||
|     kw, Errors, HdlAttr, PairsIterExt, | ||||
| }; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote_spanned, ToTokens}; | ||||
| use syn::{ | ||||
|     parse_quote, parse_quote_spanned, | ||||
|     punctuated::{Pair, Punctuated}, | ||||
|     spanned::Spanned, | ||||
|     token::Brace, | ||||
|     AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed, | ||||
|     GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub(crate) struct ParsedBundle { | ||||
|     pub(crate) attrs: Vec<Attribute>, | ||||
|     pub(crate) options: HdlAttr<ItemOptions>, | ||||
|     pub(crate) vis: Visibility, | ||||
|     pub(crate) struct_token: Token![struct], | ||||
|     pub(crate) ident: Ident, | ||||
|     pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>, | ||||
|     pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>, | ||||
|     pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>, | ||||
|     pub(crate) mask_type_ident: Ident, | ||||
|     pub(crate) mask_type_match_variant_ident: Ident, | ||||
|     pub(crate) match_variant_ident: Ident, | ||||
|     pub(crate) builder_ident: Ident, | ||||
|     pub(crate) mask_type_builder_ident: Ident, | ||||
| } | ||||
| 
 | ||||
| impl ParsedBundle { | ||||
|     fn parse_field( | ||||
|         errors: &mut Errors, | ||||
|         field: &mut Field, | ||||
|         index: usize, | ||||
|     ) -> Option<HdlAttr<kw::flip>> { | ||||
|         let Field { | ||||
|             attrs, | ||||
|             vis: _, | ||||
|             mutability, | ||||
|             ident, | ||||
|             colon_token, | ||||
|             ty, | ||||
|         } = field; | ||||
|         let ident = ident.get_or_insert_with(|| format_ident!("_{}", index, span = ty.span())); | ||||
|         if !matches!(mutability, FieldMutability::None) { | ||||
|             // FIXME: use mutability as the spanned tokens,
 | ||||
|             // blocked on https://github.com/dtolnay/syn/issues/1717
 | ||||
|             errors.error(&ident, "field mutability is not supported"); | ||||
|             *mutability = FieldMutability::None; | ||||
|         } | ||||
|         *mutability = FieldMutability::None; | ||||
|         colon_token.get_or_insert(Token)); | ||||
|         let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)); | ||||
|         options | ||||
|     } | ||||
|     fn parse(item: ItemStruct) -> syn::Result<Self> { | ||||
|         let ItemStruct { | ||||
|             mut attrs, | ||||
|             vis, | ||||
|             struct_token, | ||||
|             ident, | ||||
|             mut generics, | ||||
|             fields, | ||||
|             semi_token, | ||||
|         } = item; | ||||
|         let mut errors = Errors::new(); | ||||
|         let mut options = errors | ||||
|             .unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs)) | ||||
|             .unwrap_or_default(); | ||||
|         errors.ok(options.body.validate()); | ||||
|         let ItemOptions { | ||||
|             outline_generated: _, | ||||
|             connect_inexact: _, | ||||
|             target: _, | ||||
|             custom_bounds, | ||||
|             no_static: _, | ||||
|         } = options.body; | ||||
|         let mut fields = match fields { | ||||
|             syn::Fields::Named(fields) => fields, | ||||
|             syn::Fields::Unnamed(fields) => { | ||||
|                 errors.error(&fields, "#[hdl] struct must use curly braces: {}"); | ||||
|                 FieldsNamed { | ||||
|                     brace_token: Brace(fields.paren_token.span), | ||||
|                     named: fields.unnamed, | ||||
|                 } | ||||
|             } | ||||
|             syn::Fields::Unit => { | ||||
|                 errors.error(&fields, "#[hdl] struct must use curly braces: {}"); | ||||
|                 FieldsNamed { | ||||
|                     brace_token: Brace(semi_token.unwrap_or_default().span), | ||||
|                     named: Punctuated::default(), | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         let mut field_flips = Vec::with_capacity(fields.named.len()); | ||||
|         for (index, field) in fields.named.iter_mut().enumerate() { | ||||
|             field_flips.push(Self::parse_field(&mut errors, field, index)); | ||||
|         } | ||||
|         let generics = if custom_bounds.is_some() { | ||||
|             MaybeParsed::Unrecognized(generics) | ||||
|         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { | ||||
|             MaybeParsed::Parsed(generics) | ||||
|         } else { | ||||
|             MaybeParsed::Unrecognized(generics) | ||||
|         }; | ||||
|         let fields = TypesParser::maybe_run(generics.as_ref(), fields, &mut errors); | ||||
|         errors.finish()?; | ||||
|         Ok(Self { | ||||
|             attrs, | ||||
|             options, | ||||
|             vis, | ||||
|             struct_token, | ||||
|             generics, | ||||
|             fields, | ||||
|             field_flips, | ||||
|             mask_type_ident: format_ident!("__{}__MaskType", ident), | ||||
|             mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), | ||||
|             match_variant_ident: format_ident!("__{}__MatchVariant", ident), | ||||
|             mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), | ||||
|             builder_ident: format_ident!("__{}__Builder", ident), | ||||
|             ident, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| struct Builder { | ||||
|     vis: Visibility, | ||||
|     struct_token: Token![struct], | ||||
|     ident: Ident, | ||||
|     target: Path, | ||||
|     generics: Generics, | ||||
|     fields: FieldsNamed, | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| enum BuilderFieldState { | ||||
|     Unfilled, | ||||
|     Generic, | ||||
|     Filled, | ||||
| } | ||||
| 
 | ||||
| impl Builder { | ||||
|     fn phantom_field_name(&self) -> Ident { | ||||
|         format_ident!("__phantom", span = self.ident.span()) | ||||
|     } | ||||
|     fn phantom_field(&self) -> Field { | ||||
|         let target = &self.target; | ||||
|         let type_generics = self.generics.split_for_impl().1; | ||||
|         Field { | ||||
|             attrs: vec![], | ||||
|             vis: Visibility::Inherited, | ||||
|             mutability: FieldMutability::None, | ||||
|             ident: Some(self.phantom_field_name()), | ||||
|             colon_token: Some(Token)), | ||||
|             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)); | ||||
|             } | ||||
|             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)); | ||||
|             } | ||||
|             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), | ||||
|                     )] | ||||
|                     .into_iter() | ||||
|                     .chain(fields.named.pairs().map_pair_value_ref(|field| { | ||||
|                         let ident = field.ident.as_ref().unwrap(); | ||||
|                         let type_var = type_var_for_field_name(ident); | ||||
|                         Field { | ||||
|                             vis: Visibility::Inherited, | ||||
|                             ty: parse_quote_spanned! {ident.span()=> | ||||
|                                 #type_var | ||||
|                             }, | ||||
|                             ..field.clone() | ||||
|                         } | ||||
|                     })), | ||||
|                 ), | ||||
|             }), | ||||
|             semi_token: None, | ||||
|         }; | ||||
|         builder_struct.to_tokens(tokens); | ||||
|         let field_idents = Vec::from_iter( | ||||
|             self.fields | ||||
|                 .named | ||||
|                 .iter() | ||||
|                 .map(|field| field.ident.as_ref().unwrap()), | ||||
|         ); | ||||
|         for ( | ||||
|             field_index, | ||||
|             Field { | ||||
|                 vis, | ||||
|                 ident: field_ident, | ||||
|                 ty, | ||||
|                 .. | ||||
|             }, | ||||
|         ) in self.fields.named.iter().enumerate() | ||||
|         { | ||||
|             let field_ident = field_ident.as_ref().unwrap(); | ||||
|             let fn_ident = field_fn_for_field_name(field_ident); | ||||
|             let fn_generics = self.builder_struct_generics(|i| { | ||||
|                 if i == field_index { | ||||
|                     BuilderFieldState::Unfilled | ||||
|                 } else { | ||||
|                     BuilderFieldState::Generic | ||||
|                 } | ||||
|             }); | ||||
|             let (impl_generics, _, where_clause) = fn_generics.split_for_impl(); | ||||
|             let unfilled_ty = self.builder_struct_ty(|i| { | ||||
|                 if i == field_index { | ||||
|                     BuilderFieldState::Unfilled | ||||
|                 } else { | ||||
|                     BuilderFieldState::Generic | ||||
|                 } | ||||
|             }); | ||||
|             let filled_ty = self.builder_struct_ty(|i| { | ||||
|                 if i == field_index { | ||||
|                     BuilderFieldState::Filled | ||||
|                 } else { | ||||
|                     BuilderFieldState::Generic | ||||
|                 } | ||||
|             }); | ||||
|             let pat_fields = | ||||
|                 Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| { | ||||
|                     let field_ident = field.ident.as_ref().unwrap(); | ||||
|                     if field_index == i { | ||||
|                         quote_spanned! {self.ident.span()=> | ||||
|                             #field_ident: _, | ||||
|                         } | ||||
|                     } else { | ||||
|                         quote_spanned! {self.ident.span()=> | ||||
|                             #field_ident, | ||||
|                         } | ||||
|                     } | ||||
|                 })); | ||||
|             quote_spanned! {self.ident.span()=> | ||||
|                 #[automatically_derived] | ||||
|                 #[allow(non_camel_case_types, dead_code)] | ||||
|                 impl #impl_generics #unfilled_ty | ||||
|                 #where_clause | ||||
|                 { | ||||
|                     #vis fn #fn_ident( | ||||
|                         self, | ||||
|                         #field_ident: ::fayalite::expr::Expr<#ty>, | ||||
|                     ) -> #filled_ty { | ||||
|                         let Self { | ||||
|                             #phantom_field_name: _, | ||||
|                             #(#pat_fields)* | ||||
|                         } = self; | ||||
|                         #ident { | ||||
|                             #phantom_field_name: ::fayalite::__std::marker::PhantomData, | ||||
|                             #(#field_idents,)* | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|         let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled); | ||||
|         let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled); | ||||
|         let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl(); | ||||
|         quote_spanned! {self.ident.span()=> | ||||
|             #[automatically_derived] | ||||
|             #[allow(non_camel_case_types, dead_code)] | ||||
|             impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty | ||||
|             #unfilled_where_clause | ||||
|             { | ||||
|                 fn default() -> Self { | ||||
|                     #ident { | ||||
|                         #phantom_field_name: ::fayalite::__std::marker::PhantomData, | ||||
|                         #(#field_idents: ::fayalite::__std::default::Default::default(),)* | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled); | ||||
|         let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled); | ||||
|         let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl(); | ||||
|         let type_generics = self.generics.split_for_impl().1; | ||||
|         quote_spanned! {self.ident.span()=> | ||||
|             #[automatically_derived] | ||||
|             #[allow(non_camel_case_types, dead_code)] | ||||
|             impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty | ||||
|             #filled_where_clause | ||||
|             { | ||||
|                 type Type = #target #type_generics; | ||||
|                 fn to_expr( | ||||
|                     &self, | ||||
|                 ) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> { | ||||
|                     let __ty = #target { | ||||
|                         #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* | ||||
|                     }; | ||||
|                     let __field_values = [ | ||||
|                         #(::fayalite::expr::Expr::canonical(self.#field_idents),)* | ||||
|                     ]; | ||||
|                     ::fayalite::expr::ToExpr::to_expr( | ||||
|                         &::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values), | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToTokens for ParsedBundle { | ||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         let Self { | ||||
|             attrs, | ||||
|             options, | ||||
|             vis, | ||||
|             struct_token, | ||||
|             ident, | ||||
|             generics, | ||||
|             fields, | ||||
|             field_flips, | ||||
|             mask_type_ident, | ||||
|             mask_type_match_variant_ident, | ||||
|             match_variant_ident, | ||||
|             builder_ident, | ||||
|             mask_type_builder_ident, | ||||
|         } = self; | ||||
|         let ItemOptions { | ||||
|             outline_generated: _, | ||||
|             connect_inexact, | ||||
|             target, | ||||
|             custom_bounds: _, | ||||
|             no_static, | ||||
|         } = &options.body; | ||||
|         let target = get_target(target, ident); | ||||
|         let mut item_attrs = attrs.clone(); | ||||
|         item_attrs.push(common_derives(ident.span())); | ||||
|         ItemStruct { | ||||
|             attrs: item_attrs, | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(fields.clone().into()), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||||
|         if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields)) = (generics, fields) { | ||||
|             generics.make_runtime_generics(tokens, vis, ident, &target, |context| { | ||||
|                 let fields: Vec<_> = fields | ||||
|                     .named | ||||
|                     .iter() | ||||
|                     .map(|ParsedField { ident, ty, .. }| { | ||||
|                         let ident = ident.as_ref().unwrap(); | ||||
|                         let expr = ty.make_hdl_type_expr(context); | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             #ident: #expr, | ||||
|                         } | ||||
|                     }) | ||||
|                     .collect(); | ||||
|                 parse_quote_spanned! {ident.span()=> | ||||
|                     #target { | ||||
|                         #(#fields)* | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|         let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||
|         let tokens = wrapped_in_const.inner(); | ||||
|         let builder = Builder { | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: builder_ident.clone(), | ||||
|             target: target.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: fields.clone().into(), | ||||
|         }; | ||||
|         builder.to_tokens(tokens); | ||||
|         let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); | ||||
|         let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled); | ||||
|         let mut mask_type_fields = FieldsNamed::from(fields.clone()); | ||||
|         for Field { ident, ty, .. } in &mut mask_type_fields.named { | ||||
|             let ident = ident.as_ref().unwrap(); | ||||
|             *ty = parse_quote_spanned! {ident.span()=> | ||||
|                 <#ty as ::fayalite::ty::Type>::MaskType | ||||
|             }; | ||||
|         } | ||||
|         let mask_type_builder = Builder { | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: mask_type_builder_ident.clone(), | ||||
|             target: mask_type_ident.clone().into(), | ||||
|             generics: generics.into(), | ||||
|             fields: mask_type_fields.clone(), | ||||
|         }; | ||||
|         mask_type_builder.to_tokens(tokens); | ||||
|         let unfilled_mask_type_builder_ty = | ||||
|             mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); | ||||
|         let filled_mask_type_builder_ty = | ||||
|             mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); | ||||
|         ItemStruct { | ||||
|             attrs: vec![ | ||||
|                 common_derives(ident.span()), | ||||
|                 parse_quote_spanned! {ident.span()=> | ||||
|                     #[allow(non_camel_case_types, dead_code)] | ||||
|                 }, | ||||
|             ], | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: mask_type_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(mask_type_fields.clone()), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut mask_type_match_variant_fields = mask_type_fields; | ||||
|         for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named { | ||||
|             let ident = ident.as_ref().unwrap(); | ||||
|             *ty = parse_quote_spanned! {ident.span()=> | ||||
|                 ::fayalite::expr::Expr<#ty> | ||||
|             }; | ||||
|         } | ||||
|         ItemStruct { | ||||
|             attrs: vec![ | ||||
|                 common_derives(ident.span()), | ||||
|                 parse_quote_spanned! {ident.span()=> | ||||
|                     #[allow(non_camel_case_types, dead_code)] | ||||
|                 }, | ||||
|             ], | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: mask_type_match_variant_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(mask_type_match_variant_fields), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut match_variant_fields = FieldsNamed::from(fields.clone()); | ||||
|         for Field { ident, ty, .. } in &mut match_variant_fields.named { | ||||
|             let ident = ident.as_ref().unwrap(); | ||||
|             *ty = parse_quote_spanned! {ident.span()=> | ||||
|                 ::fayalite::expr::Expr<#ty> | ||||
|             }; | ||||
|         } | ||||
|         ItemStruct { | ||||
|             attrs: vec![ | ||||
|                 common_derives(ident.span()), | ||||
|                 parse_quote_spanned! {ident.span()=> | ||||
|                     #[allow(non_camel_case_types, dead_code)] | ||||
|                 }, | ||||
|             ], | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: match_variant_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(match_variant_fields), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|             let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|             let ident_str = ident.to_string(); | ||||
|             quote_spanned! {ident.span()=> | ||||
|                 #ident: ::fayalite::expr::Expr::field(__this, #ident_str), | ||||
|             } | ||||
|         })); | ||||
|         let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|             let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|             quote_spanned! {ident.span()=> | ||||
|                 #ident: ::fayalite::ty::Type::mask_type(&self.#ident), | ||||
|             } | ||||
|         })); | ||||
|         let from_canonical_body_fields = | ||||
|             Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map( | ||||
|                 |((index, field), flip)| { | ||||
|                     let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                     let ident_str = ident.to_string(); | ||||
|                     let flipped = flip.is_some(); | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         #ident: { | ||||
|                             let ::fayalite::bundle::BundleField { | ||||
|                                 name: __name, | ||||
|                                 flipped: __flipped, | ||||
|                                 ty: __ty, | ||||
|                             } = __fields[#index]; | ||||
|                             ::fayalite::__std::assert_eq!(&*__name, #ident_str); | ||||
|                             ::fayalite::__std::assert_eq!(__flipped, #flipped); | ||||
|                             ::fayalite::ty::Type::from_canonical(__ty) | ||||
|                         }, | ||||
|                     } | ||||
|                 }, | ||||
|             )); | ||||
|         let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map( | ||||
|             |(field, flip)| { | ||||
|                 let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                 let ident_str = ident.to_string(); | ||||
|                 let flipped = flip.is_some(); | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     ::fayalite::bundle::BundleField { | ||||
|                         name: ::fayalite::intern::Intern::intern(#ident_str), | ||||
|                         flipped: #flipped, | ||||
|                         ty: ::fayalite::ty::Type::canonical(&self.#ident), | ||||
|                     }, | ||||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let fields_len = fields.named().into_iter().len(); | ||||
|         quote_spanned! {ident.span()=> | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type MaskType = #mask_type_ident #type_generics; | ||||
|                 type MatchVariant = #mask_type_match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = (); | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||
|                     <Self as ::fayalite::ty::Type>::MatchVariant, | ||||
|                 >; | ||||
|                 type MatchVariantsIter = ::fayalite::__std::iter::Once< | ||||
|                     <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||
|                 >; | ||||
|                 fn match_variants( | ||||
|                     __this: ::fayalite::expr::Expr<Self>, | ||||
|                     __module_builder: &mut ::fayalite::module::ModuleBuilder< | ||||
|                         ::fayalite::module::NormalModule, | ||||
|                     >, | ||||
|                     __source_location: ::fayalite::source_location::SourceLocation, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||
|                     let __retval = #mask_type_match_variant_ident { | ||||
|                         #(#match_variant_body_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) | ||||
|                 } | ||||
|                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||
|                     *self | ||||
|                 } | ||||
|                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||
|                     ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) | ||||
|                 } | ||||
|                 #[track_caller] | ||||
|                 fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||
|                     let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { | ||||
|                         ::fayalite::__std::panic!("expected bundle"); | ||||
|                     }; | ||||
|                     let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); | ||||
|                     ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); | ||||
|                     Self { | ||||
|                         #(#from_canonical_body_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type Builder = #unfilled_mask_type_builder_ty; | ||||
|                 type FilledBuilder = #filled_mask_type_builder_ty; | ||||
|                 fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { | ||||
|                     ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { | ||||
|                     let __this = *__this; | ||||
|                     let __retval = #mask_type_match_variant_ident { | ||||
|                         #(#match_variant_body_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type MaskType = #mask_type_ident #type_generics; | ||||
|                 type MatchVariant = #match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = (); | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||
|                     <Self as ::fayalite::ty::Type>::MatchVariant, | ||||
|                 >; | ||||
|                 type MatchVariantsIter = ::fayalite::__std::iter::Once< | ||||
|                     <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||
|                 >; | ||||
|                 fn match_variants( | ||||
|                     __this: ::fayalite::expr::Expr<Self>, | ||||
|                     __module_builder: &mut ::fayalite::module::ModuleBuilder< | ||||
|                         ::fayalite::module::NormalModule, | ||||
|                     >, | ||||
|                     __source_location: ::fayalite::source_location::SourceLocation, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||
|                     let __retval = #match_variant_ident { | ||||
|                         #(#match_variant_body_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) | ||||
|                 } | ||||
|                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||
|                     #mask_type_ident { | ||||
|                         #(#mask_type_body_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||
|                     ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) | ||||
|                 } | ||||
|                 #[track_caller] | ||||
|                 fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||
|                     let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { | ||||
|                         ::fayalite::__std::panic!("expected bundle"); | ||||
|                     }; | ||||
|                     let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); | ||||
|                     ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); | ||||
|                     Self { | ||||
|                         #(#from_canonical_body_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type Builder = #unfilled_builder_ty; | ||||
|                 type FilledBuilder = #filled_builder_ty; | ||||
|                 fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { | ||||
|                     ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { | ||||
|                     let __this = *__this; | ||||
|                     let __retval = #match_variant_ident { | ||||
|                         #(#match_variant_body_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||
|             let static_generics = generics.clone().for_static_type(); | ||||
|             let (static_impl_generics, static_type_generics, static_where_clause) = | ||||
|                 static_generics.split_for_impl(); | ||||
|             let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|                 let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                 let ty = field.ty(); | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     #ident: <#ty as ::fayalite::ty::StaticType>::TYPE, | ||||
|                 } | ||||
|             })); | ||||
|             let static_mask_type_body_fields = | ||||
|                 Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|                     let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                     let ty = field.ty(); | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         #ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE, | ||||
|                     } | ||||
|                 })); | ||||
|             let type_properties = format_ident!("__type_properties", span = ident.span()); | ||||
|             let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { | ||||
|                 let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                 let flipped = field_flip.is_some(); | ||||
|                 let ty = field.ty(); | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES); | ||||
|                 } | ||||
|             })); | ||||
|             let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { | ||||
|                 let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                 let flipped = field_flip.is_some(); | ||||
|                 let ty = field.ty(); | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES); | ||||
|                 } | ||||
|             })); | ||||
|             quote_spanned! {ident.span()=> | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     const TYPE: Self = Self { | ||||
|                         #(#static_mask_type_body_fields)* | ||||
|                     }; | ||||
|                     const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = Self { | ||||
|                         #(#static_mask_type_body_fields)* | ||||
|                     }; | ||||
|                     const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { | ||||
|                         let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); | ||||
|                         #(#type_properties_mask_fields)* | ||||
|                         #type_properties.finish() | ||||
|                     }; | ||||
|                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { | ||||
|                         let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); | ||||
|                         #(#type_properties_mask_fields)* | ||||
|                         #type_properties.finish() | ||||
|                     }; | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     const TYPE: Self = Self { | ||||
|                         #(#static_type_body_fields)* | ||||
|                     }; | ||||
|                     const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = #mask_type_ident { | ||||
|                         #(#static_mask_type_body_fields)* | ||||
|                     }; | ||||
|                     const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { | ||||
|                         let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); | ||||
|                         #(#type_properties_fields)* | ||||
|                         #type_properties.finish() | ||||
|                     }; | ||||
|                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { | ||||
|                         let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new(); | ||||
|                         #(#type_properties_mask_fields)* | ||||
|                         #type_properties.finish() | ||||
|                     }; | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> { | ||||
|     let item = ParsedBundle::parse(item)?; | ||||
|     let outline_generated = item.options.body.outline_generated; | ||||
|     let mut contents = item.to_token_stream(); | ||||
|     if outline_generated.is_some() { | ||||
|         contents = crate::outline_generated(contents, "hdl-bundle-"); | ||||
|     } | ||||
|     Ok(contents) | ||||
| } | ||||
							
								
								
									
										656
									
								
								crates/fayalite-proc-macros-impl/src/hdl_enum.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										656
									
								
								crates/fayalite-proc-macros-impl/src/hdl_enum.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,656 @@ | |||
| use crate::{ | ||||
|     hdl_type_common::{ | ||||
|         common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, | ||||
|         ParsedType, SplitForImpl, TypesParser, WrappedInConst, | ||||
|     }, | ||||
|     Errors, HdlAttr, PairsIterExt, | ||||
| }; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote_spanned, ToTokens}; | ||||
| use syn::{ | ||||
|     parse_quote_spanned, | ||||
|     punctuated::{Pair, Punctuated}, | ||||
|     token::{Brace, Paren}, | ||||
|     Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, | ||||
|     ItemEnum, ItemStruct, Token, Type, Variant, Visibility, | ||||
| }; | ||||
| 
 | ||||
| crate::options! { | ||||
|     #[options = VariantOptions] | ||||
|     pub(crate) enum VariantOption {} | ||||
| } | ||||
| 
 | ||||
| crate::options! { | ||||
|     #[options = FieldOptions] | ||||
|     pub(crate) enum FieldOption {} | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub(crate) struct ParsedVariantField { | ||||
|     pub(crate) paren_token: Paren, | ||||
|     pub(crate) attrs: Vec<Attribute>, | ||||
|     pub(crate) options: HdlAttr<FieldOptions>, | ||||
|     pub(crate) ty: MaybeParsed<ParsedType, Type>, | ||||
|     pub(crate) comma_token: Option<Token![,]>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub(crate) struct ParsedVariant { | ||||
|     pub(crate) attrs: Vec<Attribute>, | ||||
|     pub(crate) options: HdlAttr<VariantOptions>, | ||||
|     pub(crate) ident: Ident, | ||||
|     pub(crate) field: Option<ParsedVariantField>, | ||||
| } | ||||
| 
 | ||||
| impl ParsedVariant { | ||||
|     fn parse( | ||||
|         errors: &mut Errors, | ||||
|         variant: Variant, | ||||
|         generics: &MaybeParsed<ParsedGenerics, Generics>, | ||||
|     ) -> Self { | ||||
|         let Variant { | ||||
|             mut attrs, | ||||
|             ident, | ||||
|             fields, | ||||
|             discriminant, | ||||
|         } = variant; | ||||
|         let options = errors | ||||
|             .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) | ||||
|             .unwrap_or_default(); | ||||
|         let field = match fields { | ||||
|             Fields::Unnamed(FieldsUnnamed { | ||||
|                 paren_token, | ||||
|                 unnamed, | ||||
|             }) if unnamed.len() == 1 => { | ||||
|                 let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple(); | ||||
|                 let Field { | ||||
|                     mut attrs, | ||||
|                     vis, | ||||
|                     mutability, | ||||
|                     ident: _, | ||||
|                     colon_token: _, | ||||
|                     ty, | ||||
|                 } = field; | ||||
|                 let options = errors | ||||
|                     .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) | ||||
|                     .unwrap_or_default(); | ||||
|                 if !matches!(vis, Visibility::Inherited) { | ||||
|                     errors.error( | ||||
|                         &vis, | ||||
|                         "enum variant fields must not have a visibility specifier", | ||||
|                     ); | ||||
|                 } | ||||
|                 if !matches!(mutability, FieldMutability::None) { | ||||
|                     // FIXME: use mutability as the spanned tokens,
 | ||||
|                     // blocked on https://github.com/dtolnay/syn/issues/1717
 | ||||
|                     errors.error(&ty, "field mutability is not supported"); | ||||
|                 } | ||||
|                 Some(ParsedVariantField { | ||||
|                     paren_token, | ||||
|                     attrs, | ||||
|                     options, | ||||
|                     ty: TypesParser::maybe_run(generics.as_ref(), ty, errors), | ||||
|                     comma_token, | ||||
|                 }) | ||||
|             } | ||||
|             Fields::Unit => None, | ||||
|             Fields::Unnamed(fields) if fields.unnamed.is_empty() => None, | ||||
|             Fields::Named(fields) if fields.named.is_empty() => None, | ||||
|             Fields::Unnamed(_) | Fields::Named(_) => { | ||||
|                 errors.error( | ||||
|                     fields, | ||||
|                     "enum variant must either have no fields or a single parenthesized field", | ||||
|                 ); | ||||
|                 None | ||||
|             } | ||||
|         }; | ||||
|         if let Some((eq, _)) = discriminant { | ||||
|             errors.error(eq, "custom enum discriminants are not allowed"); | ||||
|         } | ||||
|         Self { | ||||
|             attrs, | ||||
|             options, | ||||
|             ident, | ||||
|             field, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub(crate) struct ParsedEnum { | ||||
|     pub(crate) attrs: Vec<Attribute>, | ||||
|     pub(crate) options: HdlAttr<ItemOptions>, | ||||
|     pub(crate) vis: Visibility, | ||||
|     pub(crate) enum_token: Token![enum], | ||||
|     pub(crate) ident: Ident, | ||||
|     pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>, | ||||
|     pub(crate) brace_token: Brace, | ||||
|     pub(crate) variants: Punctuated<ParsedVariant, Token![,]>, | ||||
|     pub(crate) match_variant_ident: Ident, | ||||
| } | ||||
| 
 | ||||
| impl ParsedEnum { | ||||
|     fn parse(item: ItemEnum) -> syn::Result<Self> { | ||||
|         let ItemEnum { | ||||
|             mut attrs, | ||||
|             vis, | ||||
|             enum_token, | ||||
|             ident, | ||||
|             mut generics, | ||||
|             brace_token, | ||||
|             variants, | ||||
|         } = item; | ||||
|         let mut errors = Errors::new(); | ||||
|         let mut options = errors | ||||
|             .unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs)) | ||||
|             .unwrap_or_default(); | ||||
|         errors.ok(options.body.validate()); | ||||
|         let ItemOptions { | ||||
|             outline_generated: _, | ||||
|             connect_inexact: _, | ||||
|             target: _, | ||||
|             custom_bounds, | ||||
|             no_static: _, | ||||
|         } = options.body; | ||||
|         attrs.retain(|attr| { | ||||
|             if attr.path().is_ident("repr") { | ||||
|                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); | ||||
|                 false | ||||
|             } else { | ||||
|                 true | ||||
|             } | ||||
|         }); | ||||
|         let generics = if custom_bounds.is_some() { | ||||
|             MaybeParsed::Unrecognized(generics) | ||||
|         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { | ||||
|             MaybeParsed::Parsed(generics) | ||||
|         } else { | ||||
|             MaybeParsed::Unrecognized(generics) | ||||
|         }; | ||||
|         let variants = Punctuated::from_iter( | ||||
|             variants | ||||
|                 .into_pairs() | ||||
|                 .map_pair_value(|v| ParsedVariant::parse(&mut errors, v, &generics)), | ||||
|         ); | ||||
|         errors.finish()?; | ||||
|         Ok(Self { | ||||
|             attrs, | ||||
|             options, | ||||
|             vis, | ||||
|             enum_token, | ||||
|             generics, | ||||
|             brace_token, | ||||
|             variants, | ||||
|             match_variant_ident: format_ident!("__{}__MatchVariant", ident), | ||||
|             ident, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToTokens for ParsedEnum { | ||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         let Self { | ||||
|             attrs, | ||||
|             options, | ||||
|             vis, | ||||
|             enum_token, | ||||
|             ident, | ||||
|             generics, | ||||
|             brace_token, | ||||
|             variants, | ||||
|             match_variant_ident, | ||||
|         } = self; | ||||
|         let ItemOptions { | ||||
|             outline_generated: _, | ||||
|             connect_inexact, | ||||
|             target, | ||||
|             custom_bounds: _, | ||||
|             no_static, | ||||
|         } = &options.body; | ||||
|         let target = get_target(target, ident); | ||||
|         let mut struct_attrs = attrs.clone(); | ||||
|         struct_attrs.push(common_derives(ident.span())); | ||||
|         struct_attrs.push(parse_quote_spanned! {ident.span()=> | ||||
|             #[allow(non_snake_case)] | ||||
|         }); | ||||
|         let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref( | ||||
|             |ParsedVariant { | ||||
|                  attrs: _, | ||||
|                  options, | ||||
|                  ident, | ||||
|                  field, | ||||
|              }| { | ||||
|                 let VariantOptions {} = options.body; | ||||
|                 let colon_token; | ||||
|                 let ty = if let Some(ParsedVariantField { | ||||
|                     paren_token, | ||||
|                     attrs: _, | ||||
|                     options, | ||||
|                     ty, | ||||
|                     comma_token: _, | ||||
|                 }) = field | ||||
|                 { | ||||
|                     let FieldOptions {} = options.body; | ||||
|                     colon_token = Token); | ||||
|                     ty.clone().into() | ||||
|                 } else { | ||||
|                     colon_token = Token); | ||||
|                     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, | ||||
|             ident: ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: if struct_fields.is_empty() { | ||||
|                 Fields::Unit | ||||
|             } else { | ||||
|                 Fields::Named(FieldsNamed { | ||||
|                     brace_token: *brace_token, | ||||
|                     named: struct_fields, | ||||
|                 }) | ||||
|             }, | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||||
|         if let MaybeParsed::Parsed(generics) = generics { | ||||
|             generics.make_runtime_generics(tokens, vis, ident, &target, |context| { | ||||
|                 let fields: Vec<_> = variants | ||||
|                     .iter() | ||||
|                     .map(|ParsedVariant { ident, field, .. }| { | ||||
|                         if let Some(ParsedVariantField { | ||||
|                             ty: MaybeParsed::Parsed(ty), | ||||
|                             .. | ||||
|                         }) = field | ||||
|                         { | ||||
|                             let expr = ty.make_hdl_type_expr(context); | ||||
|                             quote_spanned! {ident.span()=> | ||||
|                                 #ident: #expr, | ||||
|                             } | ||||
|                         } else { | ||||
|                             quote_spanned! {ident.span()=> | ||||
|                                 #ident: (), | ||||
|                             } | ||||
|                         } | ||||
|                     }) | ||||
|                     .collect(); | ||||
|                 parse_quote_spanned! {ident.span()=> | ||||
|                     #target { | ||||
|                         #(#fields)* | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|         let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||
|         let tokens = wrapped_in_const.inner(); | ||||
|         { | ||||
|             let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||
|             let tokens = wrapped_in_const.inner(); | ||||
|             let mut enum_attrs = attrs.clone(); | ||||
|             enum_attrs.push(parse_quote_spanned! {ident.span()=> | ||||
|                 #[allow(dead_code)] | ||||
|             }); | ||||
|             ItemEnum { | ||||
|                 attrs: enum_attrs, | ||||
|                 vis: vis.clone(), | ||||
|                 enum_token: *enum_token, | ||||
|                 ident: ident.clone(), | ||||
|                 generics: generics.into(), | ||||
|                 brace_token: *brace_token, | ||||
|                 variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref( | ||||
|                     |ParsedVariant { | ||||
|                          attrs, | ||||
|                          options: _, | ||||
|                          ident, | ||||
|                          field, | ||||
|                      }| Variant { | ||||
|                         attrs: attrs.clone(), | ||||
|                         ident: ident.clone(), | ||||
|                         fields: match field { | ||||
|                             Some(ParsedVariantField { | ||||
|                                 paren_token, | ||||
|                                 attrs, | ||||
|                                 options: _, | ||||
|                                 ty, | ||||
|                                 comma_token, | ||||
|                             }) => Fields::Unnamed(FieldsUnnamed { | ||||
|                                 paren_token: *paren_token, | ||||
|                                 unnamed: Punctuated::from_iter([Pair::new( | ||||
|                                     Field { | ||||
|                                         attrs: attrs.clone(), | ||||
|                                         vis: Visibility::Inherited, | ||||
|                                         mutability: FieldMutability::None, | ||||
|                                         ident: None, | ||||
|                                         colon_token: None, | ||||
|                                         ty: ty.clone().into(), | ||||
|                                     }, | ||||
|                                     *comma_token, | ||||
|                                 )]), | ||||
|                             }), | ||||
|                             None => Fields::Unit, | ||||
|                         }, | ||||
|                         discriminant: None, | ||||
|                     }, | ||||
|                 )), | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|         let mut enum_attrs = attrs.clone(); | ||||
|         enum_attrs.push(parse_quote_spanned! {ident.span()=> | ||||
|             #[allow(dead_code, non_camel_case_types)] | ||||
|         }); | ||||
|         ItemEnum { | ||||
|             attrs: enum_attrs, | ||||
|             vis: vis.clone(), | ||||
|             enum_token: *enum_token, | ||||
|             ident: match_variant_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             brace_token: *brace_token, | ||||
|             variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref( | ||||
|                 |ParsedVariant { | ||||
|                      attrs, | ||||
|                      options: _, | ||||
|                      ident, | ||||
|                      field, | ||||
|                  }| Variant { | ||||
|                     attrs: attrs.clone(), | ||||
|                     ident: ident.clone(), | ||||
|                     fields: match field { | ||||
|                         Some(ParsedVariantField { | ||||
|                             paren_token, | ||||
|                             attrs, | ||||
|                             options: _, | ||||
|                             ty, | ||||
|                             comma_token, | ||||
|                         }) => Fields::Unnamed(FieldsUnnamed { | ||||
|                             paren_token: *paren_token, | ||||
|                             unnamed: Punctuated::from_iter([Pair::new( | ||||
|                                 Field { | ||||
|                                     attrs: attrs.clone(), | ||||
|                                     vis: Visibility::Inherited, | ||||
|                                     mutability: FieldMutability::None, | ||||
|                                     ident: None, | ||||
|                                     colon_token: None, | ||||
|                                     ty: parse_quote_spanned! {ident.span()=> | ||||
|                                         ::fayalite::expr::Expr<#ty> | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 *comma_token, | ||||
|                             )]), | ||||
|                         }), | ||||
|                         None => Fields::Unit, | ||||
|                     }, | ||||
|                     discriminant: None, | ||||
|                 }, | ||||
|             )), | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { | ||||
|             if let Some(ParsedVariantField { ty, .. }) = field { | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     #[automatically_derived] | ||||
|                     impl #impl_generics #target #type_generics | ||||
|                     #where_clause | ||||
|                     { | ||||
|                         #[allow(non_snake_case, dead_code)] | ||||
|                         #vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>( | ||||
|                             self, | ||||
|                             v: __V, | ||||
|                         ) -> ::fayalite::expr::Expr<Self> { | ||||
|                             ::fayalite::expr::ToExpr::to_expr( | ||||
|                                 &::fayalite::expr::ops::EnumLiteral::new( | ||||
|                                     self, | ||||
|                                     #index, | ||||
|                                     ::fayalite::__std::option::Option::Some( | ||||
|                                         ::fayalite::expr::Expr::canonical( | ||||
|                                             ::fayalite::expr::ToExpr::to_expr(&v), | ||||
|                                         ), | ||||
|                                     ), | ||||
|                                 ), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     #[automatically_derived] | ||||
|                     impl #impl_generics #target #type_generics | ||||
|                     #where_clause | ||||
|                     { | ||||
|                         #[allow(non_snake_case, dead_code)] | ||||
|                         #vis fn #ident(self) -> ::fayalite::expr::Expr<Self> { | ||||
|                             ::fayalite::expr::ToExpr::to_expr( | ||||
|                                 &::fayalite::expr::ops::EnumLiteral::new( | ||||
|                                     self, | ||||
|                                     #index, | ||||
|                                     ::fayalite::__std::option::Option::None, | ||||
|                                 ), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|         let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map( | ||||
|             |(index, ParsedVariant { ident, field, .. })| { | ||||
|                 let ident_str = ident.to_string(); | ||||
|                 let val = if field.is_some() { | ||||
|                     let missing_value_msg = format!("expected variant {ident} to have a field"); | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         ::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg)) | ||||
|                     } | ||||
|                 } else { | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         ::fayalite::__std::assert!(ty.is_none()); | ||||
|                     } | ||||
|                 }; | ||||
|                 quote_spanned! {ident.span()=> | ||||
|                     #ident: { | ||||
|                         let ::fayalite::enum_::EnumVariant { | ||||
|                             name, | ||||
|                             ty, | ||||
|                         } = variants[#index]; | ||||
|                         ::fayalite::__std::assert_eq!(&*name, #ident_str); | ||||
|                         #val | ||||
|                     }, | ||||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map( | ||||
|             |(index, ParsedVariant { ident, field, .. })| { | ||||
|                 if field.is_some() { | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         #index => #match_variant_ident::#ident( | ||||
|                             ::fayalite::expr::ToExpr::to_expr( | ||||
|                                 &::fayalite::expr::ops::VariantAccess::new_unchecked( | ||||
|                                     variant_access.base(), | ||||
|                                     variant_access.variant_index(), | ||||
|                                 ), | ||||
|                             ), | ||||
|                         ), | ||||
|                     } | ||||
|                 } else { | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         #index => #match_variant_ident::#ident, | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let variants_body_variants = Vec::from_iter(variants.iter().map( | ||||
|             |ParsedVariant { | ||||
|                  attrs: _, | ||||
|                  options, | ||||
|                  ident, | ||||
|                  field, | ||||
|              }| { | ||||
|                 let VariantOptions {} = options.body; | ||||
|                 let ident_str = ident.to_string(); | ||||
|                 match field { | ||||
|                     Some(ParsedVariantField { options, .. }) => { | ||||
|                         let FieldOptions {} = options.body; | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             ::fayalite::enum_::EnumVariant { | ||||
|                                 name: ::fayalite::intern::Intern::intern(#ident_str), | ||||
|                                 ty: ::fayalite::__std::option::Option::Some( | ||||
|                                     ::fayalite::ty::Type::canonical(&self.#ident), | ||||
|                                 ), | ||||
|                             }, | ||||
|                         } | ||||
|                     } | ||||
|                     None => quote_spanned! {ident.span()=> | ||||
|                         ::fayalite::enum_::EnumVariant { | ||||
|                             name: ::fayalite::intern::Intern::intern(#ident_str), | ||||
|                             ty: ::fayalite::__std::option::Option::None, | ||||
|                         }, | ||||
|                     }, | ||||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let variants_len = variants.len(); | ||||
|         quote_spanned! {ident.span()=> | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type MaskType = ::fayalite::int::Bool; | ||||
|                 type MatchVariant = #match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = ::fayalite::module::Scope; | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; | ||||
|                 type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>; | ||||
| 
 | ||||
|                 fn match_variants( | ||||
|                     this: ::fayalite::expr::Expr<Self>, | ||||
|                     module_builder: &mut ::fayalite::module::ModuleBuilder<::fayalite::module::NormalModule>, | ||||
|                     source_location: ::fayalite::source_location::SourceLocation, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||
|                     module_builder.enum_match_variants_helper(this, source_location) | ||||
|                 } | ||||
|                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||
|                     ::fayalite::int::Bool | ||||
|                 } | ||||
|                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||
|                     ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self))) | ||||
|                 } | ||||
|                 #[track_caller] | ||||
|                 #[allow(non_snake_case)] | ||||
|                 fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||
|                     let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else { | ||||
|                         ::fayalite::__std::panic!("expected enum"); | ||||
|                     }; | ||||
|                     let variants = ::fayalite::enum_::EnumType::variants(&enum_); | ||||
|                     ::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants"); | ||||
|                     Self { | ||||
|                         #(#from_canonical_body_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn match_activate_scope( | ||||
|                     v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||
|                 ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) { | ||||
|                     let (variant_access, scope) = v.activate(); | ||||
|                     ( | ||||
|                         match variant_access.variant_index() { | ||||
|                             #(#match_active_scope_match_arms)* | ||||
|                             #variants_len.. => ::fayalite::__std::panic!("invalid variant index"), | ||||
|                         }, | ||||
|                         scope, | ||||
|                     ) | ||||
|                 } | ||||
|                 fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> { | ||||
|                     ::fayalite::intern::Intern::intern(&[ | ||||
|                         #(#variants_body_variants)* | ||||
|                     ][..]) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||
|             let static_generics = generics.clone().for_static_type(); | ||||
|             let (static_impl_generics, static_type_generics, static_where_clause) = | ||||
|                 static_generics.split_for_impl(); | ||||
|             let static_type_body_variants = | ||||
|                 Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { | ||||
|                     if let Some(_) = field { | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             #ident: ::fayalite::ty::StaticType::TYPE, | ||||
|                         } | ||||
|                     } else { | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             #ident: (), | ||||
|                         } | ||||
|                     } | ||||
|                 })); | ||||
|             let type_properties = format_ident!("__type_properties", span = ident.span()); | ||||
|             let type_properties_variants = | ||||
|                 Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { | ||||
|                     let variant = if let Some(ParsedVariantField { ty, .. }) = field { | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             ::fayalite::__std::option::Option::Some( | ||||
|                                 <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES, | ||||
|                             ) | ||||
|                         } | ||||
|                     } else { | ||||
|                         quote_spanned! {ident.span()=> | ||||
|                             ::fayalite::__std::option::Option::None | ||||
|                         } | ||||
|                     }; | ||||
|                     quote_spanned! {ident.span()=> | ||||
|                         let #type_properties = #type_properties.variant(#variant); | ||||
|                     } | ||||
|                 })); | ||||
|             quote_spanned! {ident.span()=> | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType | ||||
|                 for #target #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     const TYPE: Self = Self { | ||||
|                         #(#static_type_body_variants)* | ||||
|                     }; | ||||
|                     const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = | ||||
|                         ::fayalite::int::Bool; | ||||
|                     const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = { | ||||
|                         let #type_properties = ::fayalite::enum_::EnumTypePropertiesBuilder::new(); | ||||
|                         #(#type_properties_variants)* | ||||
|                         #type_properties.finish() | ||||
|                     }; | ||||
|                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = | ||||
|                         <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> { | ||||
|     let item = ParsedEnum::parse(item)?; | ||||
|     let outline_generated = item.options.body.outline_generated; | ||||
|     let mut contents = item.to_token_stream(); | ||||
|     if outline_generated.is_some() { | ||||
|         contents = crate::outline_generated(contents, "hdl-enum-"); | ||||
|     } | ||||
|     Ok(contents) | ||||
| } | ||||
							
								
								
									
										2981
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2981
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,7 +3,10 @@ | |||
| #![cfg_attr(test, recursion_limit = "512")] | ||||
| 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<Infallible> { | ||||
|         self.push(Error::new_spanned(tokens, message)); | ||||
|         self.finish()?; | ||||
|         unreachable!() | ||||
|     } | ||||
|     pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> { | ||||
|         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<std::cmp::Ordering> { | ||||
|                 Some(self.cmp(other)) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl Ord for $option_enum_name { | ||||
|             fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||||
|                 self.variant().cmp(&other.variant()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl quote::IdentFragment for $option_enum_name { | ||||
|             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<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) { | ||||
|                 iter.into_iter().for_each(|v| match v { | ||||
|                     $($option_enum_name::$Variant(v) => { | ||||
|                         self.$key = Some(v); | ||||
|                     })* | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl FromIterator<$option_enum_name> for $options_name { | ||||
|             fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self { | ||||
|                 let mut retval = Self::default(); | ||||
|                 retval.extend(iter); | ||||
|                 retval | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl Extend<$options_name> for $options_name { | ||||
|             fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) { | ||||
|                 iter.into_iter().for_each(|v| { | ||||
|                     $(if let Some(v) = v.$key { | ||||
|                         self.$key = Some(v); | ||||
|                     })* | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl FromIterator<$options_name> for $options_name { | ||||
|             fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self { | ||||
|                 let mut retval = Self::default(); | ||||
|                 retval.extend(iter); | ||||
|                 retval | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ( | ||||
|         #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr] | ||||
|         $(#[$($enum_meta:tt)*])* | ||||
|         $enum_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<Self::Item> { | ||||
|                     $( | ||||
|                         if let Some(value) = self.0.$key.take() { | ||||
|                             return Some($option_enum_name::$Variant(value)); | ||||
|                         } | ||||
|                     )* | ||||
|                     None | ||||
|                 } | ||||
| 
 | ||||
|                 #[allow(unused_mut, unused_variables)] | ||||
|                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { | ||||
|                     $( | ||||
|                         if let Some(value) = self.0.$key.take() { | ||||
|                             init = f(init, $option_enum_name::$Variant(value)); | ||||
|                         } | ||||
|                     )* | ||||
|                     init | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         impl syn::parse::Parse for $options_name { | ||||
|             fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { | ||||
|                 #![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::<syn::Token![,]>()?; | ||||
|                     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<syn::Token![,]> = 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 = <sha2::Sha256 as sha2::Digest>::digest(&contents); | ||||
|     let hash = base16ct::HexDisplay(&hash[..5]); | ||||
|  | @ -728,3 +888,15 @@ pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> { | |||
|         )), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||
|     let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?; | ||||
|     match item { | ||||
|         Item::Enum(item) => hdl_enum::hdl_enum(item), | ||||
|         Item::Struct(item) => hdl_bundle::hdl_bundle(item), | ||||
|         _ => Err(syn::Error::new( | ||||
|             Span::call_site(), | ||||
|             "top-level #[hdl] can only be used on structs or enums", | ||||
|         )), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1290,7 +1290,10 @@ impl Visitor { | |||
|                 memory, | ||||
|                 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, | ||||
|  |  | |||
|  | @ -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(), | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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}; | ||||
|  |  | |||
|  | @ -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<T: Type = CanonicalType, Len: Size = DynSize> { | ||||
|     element: LazyInterned<T>, | ||||
|     len: Len::SizeType, | ||||
|     type_properties: TypeProperties, | ||||
| } | ||||
| 
 | ||||
| pub trait ValueArrayOrSlice: | ||||
|     sealed::Sealed | ||||
|     + BorrowMut<[<Self as ValueArrayOrSlice>::Element]> | ||||
|     + AsRef<[<Self as ValueArrayOrSlice>::Element]> | ||||
|     + AsMut<[<Self as ValueArrayOrSlice>::Element]> | ||||
|     + Hash | ||||
|     + fmt::Debug | ||||
|     + Eq | ||||
|     + Send | ||||
|     + Sync | ||||
|     + 'static | ||||
|     + IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element> | ||||
|     + ToOwned | ||||
|     + InternedCompare | ||||
| { | ||||
|     type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>; | ||||
|     type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>; | ||||
|     type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync; | ||||
|     type Match: 'static | ||||
|         + Clone | ||||
|         + Eq | ||||
|         + fmt::Debug | ||||
|         + Hash | ||||
|         + Send | ||||
|         + Sync | ||||
|         + BorrowMut<[Expr<Self::Element>]>; | ||||
|     type MaskVA: ValueArrayOrSlice< | ||||
|             Element = <Self::ElementType as Type>::MaskValue, | ||||
|             ElementType = <Self::ElementType as Type>::MaskType, | ||||
|             LenType = Self::LenType, | ||||
|             MaskVA = Self::MaskVA, | ||||
|         > + ?Sized; | ||||
|     type IsStaticLen: GenericConstBool; | ||||
|     const FIXED_LEN_TYPE: Option<Self::LenType>; | ||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match; | ||||
|     fn len_from_len_type(v: Self::LenType) -> usize; | ||||
|     #[allow(clippy::result_unit_err)] | ||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>; | ||||
|     fn len_type(&self) -> Self::LenType; | ||||
|     fn len(&self) -> usize; | ||||
|     fn is_empty(&self) -> bool; | ||||
|     fn iter(&self) -> std::slice::Iter<Self::Element> { | ||||
|         Borrow::<[_]>::borrow(self).iter() | ||||
|     } | ||||
|     fn clone_to_arc(&self) -> Arc<Self>; | ||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self; | ||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>; | ||||
| } | ||||
| 
 | ||||
| impl<T> sealed::Sealed for [T] {} | ||||
| 
 | ||||
| impl<V: Value> ValueArrayOrSlice for [V] | ||||
| where | ||||
|     V::Type: Type<Value = V>, | ||||
| { | ||||
|     type Element = V; | ||||
|     type ElementType = V::Type; | ||||
|     type LenType = usize; | ||||
|     type Match = Box<[Expr<V>]>; | ||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue]; | ||||
|     type IsStaticLen = ConstBool<false>; | ||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = None; | ||||
| 
 | ||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match { | ||||
|         (0..array.canonical_type().len()) | ||||
|             .map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()) | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     fn len_from_len_type(v: Self::LenType) -> usize { | ||||
|         v | ||||
|     } | ||||
| 
 | ||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { | ||||
|         Ok(v) | ||||
|     } | ||||
| 
 | ||||
|     fn len_type(&self) -> Self::LenType { | ||||
|         self.len() | ||||
|     } | ||||
| 
 | ||||
|     fn len(&self) -> usize { | ||||
|         <[_]>::len(self) | ||||
|     } | ||||
| 
 | ||||
|     fn is_empty(&self) -> bool { | ||||
|         <[_]>::is_empty(self) | ||||
|     } | ||||
| 
 | ||||
|     fn clone_to_arc(&self) -> Arc<Self> { | ||||
|         Arc::from(self) | ||||
|     } | ||||
| 
 | ||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { | ||||
|         MakeMutSlice::make_mut_slice(v) | ||||
|     } | ||||
| 
 | ||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { | ||||
|         self | ||||
| impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "Array<{:?}, {}>", self.element, self.len()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T, const N: usize> sealed::Sealed for [T; N] {} | ||||
| pub type Array< | ||||
|     T = CanonicalType, | ||||
|     const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE }, | ||||
| > = ArrayType<T, ConstUsize<LEN>>; | ||||
| 
 | ||||
| impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N] | ||||
| where | ||||
|     V::Type: Type<Value = V>, | ||||
| { | ||||
|     type Element = V; | ||||
|     type ElementType = V::Type; | ||||
|     type LenType = (); | ||||
|     type Match = [Expr<V>; N]; | ||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue; N]; | ||||
|     type IsStaticLen = ConstBool<true>; | ||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = Some(()); | ||||
| #[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<Array<Self>>) -> Self::Match { | ||||
|         std::array::from_fn(|index| { | ||||
|             ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr() | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fn len_from_len_type(_v: Self::LenType) -> usize { | ||||
|         N | ||||
|     } | ||||
| 
 | ||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { | ||||
|         if v == N { | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn len_type(&self) -> Self::LenType {} | ||||
| 
 | ||||
|     fn len(&self) -> usize { | ||||
|         N | ||||
|     } | ||||
| 
 | ||||
|     fn is_empty(&self) -> bool { | ||||
|         N == 0 | ||||
|     } | ||||
| 
 | ||||
|     fn clone_to_arc(&self) -> Arc<Self> { | ||||
|         Arc::new(self.clone()) | ||||
|     } | ||||
| 
 | ||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { | ||||
|         Arc::make_mut(v) | ||||
|     } | ||||
| 
 | ||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq, Eq, Hash)] | ||||
| pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> { | ||||
|     element: VA::ElementType, | ||||
|     len: VA::LenType, | ||||
|     bit_width: usize, | ||||
| } | ||||
| 
 | ||||
| pub trait ArrayTypeTrait: | ||||
|     Type< | ||||
|         CanonicalType = ArrayType<[DynCanonicalValue]>, | ||||
|         Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>, | ||||
|         CanonicalValue = Array<[DynCanonicalValue]>, | ||||
|         MaskType = ArrayType< | ||||
|             <<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA, | ||||
|         >, | ||||
|     > + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> | ||||
|     + Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> | ||||
|     + BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> | ||||
|     + sealed::Sealed | ||||
|     + Connect<Self> | ||||
| { | ||||
|     type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType> | ||||
|         + ?Sized; | ||||
|     type Element: Value<Type = Self::ElementType>; | ||||
|     type ElementType: Type<Value = Self::Element>; | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> { | ||||
|     type ValueArrayOrSlice = VA; | ||||
|     type Element = VA::Element; | ||||
|     type ElementType = VA::ElementType; | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             element: self.element.clone(), | ||||
|             len: self.len, | ||||
|             bit_width: self.bit_width, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> { | ||||
|     pub fn element(&self) -> &VA::ElementType { | ||||
|         &self.element | ||||
|     } | ||||
|     pub fn len(&self) -> usize { | ||||
|         VA::len_from_len_type(self.len) | ||||
|     } | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.len() == 0 | ||||
|     } | ||||
|     pub fn bit_width(&self) -> usize { | ||||
|         self.bit_width | ||||
|     } | ||||
|     pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { | ||||
|         ArrayType { | ||||
|             len: self.len(), | ||||
|             element: self.element, | ||||
|             bit_width: self.bit_width, | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_with_len(element: VA::ElementType, len: usize) -> Self { | ||||
|         Self::new_with_len_type( | ||||
|             element, | ||||
|             VA::try_len_type_from_len(len).expect("length should match"), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self { | ||||
|         let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else { | ||||
|             panic!("array is too big: bit-width overflowed"); | ||||
| impl<T: Type, Len: Size> ArrayType<T, Len> { | ||||
|     const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties { | ||||
|         let TypeProperties { | ||||
|             is_passive, | ||||
|             is_storable, | ||||
|             is_castable_from_bits, | ||||
|             bit_width, | ||||
|         } = element; | ||||
|         let Some(bit_width) = bit_width.checked_mul(len) else { | ||||
|             panic!("array too big"); | ||||
|         }; | ||||
|         ArrayType { | ||||
|             element, | ||||
|             len, | ||||
|         TypeProperties { | ||||
|             is_passive, | ||||
|             is_storable, | ||||
|             is_castable_from_bits, | ||||
|             bit_width, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA> | ||||
| where | ||||
|     VA::ElementType: Fold<State>, | ||||
| { | ||||
|     fn fold(self, state: &mut State) -> Result<Self, State::Error> { | ||||
|         state.fold_array_type(self) | ||||
|     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<Self, State::Error> { | ||||
|         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<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> | ||||
| where | ||||
|     VA::ElementType: Visit<State>, | ||||
| { | ||||
|     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<T: Type, Len: KnownSize> ArrayType<T, Len> { | ||||
|     pub fn new_static(element: T) -> Self { | ||||
|         Self::new(element, Len::SizeType::default()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> { | ||||
|     pub fn new_array(element: V::Type) -> Self { | ||||
|         ArrayType::new_with_len_type(element, ()) | ||||
| impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> { | ||||
|     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::<T::MaskType, Len> { | ||||
|         element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()), | ||||
|         len: Len::SIZE, | ||||
|         type_properties: Self::MASK_TYPE_PROPERTIES, | ||||
|     }; | ||||
|     const TYPE_PROPERTIES: TypeProperties = | ||||
|         Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE); | ||||
|     const MASK_TYPE_PROPERTIES: TypeProperties = | ||||
|         Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE); | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> Array<T> { | ||||
|     pub fn new_dyn(element: T, len: usize) -> Self { | ||||
|         Self::new(element, len) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> { | ||||
|     fn static_type() -> Self { | ||||
|         Self::new_array(StaticType::static_type()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> { | ||||
|     pub fn new_slice(element: V::Type, len: usize) -> Self { | ||||
|         ArrayType::new_with_len_type(element, len) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> { | ||||
|     type CanonicalType = ArrayType<[DynCanonicalValue]>; | ||||
|     type Value = Array<VA>; | ||||
|     type CanonicalValue = Array<[DynCanonicalValue]>; | ||||
|     type MaskType = ArrayType<VA::MaskVA>; | ||||
|     type MaskValue = Array<VA::MaskVA>; | ||||
|     type MatchVariant = VA::Match; | ||||
| impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||
|     type MaskType = ArrayType<T::MaskType, Len>; | ||||
|     type MatchVariant = Len::ArrayMatch<T>; | ||||
|     type MatchActiveScope = (); | ||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>; | ||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; | ||||
|     type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; | ||||
| 
 | ||||
|     fn match_variants<IO: BundleValue>( | ||||
|         this: Expr<Self::Value>, | ||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, | ||||
|     fn match_variants( | ||||
|         this: Expr<Self>, | ||||
|         module_builder: &mut ModuleBuilder<NormalModule>, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Self::MatchVariantsIter | ||||
|     where | ||||
|         IO::Type: BundleType<Value = IO>, | ||||
|     { | ||||
|     ) -> 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::<T>::try_from(retval) | ||||
|                 .ok() | ||||
|                 .expect("unreachable"), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         #[derive(Clone, Hash, Eq, PartialEq)] | ||||
|         struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>); | ||||
|         impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {} | ||||
|         impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> { | ||||
|             type Input = ArrayType<T::ValueArrayOrSlice>; | ||||
|             type InputOwned = ArrayType<T::ValueArrayOrSlice>; | ||||
|             type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType; | ||||
| 
 | ||||
|             fn inner(self, input: &Self::Input) -> Self::Output { | ||||
|                 ArrayType::new_with_len_type(input.element.mask_type(), input.len) | ||||
|             } | ||||
|         } | ||||
|         ArrayMaskTypeMemoize::<Self>(PhantomData).get(self) | ||||
|         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(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>( | ||||
|             this, | ||||
|         )?) | ||||
| impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||
|     fn expr_deref(this: &Expr<Self>) -> &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::<T>::try_from(retval) | ||||
|                 .ok() | ||||
|                 .expect("unreachable"), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> | ||||
|     for ArrayType<Lhs> | ||||
| { | ||||
| } | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
| pub struct ArrayWithoutGenerics; | ||||
| 
 | ||||
| impl CanonicalType for ArrayType<[DynCanonicalValue]> { | ||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; | ||||
| } | ||||
| impl<T: Type> Index<T> for ArrayWithoutGenerics { | ||||
|     type Output = ArrayWithoutLen<T>; | ||||
| 
 | ||||
| #[derive(Debug, PartialEq, Eq, Hash)] | ||||
| pub struct Array<VA: ValueArrayOrSlice + ?Sized> { | ||||
|     element_ty: VA::ElementType, | ||||
|     value: Arc<VA>, | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             element_ty: self.element_ty.clone(), | ||||
|             value: self.value.clone(), | ||||
|         } | ||||
|     fn index(&self, element: T) -> &Self::Output { | ||||
|         Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> { | ||||
|     type Type = ArrayType<VA>; | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct ArrayWithoutLen<T: Type> { | ||||
|     element: T, | ||||
| } | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) | ||||
|     } | ||||
| impl<T: Type> Index<usize> for ArrayWithoutLen<T> { | ||||
|     type Output = Array<T>; | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::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<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         Array { | ||||
|             element_ty: self.element_ty.canonical_dyn(), | ||||
|             value: AsRef::<[_]>::as_ref(&*self.value) | ||||
|                 .iter() | ||||
|                 .map(|v| v.to_canonical_dyn()) | ||||
|                 .collect(), | ||||
|         } | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         #[derive(Hash, Eq, PartialEq)] | ||||
|         struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>); | ||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> { | ||||
|             fn clone(&self) -> Self { | ||||
|                 *self | ||||
|             } | ||||
|         } | ||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {} | ||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> { | ||||
|             type Input = Array<VA>; | ||||
|             type InputOwned = Array<VA>; | ||||
|             type Output = Interned<BitSlice>; | ||||
| 
 | ||||
|             fn inner(self, input: &Self::Input) -> Self::Output { | ||||
|                 let mut bits = BitVec::with_capacity(input.ty().bit_width()); | ||||
|                 for element in AsRef::<[_]>::as_ref(&*input.value).iter() { | ||||
|                     bits.extend_from_bitslice(&element.to_bits()); | ||||
|                 } | ||||
|                 Intern::intern_owned(bits) | ||||
|             } | ||||
|         } | ||||
|         ArrayToBitsMemoize::<VA>(PhantomData).get(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalValue for Array<[DynCanonicalValue]> { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::Array(this.clone()) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         Value::to_bits_impl(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> { | ||||
|     pub fn element_ty(&self) -> &VA::ElementType { | ||||
|         &self.element_ty | ||||
|     } | ||||
|     pub fn len(&self) -> usize { | ||||
|         VA::len_from_len_type(self.value.len_type()) | ||||
|     } | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.len() == 0 | ||||
|     } | ||||
|     pub fn value(&self) -> &Arc<VA> { | ||||
|         &self.value | ||||
|     } | ||||
|     pub fn set_element(&mut self, index: usize, element: VA::Element) { | ||||
|         assert_eq!(self.element_ty, element.ty()); | ||||
|         VA::arc_make_mut(&mut self.value)[index] = element; | ||||
|     } | ||||
|     pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self { | ||||
|         for element in value.iter() { | ||||
|             assert_eq!(element_ty, element.ty()); | ||||
|         } | ||||
|         Self { element_ty, value } | ||||
|     } | ||||
|     pub fn into_slice(self) -> Array<[VA::Element]> { | ||||
|         Array { | ||||
|             element_ty: self.element_ty, | ||||
|             value: self.value.arc_to_arc_slice(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA> | ||||
| impl<T: Type, const LEN: usize> Index<ConstUsize<LEN>> for ArrayWithoutLen<T> | ||||
| where | ||||
|     VA::Element: StaticValue, | ||||
|     ConstUsize<LEN>: KnownSize, | ||||
| { | ||||
|     fn from(value: T) -> Self { | ||||
|         Self::new(StaticType::static_type(), value.into()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] { | ||||
|     type Type = ArrayType<[T::Value]>; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         ArrayType::new_with_len_type(StaticType::static_type(), self.len()) | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         let elements = Intern::intern_owned(Vec::from_iter( | ||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), | ||||
|         )); | ||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> { | ||||
|     type Type = ArrayType<[T::Value]>; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         <[E]>::ty(self) | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         <[E]>::to_expr(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] { | ||||
|     type Type = ArrayType<[T::Value; N]>; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         ArrayType::new_with_len_type(StaticType::static_type(), ()) | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         let elements = Intern::intern_owned(Vec::from_iter( | ||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), | ||||
|         )); | ||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct ArrayIntoIter<VA: ValueArrayOrSlice> { | ||||
|     array: Arc<VA>, | ||||
|     indexes: std::ops::Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> { | ||||
|     type Item = VA::Element; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         Some(self.array[self.indexes.next()?].clone()) | ||||
|     } | ||||
| 
 | ||||
|     fn size_hint(&self) -> (usize, Option<usize>) { | ||||
|         self.indexes.size_hint() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> { | ||||
|     fn next_back(&mut self) -> Option<Self::Item> { | ||||
|         Some(self.array[self.indexes.next_back()?].clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> Array<VA> { | ||||
|     pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> { | ||||
|         self.value.iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> { | ||||
|     type Item = &'a VA::Element; | ||||
|     type IntoIter = std::slice::Iter<'a, VA::Element>; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         self.value.iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> { | ||||
|     type Item = VA::Element; | ||||
|     type IntoIter = ArrayIntoIter<VA>; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         ArrayIntoIter { | ||||
|             indexes: 0..self.len(), | ||||
|             array: self.value, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct ArrayExprIter<VA: ValueArrayOrSlice> { | ||||
|     array: Expr<Array<VA>>, | ||||
|     indexes: std::ops::Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> { | ||||
|     type Item = Expr<VA::Element>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next()?)) | ||||
|     } | ||||
| 
 | ||||
|     fn size_hint(&self) -> (usize, Option<usize>) { | ||||
|         self.indexes.size_hint() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> { | ||||
|     fn next_back(&mut self) -> Option<Self::Item> { | ||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> { | ||||
|     type Item = Expr<VA::Element>; | ||||
|     type IntoIter = ArrayExprIter<VA>; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         self.iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> { | ||||
|     type Item = Expr<VA::Element>; | ||||
|     type IntoIter = ArrayExprIter<VA>; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         self.iter() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice> Expr<Array<VA>> { | ||||
|     pub fn len(self) -> usize { | ||||
|         self.canonical_type().len() | ||||
|     } | ||||
|     pub fn is_empty(self) -> bool { | ||||
|         self.canonical_type().is_empty() | ||||
|     } | ||||
|     pub fn iter(self) -> ArrayExprIter<VA> { | ||||
|         ArrayExprIter { | ||||
|             indexes: 0..self.len(), | ||||
|             array: self, | ||||
|         } | ||||
|     type Output = Array<T, LEN>; | ||||
| 
 | ||||
|     fn index(&self, len: ConstUsize<LEN>) -> &Self::Output { | ||||
|         Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len))) | ||||
|     } | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -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<Self> 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<Self> { | ||||
|         Expr::from_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Value for Clock { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         *self | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalValue for Clock { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::Clock(*this) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)] | ||||
| #[hdl(static, outline_generated)] | ||||
| pub struct ClockDomain { | ||||
|     pub clk: Clock, | ||||
|     pub rst: Reset, | ||||
| 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<Clock> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToClock for Clock { | ||||
|     fn to_clock(&self) -> Expr<Clock> { | ||||
|         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<Clock> { | ||||
|         self.to_expr().to_clock() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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<T> { | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct EnumVariant { | ||||
|     pub name: Interned<str>, | ||||
|     pub ty: Option<T>, | ||||
| } | ||||
| 
 | ||||
| pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>); | ||||
| 
 | ||||
| impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let VariantType { name, ref ty } = *self.0; | ||||
|         if let Some(ty) = ty { | ||||
|             write!(f, "{name}({ty:?})") | ||||
|         } else { | ||||
|             write!(f, "{name}") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         fmt::Debug::fmt(self, f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T> VariantType<T> { | ||||
|     pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> { | ||||
|         let Self { name, ty } = self; | ||||
|         VariantType { name, ty: f(ty) } | ||||
|     } | ||||
|     pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> { | ||||
|         let Self { name, ty } = self; | ||||
|         VariantType { | ||||
|             name, | ||||
|             ty: ty.map(f), | ||||
|         } | ||||
|     } | ||||
|     pub fn as_ref_ty(&self) -> VariantType<&T> { | ||||
|         VariantType { | ||||
|             name: self.name, | ||||
|             ty: self.ty.as_ref(), | ||||
|         } | ||||
|     } | ||||
|     pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> { | ||||
|         FmtDebugInEnum(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> VariantType<T> { | ||||
|     pub fn canonical(&self) -> VariantType<T::CanonicalType> { | ||||
|         self.as_ref_ty().map_ty(T::canonical) | ||||
|     } | ||||
|     pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> { | ||||
|         self.as_ref_ty().map_ty(T::to_dyn) | ||||
|     } | ||||
|     pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> { | ||||
|         self.as_ref_ty().map_ty(T::canonical_dyn) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl VariantType<Interned<dyn DynCanonicalType>> { | ||||
|     pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T { | ||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); | ||||
|         let Some(ty) = self.ty else { | ||||
|             panic!("variant {expected_name} has no value but a value is expected"); | ||||
|         }; | ||||
|         T::from_dyn_canonical_type(ty) | ||||
|     } | ||||
|     pub fn from_canonical_type_helper_no_value(self, expected_name: &str) { | ||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); | ||||
|         assert!( | ||||
|             self.ty.is_none(), | ||||
|             "variant {expected_name} has a value but is expected to have no value" | ||||
|         ); | ||||
|     } | ||||
|     pub ty: Option<CanonicalType>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Eq)] | ||||
| struct DynEnumTypeImpl { | ||||
|     variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, | ||||
| struct EnumImpl { | ||||
|     variants: Interned<[EnumVariant]>, | ||||
|     name_indexes: HashMap<Interned<str>, 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<H: std::hash::Hasher>(&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<H: Hasher>(&self, state: &mut H) { | ||||
|         self.variants.hash(state); | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct Enum(Interned<EnumImpl>); | ||||
| 
 | ||||
| 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<DynEnumTypeImpl>); | ||||
| #[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<TypeProperties>) -> 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<Interned<dyn DynCanonicalType>>]>) -> 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<Interned<str>, usize> { | ||||
|         &self.0.name_indexes | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Hash, PartialEq, Eq)] | ||||
| pub struct DynEnum { | ||||
|     ty: DynEnumType, | ||||
|     variant_index: usize, | ||||
|     variant_value: Option<DynCanonicalValue>, | ||||
| } | ||||
| 
 | ||||
| impl DynEnum { | ||||
|     #[track_caller] | ||||
|     pub fn new_by_index( | ||||
|         ty: DynEnumType, | ||||
|         variant_index: usize, | ||||
|         variant_value: Option<DynCanonicalValue>, | ||||
|     ) -> Self { | ||||
|         let variant = ty.variants()[variant_index]; | ||||
|         assert_eq!( | ||||
|             variant_value.as_ref().map(|v| v.ty()), | ||||
|             variant.ty, | ||||
|             "variant value doesn't match type" | ||||
|         ); | ||||
|         Self { | ||||
|             ty, | ||||
|             variant_index, | ||||
|             variant_value, | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_by_name( | ||||
|         ty: DynEnumType, | ||||
|         variant_name: Interned<str>, | ||||
|         variant_value: Option<DynCanonicalValue>, | ||||
|     ) -> Self { | ||||
|         let variant_index = ty.name_indexes()[&variant_name]; | ||||
|         Self::new_by_index(ty, variant_index, variant_value) | ||||
|     } | ||||
|     pub fn variant_index(&self) -> usize { | ||||
|         self.variant_index | ||||
|     } | ||||
|     pub fn variant_value(&self) -> &Option<DynCanonicalValue> { | ||||
|         &self.variant_value | ||||
|     } | ||||
|     pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> { | ||||
|         self.ty.variants()[self.variant_index] | ||||
|     } | ||||
|     pub fn variant_name(&self) -> Interned<str> { | ||||
|         self.variant_with_type().name | ||||
|     } | ||||
|     pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> { | ||||
|         self.variant_with_type().ty | ||||
|     } | ||||
|     pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> { | ||||
|         self.variant_with_type() | ||||
|             .map_opt_ty(|_| self.variant_value.as_ref()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
| pub struct VariantsHint { | ||||
|     pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>, | ||||
|     pub more_variants: bool, | ||||
| } | ||||
| 
 | ||||
| impl VariantsHint { | ||||
|     pub fn new( | ||||
|         known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>, | ||||
|         more_variants: bool, | ||||
|     ) -> Self { | ||||
|         let known_variants = Intern::intern_owned(Vec::from_iter(known_variants)); | ||||
|         Self { | ||||
|             known_variants, | ||||
|             more_variants, | ||||
|         } | ||||
|     } | ||||
|     pub fn check_variant( | ||||
|         self, | ||||
|         index: usize, | ||||
|         variant: VariantType<&dyn DynType>, | ||||
|     ) -> Result<(), String> { | ||||
|         let Some(&known_variant) = self.known_variants.get(index) else { | ||||
|             return if self.more_variants { | ||||
|                 Ok(()) | ||||
|             } else { | ||||
|                 Err(format!( | ||||
|                     "too many variants: name={:?} index={index}", | ||||
|                     variant.name | ||||
|                 )) | ||||
|             }; | ||||
|         }; | ||||
|         let VariantType { | ||||
|             name: known_name, | ||||
|             ty: type_hint, | ||||
|         } = known_variant; | ||||
|         let VariantType { name, ty } = variant; | ||||
|         if name != known_name { | ||||
|             Err(format!( | ||||
|                 "wrong variant name {name:?}, expected {known_name:?}" | ||||
|             )) | ||||
|         } else { | ||||
|             match (ty, type_hint) { | ||||
|                 (Some(ty), Some(type_hint)) => type_hint.matches(ty), | ||||
|                 (None, None) => Ok(()), | ||||
|                 (None, Some(_)) => Err(format!( | ||||
|                     "expected variant {name:?} to have type, no type provided" | ||||
|                 )), | ||||
|                 (Some(_), None) => Err(format!( | ||||
|                     "expected variant {name:?} to have no type, but a type was provided" | ||||
|                 )), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait EnumType: | ||||
|     Type< | ||||
|         CanonicalType = DynEnumType, | ||||
|         CanonicalValue = DynEnum, | ||||
|         MaskType = UIntType<1>, | ||||
|         MaskValue = UInt<1>, | ||||
|         MatchActiveScope = Scope, | ||||
|         MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, | ||||
|         MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||
|     > + Connect<Self> | ||||
| where | ||||
|     Self::Value: EnumValue + ToExpr<Type = Self>, | ||||
|     MaskType = Bool, | ||||
|     MatchActiveScope = Scope, | ||||
|     MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, | ||||
|     MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||
| > | ||||
| { | ||||
|     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<Interned<dyn DynCanonicalType>>]>; | ||||
|     fn variants_hint() -> VariantsHint; | ||||
|     #[allow(clippy::result_unit_err)] | ||||
|     fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>( | ||||
|         &self, | ||||
|         variant_index: usize, | ||||
|         variant_value: Option<&VariantValue>, | ||||
|     ) -> Result<Interned<BitSlice>, ()> { | ||||
|         #[derive(Hash, Eq, PartialEq)] | ||||
|         struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>); | ||||
|         impl<E, V> Clone for VariantToBitsMemoize<E, V> { | ||||
|             fn clone(&self) -> Self { | ||||
|                 *self | ||||
|             } | ||||
|         } | ||||
|         impl<E, V> Copy for VariantToBitsMemoize<E, V> {} | ||||
|         impl< | ||||
|                 E: EnumType<Value: EnumValue<Type = E>>, | ||||
|                 V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone, | ||||
|             > MemoizeGeneric for VariantToBitsMemoize<E, V> | ||||
|         { | ||||
|             type InputRef<'a> = (&'a E, usize, Option<&'a V>); | ||||
|             type InputOwned = (E, usize, Option<V>); | ||||
|             type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>); | ||||
|             type Output = Result<Interned<BitSlice>, ()>; | ||||
| 
 | ||||
|             fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { | ||||
|                 (&input.0, input.1, input.2.as_ref()) | ||||
|             } | ||||
|             fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { | ||||
|                 a == b | ||||
|             } | ||||
|             fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { | ||||
|                 (input.0.into_owned(), input.1, input.2.map(Cow::into_owned)) | ||||
|             } | ||||
|             fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { | ||||
|                 (&input.0, input.1, input.2.as_deref()) | ||||
|             } | ||||
|             fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { | ||||
|                 (Cow::Owned(input.0), input.1, input.2.map(Cow::Owned)) | ||||
|             } | ||||
|             fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { | ||||
|                 (Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed)) | ||||
|             } | ||||
|             fn inner(self, input: Self::InputRef<'_>) -> Self::Output { | ||||
|                 let (ty, variant_index, variant_value) = input; | ||||
|                 let ty = ty.canonical(); | ||||
|                 let mut bits = BitVec::with_capacity(ty.bit_width()); | ||||
|                 bits.extend_from_bitslice( | ||||
|                     &variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()], | ||||
|                 ); | ||||
|                 if let Some(variant_value) = variant_value { | ||||
|                     bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?); | ||||
|                 } | ||||
|                 bits.resize(ty.bit_width(), false); | ||||
|                 Ok(Intern::intern_owned(bits)) | ||||
|             } | ||||
|         } | ||||
|         VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get(( | ||||
|             self, | ||||
|             variant_index, | ||||
|             variant_value, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait EnumValue: Value | ||||
| where | ||||
|     <Self as ToExpr>::Type: EnumType<Value = Self>, | ||||
| { | ||||
| } | ||||
| pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>); | ||||
| 
 | ||||
| pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>) | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>; | ||||
| 
 | ||||
| impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
| impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> { | ||||
|     type MatchVariant = T::MatchVariant; | ||||
|     type MatchActiveScope = Scope; | ||||
| 
 | ||||
|  | @ -427,36 +190,22 @@ where | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
|     pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> { | ||||
| impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> { | ||||
|     pub fn variant_access(&self) -> Interned<VariantAccess> { | ||||
|         self.0.variant_access() | ||||
|     } | ||||
|     pub fn activate( | ||||
|         self, | ||||
|     ) -> ( | ||||
|         Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>, | ||||
|         Scope, | ||||
|     ) { | ||||
|     pub fn activate(self) -> (Interned<VariantAccess>, Scope) { | ||||
|         self.0.activate() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct EnumMatchVariantsIter<T: EnumType> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
| pub struct EnumMatchVariantsIter<T: EnumType> { | ||||
|     pub(crate) inner: EnumMatchVariantsIterImpl<T>, | ||||
|     pub(crate) variant_index: std::ops::Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
| impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> { | ||||
|     type Item = EnumMatchVariantAndInactiveScope<T>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|  | @ -470,21 +219,15 @@ where | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
| impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> { | ||||
|     fn len(&self) -> usize { | ||||
|         self.variant_index.len() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {} | ||||
| impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {} | ||||
| 
 | ||||
| impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> | ||||
| where | ||||
|     T::Value: EnumValue<Type = T>, | ||||
| { | ||||
| impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> { | ||||
|     fn next_back(&mut self) -> Option<Self::Item> { | ||||
|         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<Expr<DynCanonicalValue>>; | ||||
|     type MatchActiveScope = Scope; | ||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||
|     type MatchVariantsIter = EnumMatchVariantsIter<Self>; | ||||
| 
 | ||||
|     fn match_variants<IO: BundleValue>( | ||||
|         this: Expr<Self::Value>, | ||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Self::MatchVariantsIter | ||||
|     where | ||||
|         IO::Type: crate::bundle::BundleType<Value = IO>, | ||||
|     { | ||||
|         module_builder.enum_match_variants_helper(this, source_location) | ||||
|     } | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         UIntType::new() | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> Self::CanonicalType { | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     fn source_location(&self) -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn type_enum(&self) -> TypeEnum { | ||||
|         TypeEnum::EnumType(*self) | ||||
|     } | ||||
| 
 | ||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { | ||||
|         t | ||||
|     } | ||||
| 
 | ||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { | ||||
|         Some(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Connect<Self> for DynEnumType {} | ||||
| 
 | ||||
| pub struct NoBuilder; | ||||
| 
 | ||||
| impl EnumType for DynEnumType { | ||||
|     type Builder = NoBuilder; | ||||
| 
 | ||||
| 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<Interned<dyn DynCanonicalType>>]> { | ||||
|     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<Expr<CanonicalType>>; | ||||
|     type MatchActiveScope = Scope; | ||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||
|     type MatchVariantsIter = EnumMatchVariantsIter<Self>; | ||||
| 
 | ||||
|     fn match_variants( | ||||
|         this: Expr<Self>, | ||||
|         module_builder: &mut ModuleBuilder<NormalModule>, | ||||
|         source_location: crate::source_location::SourceLocation, | ||||
|     ) -> Self::MatchVariantsIter { | ||||
|         module_builder.enum_match_variants_helper(this, source_location) | ||||
|     } | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         Bool | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> CanonicalType { | ||||
|         CanonicalType::Enum(*self) | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|         let CanonicalType::Enum(retval) = canonical_type else { | ||||
|             panic!("expected enum"); | ||||
|         }; | ||||
|         retval | ||||
|     } | ||||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalType for DynEnumType { | ||||
|     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<<Self::Type as Type>::Value> { | ||||
|         Expr::from_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Value for DynEnum { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         self.clone() | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         this.ty | ||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) | ||||
|             .unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl EnumValue for DynEnum {} | ||||
| 
 | ||||
| impl CanonicalValue for DynEnum { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::Enum(this.clone()) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         this.ty | ||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) | ||||
|             .unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| mod impl_option { | ||||
|     #[allow(dead_code)] | ||||
|     #[derive(crate::ty::Value)] | ||||
|     #[hdl(target(std::option::Option), connect_inexact, outline_generated)] | ||||
|     pub enum Option<T> { | ||||
|         None, | ||||
|         Some(T), | ||||
|     } | ||||
| #[hdl(outline_generated)] | ||||
| pub enum HdlOption<T: Type> { | ||||
|     None, | ||||
|     Some(T), | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										398
									
								
								crates/fayalite/src/expr/target.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								crates/fayalite/src/expr/target.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,398 @@ | |||
| use crate::{ | ||||
|     array::Array, | ||||
|     bundle::{Bundle, BundleField}, | ||||
|     expr::Flow, | ||||
|     intern::{Intern, Interned}, | ||||
|     memory::{DynPortType, MemPort}, | ||||
|     module::{Instance, ModuleIO, TargetName}, | ||||
|     reg::Reg, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, Type}, | ||||
|     wire::Wire, | ||||
| }; | ||||
| use std::fmt; | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct TargetPathBundleField { | ||||
|     pub name: Interned<str>, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for TargetPathBundleField { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, ".{}", self.name) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct TargetPathArrayElement { | ||||
|     pub index: usize, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for TargetPathArrayElement { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "[{}]", self.index) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct TargetPathDynArrayElement {} | ||||
| 
 | ||||
| impl fmt::Display for TargetPathDynArrayElement { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "[<dyn>]") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub enum TargetPathElement { | ||||
|     BundleField(TargetPathBundleField), | ||||
|     ArrayElement(TargetPathArrayElement), | ||||
|     DynArrayElement(TargetPathDynArrayElement), | ||||
| } | ||||
| 
 | ||||
| impl From<TargetPathBundleField> for TargetPathElement { | ||||
|     fn from(value: TargetPathBundleField) -> Self { | ||||
|         Self::BundleField(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<TargetPathArrayElement> for TargetPathElement { | ||||
|     fn from(value: TargetPathArrayElement) -> Self { | ||||
|         Self::ArrayElement(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<TargetPathDynArrayElement> for TargetPathElement { | ||||
|     fn from(value: TargetPathDynArrayElement) -> Self { | ||||
|         Self::DynArrayElement(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for TargetPathElement { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             Self::BundleField(v) => v.fmt(f), | ||||
|             Self::ArrayElement(v) => v.fmt(f), | ||||
|             Self::DynArrayElement(v) => v.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TargetPathElement { | ||||
|     pub fn canonical_ty(&self, parent: Interned<Target>) -> CanonicalType { | ||||
|         match self { | ||||
|             &Self::BundleField(TargetPathBundleField { name }) => { | ||||
|                 let parent_ty = Bundle::from_canonical(parent.canonical_ty()); | ||||
|                 let field = parent_ty | ||||
|                     .field_by_name(name) | ||||
|                     .expect("field name is known to be a valid field of parent type"); | ||||
|                 field.ty | ||||
|             } | ||||
|             &Self::ArrayElement(TargetPathArrayElement { index }) => { | ||||
|                 let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty()); | ||||
|                 assert!(index < parent_ty.len()); | ||||
|                 parent_ty.element() | ||||
|             } | ||||
|             Self::DynArrayElement(_) => { | ||||
|                 let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty()); | ||||
|                 parent_ty.element() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub fn flow(&self, parent: Interned<Target>) -> Flow { | ||||
|         match self { | ||||
|             Self::BundleField(v) => { | ||||
|                 let parent_ty = Bundle::from_canonical(parent.canonical_ty()); | ||||
|                 let field = parent_ty | ||||
|                     .field_by_name(v.name) | ||||
|                     .expect("field name is known to be a valid field of parent type"); | ||||
|                 parent.flow().flip_if(field.flipped) | ||||
|             } | ||||
|             Self::ArrayElement(_) => parent.flow(), | ||||
|             Self::DynArrayElement(_) => parent.flow(), | ||||
|         } | ||||
|     } | ||||
|     pub fn is_static(&self) -> bool { | ||||
|         match self { | ||||
|             Self::BundleField(_) | Self::ArrayElement(_) => true, | ||||
|             Self::DynArrayElement(_) => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_target_base { | ||||
|     ( | ||||
|         $(#[$enum_meta:meta])* | ||||
|         $enum_vis:vis enum $TargetBase:ident { | ||||
|             $( | ||||
|                 #[is = $is_fn:ident] | ||||
|                 #[to = $to_fn:ident] | ||||
|                 $(#[$variant_meta:meta])* | ||||
|                 $Variant:ident($VariantTy:ty), | ||||
|             )* | ||||
|         } | ||||
|     ) => { | ||||
|         $(#[$enum_meta])* | ||||
|         $enum_vis enum $TargetBase { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Variant($VariantTy), | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         impl fmt::Debug for $TargetBase { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 match self { | ||||
|                     $(Self::$Variant(v) => v.fmt(f),)* | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $( | ||||
|         impl From<$VariantTy> for $TargetBase { | ||||
|             fn from(value: $VariantTy) -> Self { | ||||
|                 Self::$Variant(value) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl From<$VariantTy> for Target { | ||||
|             fn from(value: $VariantTy) -> Self { | ||||
|                 $TargetBase::$Variant(value).into() | ||||
|             } | ||||
|         } | ||||
|         )* | ||||
| 
 | ||||
|         impl $TargetBase { | ||||
|             $( | ||||
|             $enum_vis fn $is_fn(&self) -> bool { | ||||
|                 self.$to_fn().is_some() | ||||
|             } | ||||
|             $enum_vis fn $to_fn(&self) -> Option<&$VariantTy> { | ||||
|                 if let Self::$Variant(retval) = self { | ||||
|                     Some(retval) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
|             )* | ||||
|             $enum_vis fn must_connect_to(&self) -> bool { | ||||
|                 match self { | ||||
|                     $(Self::$Variant(v) => v.must_connect_to(),)* | ||||
|                 } | ||||
|             } | ||||
|             $enum_vis fn flow(&self) -> Flow { | ||||
|                 match self { | ||||
|                     $(Self::$Variant(v) => v.flow(),)* | ||||
|                 } | ||||
|             } | ||||
|             $enum_vis fn source_location(&self) -> SourceLocation { | ||||
|                 match self { | ||||
|                     $(Self::$Variant(v) => v.source_location(),)* | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_target_base! { | ||||
|     #[derive(Clone, PartialEq, Eq, Hash)] | ||||
|     pub enum TargetBase { | ||||
|         #[is = is_module_io] | ||||
|         #[to = module_io] | ||||
|         ModuleIO(ModuleIO<CanonicalType>), | ||||
|         #[is = is_mem_port] | ||||
|         #[to = mem_port] | ||||
|         MemPort(MemPort<DynPortType>), | ||||
|         #[is = is_reg] | ||||
|         #[to = reg] | ||||
|         Reg(Reg<CanonicalType>), | ||||
|         #[is = is_wire] | ||||
|         #[to = wire] | ||||
|         Wire(Wire<CanonicalType>), | ||||
|         #[is = is_instance] | ||||
|         #[to = instance] | ||||
|         Instance(Instance<Bundle>), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for TargetBase { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "{:?}", self.target_name()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TargetBase { | ||||
|     pub fn target_name(&self) -> TargetName { | ||||
|         match self { | ||||
|             TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None), | ||||
|             TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())), | ||||
|             TargetBase::Reg(v) => TargetName(v.scoped_name(), None), | ||||
|             TargetBase::Wire(v) => TargetName(v.scoped_name(), None), | ||||
|             TargetBase::Instance(v) => TargetName(v.scoped_name(), None), | ||||
|         } | ||||
|     } | ||||
|     pub fn canonical_ty(&self) -> CanonicalType { | ||||
|         match self { | ||||
|             TargetBase::ModuleIO(v) => v.ty(), | ||||
|             TargetBase::MemPort(v) => v.ty().canonical(), | ||||
|             TargetBase::Reg(v) => v.ty(), | ||||
|             TargetBase::Wire(v) => v.ty(), | ||||
|             TargetBase::Instance(v) => v.ty().canonical(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct TargetChild { | ||||
|     parent: Interned<Target>, | ||||
|     path_element: Interned<TargetPathElement>, | ||||
|     canonical_ty: CanonicalType, | ||||
|     flow: Flow, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for TargetChild { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let Self { | ||||
|             parent, | ||||
|             path_element, | ||||
|             canonical_ty: _, | ||||
|             flow: _, | ||||
|         } = self; | ||||
|         parent.fmt(f)?; | ||||
|         fmt::Display::fmt(path_element, f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for TargetChild { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let Self { | ||||
|             parent, | ||||
|             path_element, | ||||
|             canonical_ty: _, | ||||
|             flow: _, | ||||
|         } = self; | ||||
|         parent.fmt(f)?; | ||||
|         path_element.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl TargetChild { | ||||
|     pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self { | ||||
|         Self { | ||||
|             parent, | ||||
|             path_element, | ||||
|             canonical_ty: path_element.canonical_ty(parent), | ||||
|             flow: path_element.flow(parent), | ||||
|         } | ||||
|     } | ||||
|     pub fn parent(self) -> Interned<Target> { | ||||
|         self.parent | ||||
|     } | ||||
|     pub fn path_element(self) -> Interned<TargetPathElement> { | ||||
|         self.path_element | ||||
|     } | ||||
|     pub fn canonical_ty(self) -> CanonicalType { | ||||
|         self.canonical_ty | ||||
|     } | ||||
|     pub fn flow(self) -> Flow { | ||||
|         self.flow | ||||
|     } | ||||
|     pub fn bundle_field(self) -> Option<BundleField> { | ||||
|         if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element { | ||||
|             let parent_ty = Bundle::from_canonical(self.parent.canonical_ty()); | ||||
|             Some( | ||||
|                 parent_ty | ||||
|                     .field_by_name(name) | ||||
|                     .expect("field name known to be a valid field of parent"), | ||||
|             ) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash)] | ||||
| pub enum Target { | ||||
|     Base(Interned<TargetBase>), | ||||
|     Child(TargetChild), | ||||
| } | ||||
| 
 | ||||
| impl From<TargetBase> for Target { | ||||
|     fn from(value: TargetBase) -> Self { | ||||
|         Self::Base(Intern::intern_sized(value)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<TargetChild> for Target { | ||||
|     fn from(value: TargetChild) -> Self { | ||||
|         Self::Child(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Interned<TargetBase>> for Target { | ||||
|     fn from(value: Interned<TargetBase>) -> Self { | ||||
|         Self::Base(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Target { | ||||
|     pub fn base(&self) -> Interned<TargetBase> { | ||||
|         let mut target = self; | ||||
|         loop { | ||||
|             match target { | ||||
|                 Self::Base(v) => break *v, | ||||
|                 Self::Child(v) => target = &v.parent, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub fn child(&self) -> Option<TargetChild> { | ||||
|         match *self { | ||||
|             Target::Base(_) => None, | ||||
|             Target::Child(v) => Some(v), | ||||
|         } | ||||
|     } | ||||
|     pub fn is_static(&self) -> bool { | ||||
|         let mut target = self; | ||||
|         loop { | ||||
|             match target { | ||||
|                 Self::Base(_) => return true, | ||||
|                 Self::Child(v) if !v.path_element().is_static() => return false, | ||||
|                 Self::Child(v) => target = &v.parent, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     #[must_use] | ||||
|     pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self { | ||||
|         TargetChild::new(self.intern(), path_element).into() | ||||
|     } | ||||
|     pub fn flow(&self) -> Flow { | ||||
|         match self { | ||||
|             Self::Base(v) => v.flow(), | ||||
|             Self::Child(v) => v.flow(), | ||||
|         } | ||||
|     } | ||||
|     pub fn canonical_ty(&self) -> CanonicalType { | ||||
|         match self { | ||||
|             Target::Base(v) => v.canonical_ty(), | ||||
|             Target::Child(v) => v.canonical_ty(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for Target { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             Self::Base(v) => v.fmt(f), | ||||
|             Self::Child(v) => v.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for Target { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             Self::Base(v) => v.fmt(f), | ||||
|             Self::Child(v) => v.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -22,6 +22,179 @@ use std::{ | |||
| 
 | ||||
| pub mod type_map; | ||||
| 
 | ||||
| pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any { | ||||
|     fn get(&self) -> Interned<T>; | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any> | ||||
|     LazyInternedTrait<T> for F | ||||
| { | ||||
|     fn get(&self) -> Interned<T> { | ||||
|         self() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[repr(transparent)] | ||||
| pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>); | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> { | ||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | ||||
|         self.0.get_ptr_eq_with_type_id().hash(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> { | ||||
|     Interned(Interned<T>), | ||||
|     Lazy(LazyInternedFn<T>), | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> { | ||||
|     pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self { | ||||
|         Self::Lazy(LazyInternedFn(v)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> { | ||||
|     type Target = T; | ||||
| 
 | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         Interned::into_inner(self.interned()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {} | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T> | ||||
| where | ||||
|     Interned<T>: PartialEq, | ||||
| { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.interned() == other.interned() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T> | ||||
| where | ||||
|     Interned<T>: Ord, | ||||
| { | ||||
|     fn cmp(&self, other: &Self) -> Ordering { | ||||
|         self.interned().cmp(&other.interned()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T> | ||||
| where | ||||
|     Interned<T>: PartialOrd, | ||||
| { | ||||
|     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||||
|         self.interned().partial_cmp(&other.interned()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T> | ||||
| where | ||||
|     Interned<T>: Hash, | ||||
| { | ||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | ||||
|         self.interned().hash(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> { | ||||
|     pub fn interned(self) -> Interned<T> | ||||
|     where | ||||
|         T: Intern, | ||||
|     { | ||||
|         struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>); | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> { | ||||
|             fn hash<H: Hasher>(&self, _state: &mut H) {} | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> { | ||||
|             fn eq(&self, _other: &Self) -> bool { | ||||
|                 true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {} | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> { | ||||
|             fn clone(&self) -> Self { | ||||
|                 *self | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {} | ||||
| 
 | ||||
|         impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> { | ||||
|             type InputRef<'a> = LazyInternedFn<T>; | ||||
| 
 | ||||
|             type InputOwned = LazyInternedFn<T>; | ||||
| 
 | ||||
|             type InputCow<'a> = LazyInternedFn<T>; | ||||
| 
 | ||||
|             type Output = Interned<T>; | ||||
| 
 | ||||
|             fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { | ||||
|                 a == b | ||||
|             } | ||||
| 
 | ||||
|             fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { | ||||
|                 *input | ||||
|             } | ||||
| 
 | ||||
|             fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { | ||||
|                 input | ||||
|             } | ||||
| 
 | ||||
|             fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { | ||||
|                 *input | ||||
|             } | ||||
| 
 | ||||
|             fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { | ||||
|                 input | ||||
|             } | ||||
| 
 | ||||
|             fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { | ||||
|                 input | ||||
|             } | ||||
| 
 | ||||
|             fn inner(self, input: Self::InputRef<'_>) -> Self::Output { | ||||
|                 input.0.get() | ||||
|             } | ||||
|         } | ||||
|         match self { | ||||
|             Self::Interned(retval) => retval, | ||||
|             Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait InternedCompare { | ||||
|     type InternedCompareKey: Ord + Hash; | ||||
|     fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; | ||||
|  | @ -471,6 +644,12 @@ pub struct Interned<T: ?Sized + 'static + Send + Sync, C: InternContext = Global | |||
| 
 | ||||
| macro_rules! forward_fmt_trait { | ||||
|     ($Tr:ident) => { | ||||
|         impl<T: ?Sized + Intern + fmt::$Tr> fmt::$Tr for LazyInterned<T> { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 T::fmt(&**self, f) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr | ||||
|             for Interned<T, C> | ||||
|         { | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ extern crate self as fayalite; | |||
| pub use std as __std; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| #[doc(alias = "hdl")] | ||||
| /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | ||||
| /// a [`Module`][`::fayalite::module::Module`] when called.
 | ||||
| /// In the function body it will implicitly create a
 | ||||
|  | @ -21,25 +20,146 @@ pub use std as __std; | |||
| /// See [Fayalite Modules][crate::_docs::modules]
 | ||||
| pub use fayalite_proc_macros::hdl_module; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use fayalite_proc_macros::hdl; | ||||
| 
 | ||||
| /// struct used as a placeholder when applying defaults
 | ||||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||
| pub struct __; | ||||
| 
 | ||||
| #[cfg(feature = "unstable-doc")] | ||||
| pub mod _docs; | ||||
| 
 | ||||
| // FIXME: finish
 | ||||
| pub mod annotations; | ||||
| pub mod array; | ||||
| pub mod bundle; | ||||
| pub mod cli; | ||||
| //pub mod cli;
 | ||||
| pub mod clock; | ||||
| pub mod enum_; | ||||
| pub mod expr; | ||||
| pub mod firrtl; | ||||
| //pub mod firrtl;
 | ||||
| pub mod int; | ||||
| pub mod intern; | ||||
| pub mod memory; | ||||
| pub mod module; | ||||
| //pub mod module;
 | ||||
| pub mod reg; | ||||
| pub mod reset; | ||||
| pub mod source_location; | ||||
| pub mod ty; | ||||
| pub mod util; | ||||
| pub mod valueless; | ||||
| //pub mod valueless;
 | ||||
| pub mod wire; | ||||
| 
 | ||||
| // FIXME: switch to real module mod
 | ||||
| pub mod module { | ||||
|     use crate::{ | ||||
|         bundle::{Bundle, BundleType}, | ||||
|         enum_::{EnumMatchVariantsIter, EnumType}, | ||||
|         expr::{ops::VariantAccess, Expr, Flow}, | ||||
|         intern::Interned, | ||||
|         memory::PortName, | ||||
|         source_location::SourceLocation, | ||||
|         ty::{CanonicalType, Type}, | ||||
|     }; | ||||
|     use std::marker::PhantomData; | ||||
| 
 | ||||
|     pub struct NormalModule; | ||||
| 
 | ||||
|     pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>); | ||||
| 
 | ||||
|     impl ModuleBuilder<NormalModule> { | ||||
|         pub fn enum_match_variants_helper<Enum: EnumType>( | ||||
|             &mut self, | ||||
|             _base: Expr<Enum>, | ||||
|             _source_location: SourceLocation, | ||||
|         ) -> EnumMatchVariantsIter<Enum> { | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub struct Scope(()); | ||||
| 
 | ||||
|     #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
|     pub struct NameId(pub Interned<str>, pub usize); | ||||
| 
 | ||||
|     #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
|     pub struct ScopedNameId(pub NameId, pub NameId); | ||||
| 
 | ||||
|     #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
|     pub struct TargetName(pub ScopedNameId, pub Option<PortName>); | ||||
| 
 | ||||
|     #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
|     pub struct Instance<T: BundleType>(T); | ||||
| 
 | ||||
|     impl<T: BundleType> Instance<T> { | ||||
|         pub fn scoped_name(&self) -> ScopedNameId { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn ty(&self) -> T { | ||||
|             self.0 | ||||
|         } | ||||
|         pub fn must_connect_to(&self) -> bool { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn flow(&self) -> Flow { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn source_location(&self) -> SourceLocation { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn canonical(&self) -> Instance<Bundle> { | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
|     pub struct ModuleIO<T: Type>(T); | ||||
| 
 | ||||
|     impl<T: Type> ModuleIO<T> { | ||||
|         pub fn scoped_name(&self) -> ScopedNameId { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn ty(&self) -> T { | ||||
|             self.0 | ||||
|         } | ||||
|         pub fn must_connect_to(&self) -> bool { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn flow(&self) -> Flow { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn source_location(&self) -> SourceLocation { | ||||
|             todo!() | ||||
|         } | ||||
|         pub fn canonical(&self) -> ModuleIO<CanonicalType> { | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> { | ||||
|         variant_access: Interned<VariantAccess>, | ||||
|         _phantom: PhantomData<T>, | ||||
|     } | ||||
| 
 | ||||
|     impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> { | ||||
|         pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) { | ||||
|             (self.variant_access, Scope(())) | ||||
|         } | ||||
|         pub(crate) fn variant_access(&self) -> Interned<VariantAccess> { | ||||
|             self.variant_access | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[derive(Clone)] | ||||
|     pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>); | ||||
| 
 | ||||
|     impl<T: EnumType> EnumMatchVariantsIterImpl<T> { | ||||
|         pub(crate) fn for_variant_index( | ||||
|             &self, | ||||
|             _variant_index: usize, | ||||
|         ) -> EnumMatchVariantAndInactiveScopeImpl<T> { | ||||
|             todo!() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -4,15 +4,16 @@ | |||
| #![allow(clippy::multiple_bound_locations)] | ||||
| use crate::{ | ||||
|     annotations::{Annotation, IntoAnnotations, TargetedAnnotation}, | ||||
|     array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, | ||||
|     bundle::{BundleType, BundleValue, DynBundle, DynBundleType}, | ||||
|     clock::{Clock, ClockType}, | ||||
|     expr::{Expr, ExprEnum, ExprTrait, Flow, ToExpr}, | ||||
|     int::{DynUInt, DynUIntType, UInt, UIntType}, | ||||
|     array::{Array, ArrayType}, | ||||
|     bundle::{Bundle, BundleType}, | ||||
|     clock::Clock, | ||||
|     expr::{Expr, Flow, ToExpr, ToLiteralBits}, | ||||
|     hdl, | ||||
|     int::{Bool, DynSize, Size, UInt, UIntType}, | ||||
|     intern::{Intern, Interned}, | ||||
|     module::ScopedNameId, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value}, | ||||
|     ty::{AsMask, CanonicalType, Type}, | ||||
|     util::DebugAsDisplay, | ||||
| }; | ||||
| use bitvec::slice::BitSlice; | ||||
|  | @ -20,37 +21,37 @@ use std::{ | |||
|     cell::RefCell, | ||||
|     fmt, | ||||
|     hash::{Hash, Hasher}, | ||||
|     marker::PhantomData, | ||||
|     num::NonZeroU32, | ||||
|     rc::Rc, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct ReadStruct<Element> { | ||||
|     pub addr: DynUInt, | ||||
|     pub en: UInt<1>, | ||||
| #[hdl] | ||||
| pub struct ReadStruct<Element, AddrWidth: Size> { | ||||
|     pub addr: UIntType<AddrWidth>, | ||||
|     pub en: Bool, | ||||
|     pub clk: Clock, | ||||
|     #[hdl(flip)] | ||||
|     pub data: Element, | ||||
| } | ||||
| 
 | ||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct WriteStruct<Element: Value> { | ||||
|     pub addr: DynUInt, | ||||
|     pub en: UInt<1>, | ||||
| #[hdl] | ||||
| pub struct WriteStruct<Element, AddrWidth: Size> { | ||||
|     pub addr: UIntType<AddrWidth>, | ||||
|     pub en: Bool, | ||||
|     pub clk: Clock, | ||||
|     pub data: Element, | ||||
|     pub mask: AsMask<Element>, | ||||
| } | ||||
| 
 | ||||
| #[allow(clippy::multiple_bound_locations)] | ||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct ReadWriteStruct<Element: Value> { | ||||
|     pub addr: DynUInt, | ||||
|     pub en: UInt<1>, | ||||
| #[hdl] | ||||
| pub struct ReadWriteStruct<Element, AddrWidth: Size> { | ||||
|     pub addr: UIntType<AddrWidth>, | ||||
|     pub en: Bool, | ||||
|     pub clk: Clock, | ||||
|     #[hdl(flip)] | ||||
|     pub rdata: Element, | ||||
|     pub wmode: UInt<1>, | ||||
|     pub wmode: Bool, | ||||
|     pub wdata: Element, | ||||
|     pub wmask: AsMask<Element>, | ||||
| } | ||||
|  | @ -63,11 +64,10 @@ pub trait PortType: | |||
|     sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static | ||||
| { | ||||
|     type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static; | ||||
|     type PortType: BundleType<Value = Self::PortValue>; | ||||
|     type PortValue: BundleValue<Type = Self::PortType>; | ||||
|     type Port: BundleType; | ||||
|     fn port_kind(port_kind: Self::PortKindTy) -> PortKind; | ||||
|     fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy; | ||||
|     fn port_ty(port: &MemPort<Self>) -> Self::PortType; | ||||
|     fn port_ty(port: &MemPort<Self>) -> Self::Port; | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
|  | @ -79,8 +79,7 @@ impl sealed::Sealed for DynPortType {} | |||
| 
 | ||||
| impl PortType for DynPortType { | ||||
|     type PortKindTy = PortKind; | ||||
|     type PortType = DynBundleType; | ||||
|     type PortValue = DynBundle; | ||||
|     type Port = Bundle; | ||||
| 
 | ||||
|     fn port_kind(port_kind: Self::PortKindTy) -> PortKind { | ||||
|         port_kind | ||||
|  | @ -90,41 +89,38 @@ impl PortType for DynPortType { | |||
|         port_kind | ||||
|     } | ||||
| 
 | ||||
|     fn port_ty(port: &MemPort<Self>) -> Self::PortType { | ||||
|         match port.port_kind { | ||||
|             PortKind::ReadOnly => MemPort::<ReadStruct<DynCanonicalValue>>::from_canonical(*port) | ||||
|                 .ty() | ||||
|                 .canonical(), | ||||
|             PortKind::WriteOnly => MemPort::<WriteStruct<DynCanonicalValue>>::from_canonical(*port) | ||||
|                 .ty() | ||||
|                 .canonical(), | ||||
|             PortKind::ReadWrite => { | ||||
|                 MemPort::<ReadWriteStruct<DynCanonicalValue>>::from_canonical(*port) | ||||
|     fn port_ty(port: &MemPort<Self>) -> Self::Port { | ||||
|         Bundle::new(match port.port_kind { | ||||
|             PortKind::ReadOnly => { | ||||
|                 MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||
|                     .ty() | ||||
|                     .canonical() | ||||
|                     .fields() | ||||
|             } | ||||
|         } | ||||
|             PortKind::WriteOnly => { | ||||
|                 MemPort::<WriteStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||
|                     .ty() | ||||
|                     .fields() | ||||
|             } | ||||
|             PortKind::ReadWrite => { | ||||
|                 MemPort::<ReadWriteStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||
|                     .ty() | ||||
|                     .fields() | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait PortStruct: | ||||
|     BundleValue | ||||
|     + sealed::Sealed | ||||
|     + PortType<PortKindTy = (), PortType = <Self as ToExpr>::Type, PortValue = Self> | ||||
| where | ||||
|     Self::Type: BundleType<Value = Self>, | ||||
| { | ||||
|     type Element: Value<Type = Self::ElementType>; | ||||
|     type ElementType: Type<Value = Self::Element>; | ||||
| pub trait PortStruct: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> { | ||||
|     type Element: Type; | ||||
|     const PORT_KIND: PortKind; | ||||
| 
 | ||||
|     fn addr(this: Expr<Self>) -> Expr<DynUInt>; | ||||
|     fn en(this: Expr<Self>) -> Expr<UInt<1>>; | ||||
|     fn addr(this: Expr<Self>) -> Expr<UInt>; | ||||
|     fn en(this: Expr<Self>) -> Expr<Bool>; | ||||
|     fn clk(this: Expr<Self>) -> Expr<Clock>; | ||||
|     fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; | ||||
|     fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; | ||||
|     fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>>; | ||||
|     fn wmode(this: Expr<Self>) -> Expr<UInt<1>>; | ||||
|     fn wmode(this: Expr<Self>) -> Expr<Bool>; | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_port_struct { | ||||
|  | @ -137,19 +133,11 @@ macro_rules! impl_port_struct { | |||
|             $($body:tt)* | ||||
|         } | ||||
|     ) => { | ||||
|         impl<$Element: Value> sealed::Sealed for $Struct<$Element> | ||||
|         where | ||||
|             $Element::Type: Type<Value = $Element>, | ||||
|         { | ||||
|         } | ||||
|         impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {} | ||||
| 
 | ||||
|         impl<$Element: Value> PortType for $Struct<$Element> | ||||
|         where | ||||
|             $Element::Type: Type<Value = $Element>, | ||||
|         { | ||||
|         impl<$Element: Type> PortType for $Struct<$Element, DynSize> { | ||||
|             type PortKindTy = (); | ||||
|             type PortType = <Self as ToExpr>::Type; | ||||
|             type PortValue = Self; | ||||
|             type Port = Self; | ||||
| 
 | ||||
|             fn port_kind(_port_kind: Self::PortKindTy) -> PortKind { | ||||
|                 Self::PORT_KIND | ||||
|  | @ -164,18 +152,14 @@ macro_rules! impl_port_struct { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<$Element: Value> PortStruct for $Struct<$Element> | ||||
|         where | ||||
|             $Element::Type: Type<Value = $Element>, | ||||
|         { | ||||
|         impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> { | ||||
|             type Element = $Element; | ||||
|             type ElementType = $Element::Type; | ||||
| 
 | ||||
|             fn addr(this: Expr<Self>) -> Expr<DynUInt> { | ||||
|             fn addr(this: Expr<Self>) -> Expr<UInt> { | ||||
|                 this.addr | ||||
|             } | ||||
| 
 | ||||
|             fn en(this: Expr<Self>) -> Expr<UInt<1>> { | ||||
|             fn en(this: Expr<Self>) -> Expr<Bool> { | ||||
|                 this.en | ||||
|             } | ||||
| 
 | ||||
|  | @ -190,14 +174,9 @@ macro_rules! impl_port_struct { | |||
| 
 | ||||
| impl_port_struct! { | ||||
|     impl<Element> _ for ReadStruct { | ||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { | ||||
|             type T<V> = <V as ToExpr>::Type; | ||||
|             T::<Self> { | ||||
|                 addr: port.addr_type, | ||||
|                 en: UIntType::new(), | ||||
|                 clk: ClockType, | ||||
|                 data: Element::Type::from_dyn_canonical_type(port.mem_element_type), | ||||
|             } | ||||
|         fn port_ty(port: &MemPort<Self>) -> Self { | ||||
|             let element = Element::from_canonical(port.mem_element_type); | ||||
|             ReadStruct[element][port.addr_type.width()] | ||||
|         } | ||||
| 
 | ||||
|         const PORT_KIND: PortKind = PortKind::ReadOnly; | ||||
|  | @ -214,7 +193,7 @@ impl_port_struct! { | |||
|             None | ||||
|         } | ||||
| 
 | ||||
|         fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { | ||||
|         fn wmode(_this: Expr<Self>) -> Expr<Bool> { | ||||
|             false.to_expr() | ||||
|         } | ||||
|     } | ||||
|  | @ -222,16 +201,9 @@ impl_port_struct! { | |||
| 
 | ||||
| impl_port_struct! { | ||||
|     impl<Element> _ for WriteStruct { | ||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { | ||||
|             type T<V> = <V as ToExpr>::Type; | ||||
|             let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); | ||||
|             T::<Self> { | ||||
|                 addr: port.addr_type, | ||||
|                 en: UIntType::new(), | ||||
|                 clk: ClockType, | ||||
|                 mask: element_ty.mask_type(), | ||||
|                 data: element_ty, | ||||
|             } | ||||
|         fn port_ty(port: &MemPort<Self>) -> Self { | ||||
|             let element = Element::from_canonical(port.mem_element_type); | ||||
|             WriteStruct[element][port.addr_type.width()] | ||||
|         } | ||||
| 
 | ||||
|         const PORT_KIND: PortKind = PortKind::WriteOnly; | ||||
|  | @ -248,7 +220,7 @@ impl_port_struct! { | |||
|             Some(this.mask) | ||||
|         } | ||||
| 
 | ||||
|         fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { | ||||
|         fn wmode(_this: Expr<Self>) -> Expr<Bool> { | ||||
|             true.to_expr() | ||||
|         } | ||||
|     } | ||||
|  | @ -256,18 +228,9 @@ impl_port_struct! { | |||
| 
 | ||||
| impl_port_struct! { | ||||
|     impl<Element> _ for ReadWriteStruct { | ||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { | ||||
|             type T<V> = <V as ToExpr>::Type; | ||||
|             let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); | ||||
|             T::<Self> { | ||||
|                 addr: port.addr_type, | ||||
|                 en: UIntType::new(), | ||||
|                 clk: ClockType, | ||||
|                 rdata: element_ty.clone(), | ||||
|                 wmode: UIntType::new(), | ||||
|                 wmask: element_ty.mask_type(), | ||||
|                 wdata: element_ty, | ||||
|             } | ||||
|         fn port_ty(port: &MemPort<Self>) -> Self { | ||||
|             let element = Element::from_canonical(port.mem_element_type); | ||||
|             ReadWriteStruct[element][port.addr_type.width()] | ||||
|         } | ||||
| 
 | ||||
|         const PORT_KIND: PortKind = PortKind::ReadWrite; | ||||
|  | @ -284,7 +247,7 @@ impl_port_struct! { | |||
|             Some(this.wmask) | ||||
|         } | ||||
| 
 | ||||
|         fn wmode(this: Expr<Self>) -> Expr<UInt<1>> { | ||||
|         fn wmode(this: Expr<Self>) -> Expr<Bool> { | ||||
|             this.wmode | ||||
|         } | ||||
|     } | ||||
|  | @ -382,8 +345,8 @@ pub struct MemPort<T: PortType> { | |||
|     source_location: SourceLocation, | ||||
|     port_kind: T::PortKindTy, | ||||
|     port_index: usize, | ||||
|     addr_type: DynUIntType, | ||||
|     mem_element_type: Interned<dyn DynCanonicalType>, | ||||
|     addr_type: UInt, | ||||
|     mem_element_type: CanonicalType, | ||||
| } | ||||
| 
 | ||||
| impl<T: PortType> fmt::Debug for MemPort<T> { | ||||
|  | @ -405,19 +368,10 @@ impl<T: PortType> fmt::Debug for MemPort<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PortType> ToExpr for MemPort<T> { | ||||
|     type Type = T::PortType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
| impl<T: PortType> MemPort<T> { | ||||
|     pub fn ty(&self) -> T::Port { | ||||
|         T::port_ty(self) | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         Expr::new_unchecked(self.expr_enum()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: PortType> MemPort<T> { | ||||
|     pub fn source_location(&self) -> SourceLocation { | ||||
|         self.source_location | ||||
|     } | ||||
|  | @ -436,10 +390,10 @@ impl<T: PortType> MemPort<T> { | |||
|             index: self.port_index, | ||||
|         } | ||||
|     } | ||||
|     pub fn mem_element_type(&self) -> Interned<dyn DynCanonicalType> { | ||||
|     pub fn mem_element_type(&self) -> CanonicalType { | ||||
|         self.mem_element_type | ||||
|     } | ||||
|     pub fn addr_type(&self) -> DynUIntType { | ||||
|     pub fn addr_type(&self) -> UInt { | ||||
|         self.addr_type | ||||
|     } | ||||
|     pub fn canonical(&self) -> MemPort<DynPortType> { | ||||
|  | @ -463,7 +417,6 @@ impl<T: PortType> MemPort<T> { | |||
|     pub fn from_canonical(port: MemPort<DynPortType>) -> Self | ||||
|     where | ||||
|         T: PortStruct, | ||||
|         T::Type: BundleType<Value = T>, | ||||
|     { | ||||
|         let MemPort { | ||||
|             mem_name, | ||||
|  | @ -488,8 +441,8 @@ impl<T: PortType> MemPort<T> { | |||
|         mem_name: ScopedNameId, | ||||
|         source_location: SourceLocation, | ||||
|         port_name: PortName, | ||||
|         addr_type: DynUIntType, | ||||
|         mem_element_type: Interned<dyn DynCanonicalType>, | ||||
|         addr_type: UInt, | ||||
|         mem_element_type: CanonicalType, | ||||
|     ) -> Self { | ||||
|         assert!( | ||||
|             mem_element_type.is_storable(), | ||||
|  | @ -520,10 +473,10 @@ pub enum ReadUnderWrite { | |||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
| struct MemImpl<T: ArrayTypeTrait, P> { | ||||
| struct MemImpl<Element: Type, Len: Size, P> { | ||||
|     scoped_name: ScopedNameId, | ||||
|     source_location: SourceLocation, | ||||
|     array_type: T, | ||||
|     array_type: ArrayType<Element, Len>, | ||||
|     initial_value: Option<Interned<BitSlice>>, | ||||
|     ports: P, | ||||
|     read_latency: usize, | ||||
|  | @ -533,11 +486,11 @@ struct MemImpl<T: ArrayTypeTrait, P> { | |||
|     mem_annotations: Interned<[Annotation]>, | ||||
| } | ||||
| 
 | ||||
| pub struct Mem<VA: ValueArrayOrSlice + ?Sized>( | ||||
|     Interned<MemImpl<ArrayType<VA>, Interned<[Interned<MemPort<DynPortType>>]>>>, | ||||
| pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>( | ||||
|     Interned<MemImpl<Element, Len, Interned<[MemPort<DynPortType>]>>>, | ||||
| ); | ||||
| 
 | ||||
| struct PortsDebug<'a>(&'a [Interned<MemPort<DynPortType>>]); | ||||
| struct PortsDebug<'a>(&'a [MemPort<DynPortType>]); | ||||
| 
 | ||||
| impl fmt::Debug for PortsDebug<'_> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|  | @ -551,7 +504,7 @@ impl fmt::Debug for PortsDebug<'_> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> { | ||||
| impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let MemImpl { | ||||
|             scoped_name, | ||||
|  | @ -579,37 +532,37 @@ impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Hash for Mem<VA> { | ||||
| impl<Element: Type, Len: Size> Hash for Mem<Element, Len> { | ||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | ||||
|         self.0.hash(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Eq for Mem<VA> {} | ||||
| impl<Element: Type, Len: Size> Eq for Mem<Element, Len> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> PartialEq for Mem<VA> { | ||||
| impl<Element: Type, Len: Size> PartialEq for Mem<Element, Len> { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.0 == other.0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Copy for Mem<VA> {} | ||||
| impl<Element: Type, Len: Size> Copy for Mem<Element, Len> {} | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for Mem<VA> { | ||||
| impl<Element: Type, Len: Size> Clone for Mem<Element, Len> { | ||||
|     fn clone(&self) -> Self { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||
| impl<Element: Type, Len: Size> Mem<Element, Len> { | ||||
|     #[allow(clippy::too_many_arguments)] | ||||
|     #[track_caller] | ||||
|     pub fn new_unchecked( | ||||
|         scoped_name: ScopedNameId, | ||||
|         source_location: SourceLocation, | ||||
|         array_type: ArrayType<VA>, | ||||
|         array_type: ArrayType<Element, Len>, | ||||
|         initial_value: Option<Interned<BitSlice>>, | ||||
|         ports: Interned<[Interned<MemPort<DynPortType>>]>, | ||||
|         ports: Interned<[MemPort<DynPortType>]>, | ||||
|         read_latency: usize, | ||||
|         write_latency: NonZeroU32, | ||||
|         read_under_write: ReadUnderWrite, | ||||
|  | @ -617,14 +570,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|         mem_annotations: Interned<[Annotation]>, | ||||
|     ) -> Self { | ||||
|         if let Some(initial_value) = initial_value { | ||||
|             MemBuilder::<VA>::check_initial_value_bit_slice( | ||||
|             MemBuilder::<Element, Len>::check_initial_value_bit_slice( | ||||
|                 array_type.element(), | ||||
|                 Some(array_type.len()), | ||||
|                 initial_value, | ||||
|             ); | ||||
|         } | ||||
|         let addr_width = memory_addr_width(array_type.len()); | ||||
|         let expected_mem_element_type = array_type.element().canonical_dyn(); | ||||
|         let expected_mem_element_type = array_type.element().canonical(); | ||||
|         assert!( | ||||
|             expected_mem_element_type.is_storable(), | ||||
|             "memory element type must be a storable type" | ||||
|  | @ -637,7 +590,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|                 port_index, | ||||
|                 addr_type, | ||||
|                 mem_element_type, | ||||
|             } = **port; | ||||
|             } = *port; | ||||
|             assert_eq!(mem_name, scoped_name, "memory name must match with ports"); | ||||
|             assert_eq!( | ||||
|                 port_index, index, | ||||
|  | @ -659,7 +612,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|             }; | ||||
|             assert_eq!( | ||||
|                 Some(port), | ||||
|                 ports.get(port.port_index).map(|v| &**v), | ||||
|                 ports.get(port.port_index), | ||||
|                 "port on memory must match annotation's target base" | ||||
|             ); | ||||
|         } | ||||
|  | @ -682,13 +635,13 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|     pub fn source_location(self) -> SourceLocation { | ||||
|         self.0.source_location | ||||
|     } | ||||
|     pub fn array_type(self) -> ArrayType<VA> { | ||||
|     pub fn array_type(self) -> ArrayType<Element, Len> { | ||||
|         self.0.array_type.clone() | ||||
|     } | ||||
|     pub fn initial_value(self) -> Option<Interned<BitSlice>> { | ||||
|         self.0.initial_value | ||||
|     } | ||||
|     pub fn ports(self) -> Interned<[Interned<MemPort<DynPortType>>]> { | ||||
|     pub fn ports(self) -> Interned<[MemPort<DynPortType>]> { | ||||
|         self.0.ports | ||||
|     } | ||||
|     pub fn read_latency(self) -> usize { | ||||
|  | @ -706,7 +659,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|     pub fn mem_annotations(self) -> Interned<[Annotation]> { | ||||
|         self.0.mem_annotations | ||||
|     } | ||||
|     pub fn canonical(self) -> Mem<[DynCanonicalValue]> { | ||||
|     pub fn canonical(self) -> Mem { | ||||
|         let MemImpl { | ||||
|             scoped_name, | ||||
|             source_location, | ||||
|  | @ -719,7 +672,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | |||
|             port_annotations, | ||||
|             mem_annotations, | ||||
|         } = *self.0; | ||||
|         let array_type = array_type.canonical(); | ||||
|         let array_type = array_type.as_dyn_array(); | ||||
|         Mem(Intern::intern_sized(MemImpl { | ||||
|             scoped_name, | ||||
|             source_location, | ||||
|  | @ -751,10 +704,10 @@ impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> { | |||
| pub(crate) struct MemBuilderTarget { | ||||
|     pub(crate) scoped_name: ScopedNameId, | ||||
|     pub(crate) source_location: SourceLocation, | ||||
|     pub(crate) mem_element_type: Interned<dyn DynCanonicalType>, | ||||
|     pub(crate) mem_element_type: CanonicalType, | ||||
|     pub(crate) depth: Option<usize>, | ||||
|     pub(crate) initial_value: Option<Interned<BitSlice>>, | ||||
|     pub(crate) ports: Vec<Interned<MemPort<DynPortType>>>, | ||||
|     pub(crate) ports: Vec<MemPort<DynPortType>>, | ||||
|     pub(crate) read_latency: usize, | ||||
|     pub(crate) write_latency: NonZeroU32, | ||||
|     pub(crate) read_under_write: ReadUnderWrite, | ||||
|  | @ -763,11 +716,11 @@ pub(crate) struct MemBuilderTarget { | |||
| } | ||||
| 
 | ||||
| impl MemBuilderTarget { | ||||
|     pub(crate) fn make_memory(&self) -> Option<Mem<[DynCanonicalValue]>> { | ||||
|     pub(crate) fn make_memory(&self) -> Option<Mem> { | ||||
|         Some(Mem::new_unchecked( | ||||
|             self.scoped_name, | ||||
|             self.source_location, | ||||
|             ArrayType::new_slice(self.mem_element_type, self.depth?), | ||||
|             ArrayType::new_dyn(self.mem_element_type, self.depth?), | ||||
|             self.initial_value, | ||||
|             Intern::intern(&self.ports), | ||||
|             self.read_latency, | ||||
|  | @ -817,16 +770,18 @@ impl fmt::Debug for MemBuilderTarget { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct MemBuilder<VA: ValueArrayOrSlice + ?Sized> { | ||||
|     mem_element_type: VA::ElementType, | ||||
| pub struct MemBuilder<Element: Type, Len: Size> { | ||||
|     mem_element_type: Element, | ||||
|     target: Rc<RefCell<MemBuilderTarget>>, | ||||
|     _phantom: PhantomData<Len>, | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for MemBuilder<VA> { | ||||
| impl<Element: Type, Len: Size> fmt::Debug for MemBuilder<Element, Len> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let Self { | ||||
|             mem_element_type, | ||||
|             target, | ||||
|             _phantom: _, | ||||
|         } = &self; | ||||
|         target.borrow().debug_fmt("MemBuilder", mem_element_type, f) | ||||
|     } | ||||
|  | @ -838,15 +793,16 @@ pub fn memory_addr_width(depth: usize) -> usize { | |||
|         .map_or(usize::BITS, usize::ilog2) as usize | ||||
| } | ||||
| 
 | ||||
| impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||
| impl<Element: Type, Len: Size> MemBuilder<Element, Len> { | ||||
|     #[track_caller] | ||||
|     fn check_initial_value_bit_slice( | ||||
|         mem_element_type: &VA::ElementType, | ||||
|         mem_element_type: Element, | ||||
|         depth: Option<usize>, | ||||
|         initial_value: Interned<BitSlice>, | ||||
|     ) -> Interned<BitSlice> { | ||||
|         let element_bit_width = mem_element_type.canonical().bit_width(); | ||||
|         if let Some(depth) = depth { | ||||
|             let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect( | ||||
|             let expected_len = depth.checked_mul(element_bit_width).expect( | ||||
|                 "memory must be small enough that its initializer bit length fits in usize", | ||||
|             ); | ||||
|             assert_eq!( | ||||
|  | @ -858,7 +814,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|         assert!( | ||||
|             initial_value | ||||
|                 .len() | ||||
|                 .checked_rem(mem_element_type.bit_width()) | ||||
|                 .checked_rem(element_bit_width) | ||||
|                 .unwrap_or(initial_value.len()) | ||||
|                 == 0, | ||||
|             "Mem's initializer bit length must be a multiple of the element type's bit width", | ||||
|  | @ -867,14 +823,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|     } | ||||
|     #[track_caller] | ||||
|     fn check_initial_value_expr( | ||||
|         mem_element_type: &VA::ElementType, | ||||
|         mem_element_type: &Element, | ||||
|         depth: Option<usize>, | ||||
|         initial_value: Expr<Array<[DynCanonicalValue]>>, | ||||
|         initial_value: Expr<Array>, | ||||
|     ) -> Interned<BitSlice> { | ||||
|         let initial_value_ty = initial_value.canonical_type(); | ||||
|         let initial_value_ty = Expr::ty(initial_value); | ||||
|         assert_eq!( | ||||
|             *mem_element_type, | ||||
|             <VA::ElementType as DynType>::from_dyn_canonical_type(*initial_value_ty.element()), | ||||
|             Element::from_canonical(initial_value_ty.element()), | ||||
|             "Mem's element type must match initializer's element type", | ||||
|         ); | ||||
|         if let Some(depth) = depth { | ||||
|  | @ -889,7 +845,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|         }; | ||||
|         debug_assert_eq!( | ||||
|             retval.len(), | ||||
|             initial_value_ty.bit_width(), | ||||
|             initial_value_ty.type_properties().bit_width, | ||||
|             "initial value produced wrong literal bits length" | ||||
|         ); | ||||
|         retval | ||||
|  | @ -898,9 +854,9 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|     pub(crate) fn new( | ||||
|         scoped_name: ScopedNameId, | ||||
|         source_location: SourceLocation, | ||||
|         mem_element_type: VA::ElementType, | ||||
|         mem_element_type: Element, | ||||
|     ) -> (Self, Rc<RefCell<MemBuilderTarget>>) { | ||||
|         let canonical_mem_element_type = mem_element_type.canonical_dyn(); | ||||
|         let canonical_mem_element_type = mem_element_type.canonical(); | ||||
|         assert!( | ||||
|             canonical_mem_element_type.is_storable(), | ||||
|             "memory element type must be a storable type" | ||||
|  | @ -909,7 +865,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|             scoped_name, | ||||
|             source_location, | ||||
|             mem_element_type: canonical_mem_element_type, | ||||
|             depth: VA::FIXED_LEN_TYPE.map(VA::len_from_len_type), | ||||
|             depth: Len::KNOWN_VALUE, | ||||
|             initial_value: None, | ||||
|             ports: vec![], | ||||
|             read_latency: 0, | ||||
|  | @ -922,6 +878,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|             Self { | ||||
|                 mem_element_type, | ||||
|                 target: Rc::clone(&target), | ||||
|                 _phantom: PhantomData, | ||||
|             }, | ||||
|             target, | ||||
|         ) | ||||
|  | @ -931,19 +888,19 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|         &mut self, | ||||
|         source_location: SourceLocation, | ||||
|         port_kind: PortKind, | ||||
|     ) -> Interned<MemPort<DynPortType>> { | ||||
|     ) -> MemPort<DynPortType> { | ||||
|         let mut target = self.target.borrow_mut(); | ||||
|         let Some(depth) = target.depth else { | ||||
|             panic!("MemBuilder::depth must be called before adding ports"); | ||||
|         }; | ||||
|         let port = Intern::intern_sized(MemPort { | ||||
|         let port = MemPort { | ||||
|             mem_name: target.scoped_name, | ||||
|             source_location, | ||||
|             port_kind, | ||||
|             port_index: target.ports.len(), | ||||
|             addr_type: DynUIntType::new(memory_addr_width(depth)), | ||||
|             addr_type: UInt::new(memory_addr_width(depth)), | ||||
|             mem_element_type: target.mem_element_type, | ||||
|         }); | ||||
|         }; | ||||
|         target.ports.push(port); | ||||
|         port | ||||
|     } | ||||
|  | @ -952,50 +909,53 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|         &mut self, | ||||
|         source_location: SourceLocation, | ||||
|         kind: PortKind, | ||||
|     ) -> Expr<DynBundle> { | ||||
|         Expr::new_unchecked(ExprEnum::MemPort(self.new_port_impl(source_location, kind))) | ||||
|     ) -> Expr<Bundle> { | ||||
|         self.new_port_impl(source_location, kind).to_expr() | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_port(&mut self, kind: PortKind) -> Expr<DynBundle> { | ||||
|     pub fn new_port(&mut self, kind: PortKind) -> Expr<Bundle> { | ||||
|         self.new_port_with_loc(SourceLocation::caller(), kind) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_read_port_with_loc( | ||||
|         &mut self, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Expr<ReadStruct<VA::Element>> { | ||||
|         Expr::new_unchecked(ExprEnum::MemPort( | ||||
|             self.new_port_impl(source_location, PortKind::ReadOnly), | ||||
|         )) | ||||
|     ) -> Expr<ReadStruct<Element, DynSize>> { | ||||
|         Expr::from_bundle( | ||||
|             self.new_port_impl(source_location, PortKind::ReadOnly) | ||||
|                 .to_expr(), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_read_port(&mut self) -> Expr<ReadStruct<VA::Element>> { | ||||
|     pub fn new_read_port(&mut self) -> Expr<ReadStruct<Element, DynSize>> { | ||||
|         self.new_read_port_with_loc(SourceLocation::caller()) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_write_port_with_loc( | ||||
|         &mut self, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Expr<WriteStruct<VA::Element>> { | ||||
|         Expr::new_unchecked(ExprEnum::MemPort( | ||||
|             self.new_port_impl(source_location, PortKind::WriteOnly), | ||||
|         )) | ||||
|     ) -> Expr<WriteStruct<Element, DynSize>> { | ||||
|         Expr::from_bundle( | ||||
|             self.new_port_impl(source_location, PortKind::WriteOnly) | ||||
|                 .to_expr(), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_write_port(&mut self) -> Expr<WriteStruct<VA::Element>> { | ||||
|     pub fn new_write_port(&mut self) -> Expr<WriteStruct<Element, DynSize>> { | ||||
|         self.new_write_port_with_loc(SourceLocation::caller()) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_rw_port_with_loc( | ||||
|         &mut self, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Expr<ReadWriteStruct<VA::Element>> { | ||||
|         Expr::new_unchecked(ExprEnum::MemPort( | ||||
|             self.new_port_impl(source_location, PortKind::ReadWrite), | ||||
|         )) | ||||
|     ) -> Expr<ReadWriteStruct<Element, DynSize>> { | ||||
|         Expr::from_bundle( | ||||
|             self.new_port_impl(source_location, PortKind::ReadWrite) | ||||
|                 .to_expr(), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<VA::Element>> { | ||||
|     pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<Element, DynSize>> { | ||||
|         self.new_rw_port_with_loc(SourceLocation::caller()) | ||||
|     } | ||||
|     pub fn scoped_name(&self) -> ScopedNameId { | ||||
|  | @ -1004,7 +964,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|     pub fn source_location(&self) -> SourceLocation { | ||||
|         self.target.borrow().source_location | ||||
|     } | ||||
|     pub fn get_mem_element_type(&self) -> &VA::ElementType { | ||||
|     pub fn get_mem_element_type(&self) -> &Element { | ||||
|         &self.mem_element_type | ||||
|     } | ||||
|     #[allow(clippy::result_unit_err)] | ||||
|  | @ -1027,28 +987,28 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|         target.depth = Some(depth); | ||||
|     } | ||||
|     #[allow(clippy::result_unit_err)] | ||||
|     pub fn get_array_type(&self) -> Result<ArrayType<VA>, ()> { | ||||
|         Ok(ArrayType::new_with_len( | ||||
|     pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> { | ||||
|         Ok(ArrayType::new( | ||||
|             self.mem_element_type.clone(), | ||||
|             self.get_depth()?, | ||||
|             Len::from_usize(self.get_depth()?), | ||||
|         )) | ||||
|     } | ||||
|     pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> { | ||||
|         self.target.borrow().initial_value | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<VA>>) { | ||||
|     pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<Element, Len>>) { | ||||
|         let mut target = self.target.borrow_mut(); | ||||
|         if target.initial_value.is_some() { | ||||
|             panic!("can't set Mem's initial value more than once"); | ||||
|         } | ||||
|         let initial_value = initial_value.to_expr().canonical(); | ||||
|         let initial_value = Expr::as_dyn_array(initial_value.to_expr()); | ||||
|         target.initial_value = Some(Self::check_initial_value_expr( | ||||
|             &self.mem_element_type, | ||||
|             target.depth, | ||||
|             initial_value, | ||||
|         )); | ||||
|         target.depth = Some(initial_value.ty().len()); | ||||
|         target.depth = Some(Expr::ty(initial_value).len()); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) { | ||||
|  | @ -1057,11 +1017,11 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | |||
|             panic!("can't set Mem's initial value more than once"); | ||||
|         } | ||||
|         target.initial_value = Some(Self::check_initial_value_bit_slice( | ||||
|             &self.mem_element_type, | ||||
|             self.mem_element_type, | ||||
|             target.depth, | ||||
|             initial_value, | ||||
|         )); | ||||
|         let element_bit_width = self.mem_element_type.bit_width(); | ||||
|         let element_bit_width = self.mem_element_type.canonical().bit_width(); | ||||
|         if element_bit_width != 0 { | ||||
|             target.depth = Some(initial_value.len() / element_bit_width); | ||||
|         } | ||||
|  |  | |||
|  | @ -2,21 +2,21 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| use crate::{ | ||||
|     clock::ClockDomain, | ||||
|     expr::{Expr, ExprTrait, Flow, ToExpr}, | ||||
|     expr::{Expr, Flow}, | ||||
|     intern::Interned, | ||||
|     module::{NameId, ScopedNameId}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{DynCanonicalType, DynType, Type}, | ||||
|     ty::{CanonicalType, Type}, | ||||
| }; | ||||
| use std::fmt; | ||||
| 
 | ||||
| #[derive(Clone, Eq, PartialEq, Hash)] | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash)] | ||||
| pub struct Reg<T: Type> { | ||||
|     name: ScopedNameId, | ||||
|     source_location: SourceLocation, | ||||
|     ty: T, | ||||
|     clock_domain: Expr<ClockDomain>, | ||||
|     init: Option<Expr<T::Value>>, | ||||
|     init: Option<Expr<T>>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> { | ||||
|  | @ -37,20 +37,8 @@ impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToExpr for Reg<T> { | ||||
|     type Type = T; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         self.ty.clone() | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         Expr::new_unchecked(self.expr_enum()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> Reg<T> { | ||||
|     pub fn canonical(&self) -> Reg<T::CanonicalType> { | ||||
|     pub fn canonical(&self) -> Reg<CanonicalType> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|  | @ -66,49 +54,20 @@ impl<T: Type> Reg<T> { | |||
|             init: init.map(Expr::canonical), | ||||
|         } | ||||
|     } | ||||
|     pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|             ref ty, | ||||
|             clock_domain, | ||||
|             init, | ||||
|         } = *self; | ||||
|         Reg { | ||||
|             name, | ||||
|             source_location, | ||||
|             ty: ty.to_dyn(), | ||||
|             clock_domain, | ||||
|             init: init.map(Expr::to_dyn), | ||||
|         } | ||||
|     } | ||||
|     pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|             ref ty, | ||||
|             clock_domain, | ||||
|             init, | ||||
|         } = *self; | ||||
|         Reg { | ||||
|             name, | ||||
|             source_location, | ||||
|             ty: ty.canonical_dyn(), | ||||
|             clock_domain, | ||||
|             init: init.map(Expr::to_canonical_dyn), | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn new_unchecked( | ||||
|         scoped_name: ScopedNameId, | ||||
|         source_location: SourceLocation, | ||||
|         ty: T, | ||||
|         clock_domain: Expr<ClockDomain>, | ||||
|         init: Option<Expr<T::Value>>, | ||||
|         init: Option<Expr<T>>, | ||||
|     ) -> Self { | ||||
|         assert!(ty.is_storable(), "register type must be a storable type"); | ||||
|         assert!( | ||||
|             ty.canonical().is_storable(), | ||||
|             "register type must be a storable type" | ||||
|         ); | ||||
|         if let Some(init) = init { | ||||
|             assert_eq!(ty, init.ty(), "register's type must match init type"); | ||||
|             assert_eq!(ty, Expr::ty(init), "register's type must match init type"); | ||||
|         } | ||||
|         Self { | ||||
|             name: scoped_name, | ||||
|  | @ -118,6 +77,9 @@ impl<T: Type> Reg<T> { | |||
|             init, | ||||
|         } | ||||
|     } | ||||
|     pub fn ty(&self) -> T { | ||||
|         self.ty | ||||
|     } | ||||
|     pub fn source_location(&self) -> SourceLocation { | ||||
|         self.source_location | ||||
|     } | ||||
|  | @ -139,7 +101,7 @@ impl<T: Type> Reg<T> { | |||
|     pub fn clock_domain(&self) -> Expr<ClockDomain> { | ||||
|         self.clock_domain | ||||
|     } | ||||
|     pub fn init(&self) -> Option<Expr<T::Value>> { | ||||
|     pub fn init(&self) -> Option<Expr<T>> { | ||||
|         self.init | ||||
|     } | ||||
|     pub fn flow(&self) -> Flow { | ||||
|  |  | |||
|  | @ -2,358 +2,115 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| use crate::{ | ||||
|     expr::{Expr, ToExpr}, | ||||
|     int::{UInt, UIntType}, | ||||
|     intern::Interned, | ||||
|     int::Bool, | ||||
|     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; | ||||
| 
 | ||||
| pub trait ResetTypeTrait: CanonicalType + StaticType<MaskType = UIntType<1>> {} | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||
| pub struct AsyncResetType; | ||||
| 
 | ||||
| impl AsyncResetType { | ||||
|     pub const fn new() -> Self { | ||||
|         Self | ||||
|     } | ||||
| mod sealed { | ||||
|     pub trait ResetTypeSealed {} | ||||
| } | ||||
| 
 | ||||
| impl Type for AsyncResetType { | ||||
|     type Value = AsyncReset; | ||||
|     type CanonicalType = AsyncResetType; | ||||
|     type CanonicalValue = AsyncReset; | ||||
|     type MaskType = UIntType<1>; | ||||
|     type MaskValue = UInt<1>; | ||||
| pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {} | ||||
| 
 | ||||
|     impl_match_values_as_self!(); | ||||
| macro_rules! reset_type { | ||||
|     ($name:ident, $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal) => { | ||||
|         #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||
|         pub struct $name; | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         UIntType::new() | ||||
|     } | ||||
|         impl Type for $name { | ||||
|             type MaskType = Bool; | ||||
| 
 | ||||
|     fn canonical(&self) -> Self::CanonicalType { | ||||
|         *self | ||||
|     } | ||||
|             impl_match_variant_as_self!(); | ||||
| 
 | ||||
|     fn source_location(&self) -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|             fn mask_type(&self) -> Self::MaskType { | ||||
|                 Bool | ||||
|             } | ||||
| 
 | ||||
|     fn type_enum(&self) -> TypeEnum { | ||||
|         TypeEnum::AsyncReset(*self) | ||||
|     } | ||||
|             fn canonical(&self) -> CanonicalType { | ||||
|                 CanonicalType::$name(*self) | ||||
|             } | ||||
| 
 | ||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { | ||||
|         t | ||||
|     } | ||||
|             fn source_location() -> SourceLocation { | ||||
|                 SourceLocation::builtin() | ||||
|             } | ||||
| 
 | ||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { | ||||
|         Some(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Connect<Self> for AsyncResetType {} | ||||
| 
 | ||||
| impl CanonicalType for AsyncResetType { | ||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset; | ||||
| } | ||||
| 
 | ||||
| impl StaticType for AsyncResetType { | ||||
|     fn static_type() -> Self { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ResetTypeTrait for AsyncResetType {} | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||||
| pub struct AsyncReset(pub bool); | ||||
| 
 | ||||
| impl ToExpr for AsyncReset { | ||||
|     type Type = AsyncResetType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         AsyncResetType | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<Self> { | ||||
|         Expr::from_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Value for AsyncReset { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         *self | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalValue for AsyncReset { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::AsyncReset(*this) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||
| pub struct SyncResetType; | ||||
| 
 | ||||
| impl SyncResetType { | ||||
|     pub const fn new() -> Self { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Type for SyncResetType { | ||||
|     type CanonicalType = SyncResetType; | ||||
|     type Value = SyncReset; | ||||
|     type CanonicalValue = SyncReset; | ||||
|     type MaskType = UIntType<1>; | ||||
|     type MaskValue = UInt<1>; | ||||
| 
 | ||||
|     impl_match_values_as_self!(); | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         UIntType::new() | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> Self::CanonicalType { | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     fn source_location(&self) -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn type_enum(&self) -> TypeEnum { | ||||
|         TypeEnum::SyncReset(*self) | ||||
|     } | ||||
| 
 | ||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { | ||||
|         t | ||||
|     } | ||||
| 
 | ||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { | ||||
|         Some(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Connect<Self> for SyncResetType {} | ||||
| 
 | ||||
| impl CanonicalType for SyncResetType { | ||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset; | ||||
| } | ||||
| 
 | ||||
| impl StaticType for SyncResetType { | ||||
|     fn static_type() -> Self { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ResetTypeTrait for SyncResetType {} | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||
| pub struct SyncReset(pub bool); | ||||
| 
 | ||||
| impl ToExpr for SyncReset { | ||||
|     type Type = SyncResetType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         SyncResetType | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<Self> { | ||||
|         Expr::from_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Value for SyncReset { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         *self | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalValue for SyncReset { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::SyncReset(*this) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         interned_bit(this.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] | ||||
| pub struct ResetType; | ||||
| 
 | ||||
| impl ResetType { | ||||
|     pub const fn new() -> Self { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Type for ResetType { | ||||
|     type Value = Reset; | ||||
|     type CanonicalType = ResetType; | ||||
|     type CanonicalValue = Reset; | ||||
|     type MaskType = UIntType<1>; | ||||
|     type MaskValue = UInt<1>; | ||||
| 
 | ||||
|     impl_match_values_as_self!(); | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         UIntType::new() | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> Self::CanonicalType { | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     fn source_location(&self) -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn type_enum(&self) -> TypeEnum { | ||||
|         TypeEnum::Reset(*self) | ||||
|     } | ||||
| 
 | ||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { | ||||
|         t | ||||
|     } | ||||
| 
 | ||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { | ||||
|         Some(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Connect<Self> for ResetType {} | ||||
| 
 | ||||
| impl CanonicalType for ResetType { | ||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset; | ||||
| } | ||||
| 
 | ||||
| impl StaticType for ResetType { | ||||
|     fn static_type() -> Self { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ResetTypeTrait for ResetType {} | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] | ||||
| pub enum Reset {} | ||||
| 
 | ||||
| impl ToExpr for Reset { | ||||
|     type Type = ResetType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         match *self {} | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<Self> { | ||||
|         Expr::from_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Value for Reset { | ||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { | ||||
|         *self | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         match *this {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalValue for Reset { | ||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { | ||||
|         ValueEnum::Reset(*this) | ||||
|     } | ||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { | ||||
|         match *this {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! make_to_reset { | ||||
|     ( | ||||
|         $(#[from_value($from_value_ty:ty)])* | ||||
|         $vis:vis trait $Trait:ident { | ||||
|             fn $fn:ident(&self) -> Expr<$T:ty>; | ||||
|             fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|                 let CanonicalType::$name(retval) = canonical_type else { | ||||
|                     panic!("expected {}", stringify!($name)); | ||||
|                 }; | ||||
|                 retval | ||||
|             } | ||||
|         } | ||||
|     ) => { | ||||
|         $vis trait $Trait { | ||||
|             fn $fn(&self) -> Expr<$T>; | ||||
| 
 | ||||
|         impl $name { | ||||
|             pub fn type_properties(self) -> TypeProperties { | ||||
|                 Self::TYPE_PROPERTIES | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl StaticType for $name { | ||||
|             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: $is_castable_from_bits, | ||||
|                 bit_width: 1, | ||||
|             }; | ||||
|             const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; | ||||
|         } | ||||
| 
 | ||||
|         impl sealed::ResetTypeSealed for $name {} | ||||
| 
 | ||||
|         impl ResetType for $name {} | ||||
| 
 | ||||
|         pub trait $Trait { | ||||
|             fn $trait_fn(&self) -> Expr<$name>; | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + $Trait> $Trait for &'_ T { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|                 (**self).$fn() | ||||
|             fn $trait_fn(&self) -> Expr<$name> { | ||||
|                 (**self).$trait_fn() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + $Trait> $Trait for &'_ mut T { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|                 (**self).$fn() | ||||
|             fn $trait_fn(&self) -> Expr<$name> { | ||||
|                 (**self).$trait_fn() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<T: ?Sized + $Trait> $Trait for Box<T> { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|                 (**self).$fn() | ||||
|             fn $trait_fn(&self) -> Expr<$name> { | ||||
|                 (**self).$trait_fn() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl $Trait for Expr<$T> { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|         impl $Trait for Expr<$name> { | ||||
|             fn $trait_fn(&self) -> Expr<$name> { | ||||
|                 *self | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl $Trait for $T { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|                 self.to_expr() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $(impl $Trait for $from_value_ty { | ||||
|             fn $fn(&self) -> Expr<$T> { | ||||
|                 self.to_expr().$fn() | ||||
|             } | ||||
|         })* | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| make_to_reset! { | ||||
|     #[from_value(SyncReset)] | ||||
|     #[from_value(AsyncReset)] | ||||
|     pub trait ToReset { | ||||
|         fn to_reset(&self) -> Expr<Reset>; | ||||
| reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true); | ||||
| reset_type!(SyncReset, ToSyncReset::to_sync_reset, true); | ||||
| reset_type!( | ||||
|     Reset, | ||||
|     ToReset::to_reset, | ||||
|     false // Reset is not castable from bits because we don't know if it's async or sync
 | ||||
| ); | ||||
| 
 | ||||
| impl ToSyncReset for bool { | ||||
|     fn to_sync_reset(&self) -> Expr<SyncReset> { | ||||
|         self.to_expr().to_sync_reset() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| make_to_reset! { | ||||
|     #[from_value(bool)] | ||||
|     #[from_value(UInt<1>)] | ||||
|     pub trait ToAsyncReset { | ||||
|         fn to_async_reset(&self) -> Expr<AsyncReset>; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| make_to_reset! { | ||||
|     #[from_value(bool)] | ||||
|     #[from_value(UInt<1>)] | ||||
|     pub trait ToSyncReset { | ||||
|         fn to_sync_reset(&self) -> Expr<SyncReset>; | ||||
| impl ToAsyncReset for bool { | ||||
|     fn to_async_reset(&self) -> Expr<AsyncReset> { | ||||
|         self.to_expr().to_async_reset() | ||||
|     } | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,10 +3,13 @@ | |||
| 
 | ||||
| mod const_bool; | ||||
| mod const_cmp; | ||||
| mod const_usize; | ||||
| mod misc; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||
| #[doc(inline)] | ||||
| pub use const_usize::{ConstUsize, GenericConstUsize}; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use const_cmp::{ | ||||
|  |  | |||
							
								
								
									
										29
									
								
								crates/fayalite/src/util/const_usize.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								crates/fayalite/src/util/const_usize.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use std::{fmt::Debug, hash::Hash}; | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait Sealed {} | ||||
| } | ||||
| 
 | ||||
| /// the only implementation is `ConstUsize<Self::VALUE>`
 | ||||
| pub trait GenericConstUsize: | ||||
|     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||
| { | ||||
|     const VALUE: usize; | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||
| pub struct ConstUsize<const VALUE: usize>; | ||||
| 
 | ||||
| impl<const VALUE: usize> Debug for ConstUsize<VALUE> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_tuple("ConstUsize").field(&Self::VALUE).finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {} | ||||
| 
 | ||||
| impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> { | ||||
|     const VALUE: usize = VALUE; | ||||
| } | ||||
|  | @ -1,15 +1,15 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::{ | ||||
|     expr::{Expr, ExprTrait, Flow, ToExpr}, | ||||
|     expr::Flow, | ||||
|     intern::Interned, | ||||
|     module::{NameId, ScopedNameId}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{DynCanonicalType, DynType, Type}, | ||||
|     ty::{CanonicalType, Type}, | ||||
| }; | ||||
| use std::fmt; | ||||
| 
 | ||||
| #[derive(Clone, Eq, PartialEq, Hash)] | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash)] | ||||
| pub struct Wire<T: Type> { | ||||
|     name: ScopedNameId, | ||||
|     source_location: SourceLocation, | ||||
|  | @ -25,20 +25,8 @@ impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToExpr for Wire<T> { | ||||
|     type Type = T; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         self.ty.clone() | ||||
|     } | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { | ||||
|         Expr::new_unchecked(self.expr_enum()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> Wire<T> { | ||||
|     pub fn canonical(&self) -> Wire<T::CanonicalType> { | ||||
|     pub fn canonical(&self) -> Wire<CanonicalType> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|  | @ -50,29 +38,8 @@ impl<T: Type> Wire<T> { | |||
|             ty: ty.canonical(), | ||||
|         } | ||||
|     } | ||||
|     pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|             ref ty, | ||||
|         } = *self; | ||||
|         Wire { | ||||
|             name, | ||||
|             source_location, | ||||
|             ty: ty.to_dyn(), | ||||
|         } | ||||
|     } | ||||
|     pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> { | ||||
|         let Self { | ||||
|             name, | ||||
|             source_location, | ||||
|             ref ty, | ||||
|         } = *self; | ||||
|         Wire { | ||||
|             name, | ||||
|             source_location, | ||||
|             ty: ty.canonical_dyn(), | ||||
|         } | ||||
|     pub fn ty(&self) -> T { | ||||
|         self.ty | ||||
|     } | ||||
|     pub fn new_unchecked( | ||||
|         scoped_name: ScopedNameId, | ||||
|  |  | |||
|  | @ -1,32 +1,26 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use fayalite::{ | ||||
|     int::UInt, | ||||
|     ty::{StaticValue, Value}, | ||||
|     array::ArrayType, | ||||
|     hdl, | ||||
|     int::{IntType, Size, UInt}, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] | ||||
| #[hdl(outline_generated)] | ||||
| pub struct S<T> { | ||||
| pub struct S<T: IntType, Len: Size> { | ||||
|     pub a: T, | ||||
|     b: UInt<3>, | ||||
|     pub(crate) c: ArrayType<UInt<1>, Len>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] | ||||
| #[hdl(outline_generated)] | ||||
| pub enum E<T> { | ||||
|     A, | ||||
|     B {}, | ||||
|     C(), | ||||
|     D(UInt<3>), | ||||
|     E { a: UInt<3> }, | ||||
|     F(UInt<3>, UInt<3>), | ||||
|     G(T), | ||||
|     H(T, UInt<1>), | ||||
|     B(UInt<3>), | ||||
|     C(T), | ||||
| } | ||||
| 
 | ||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] | ||||
| #[hdl(outline_generated, static, where(T: StaticValue))] | ||||
| pub struct S2<T> { | ||||
| #[hdl(outline_generated)] | ||||
| pub struct S2<T = ()> { | ||||
|     pub v: E<T>, | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue