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
						1f83a9cca3
					
				
					 43 changed files with 12554 additions and 12237 deletions
				
			
		|  | @ -7,13 +7,14 @@ jobs: | ||||||
|       - uses: https://code.forgejo.org/actions/checkout@v3 |       - uses: https://code.forgejo.org/actions/checkout@v3 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - run: | | # FIXME: uncomment once the code works again | ||||||
|           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 | #      - run: | | ||||||
|           source "$HOME/.cargo/env" | #          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 | ||||||
|           echo "$PATH" >> "$GITHUB_PATH" | #          source "$HOME/.cargo/env" | ||||||
|       - uses: https://github.com/Swatinem/rust-cache@v2 | #          echo "$PATH" >> "$GITHUB_PATH" | ||||||
|         with: | #      - uses: https://github.com/Swatinem/rust-cache@v2 | ||||||
|           save-if: ${{ github.ref == 'refs/heads/master' }} | #        with: | ||||||
|       - run: cargo test | #          save-if: ${{ github.ref == 'refs/heads/master' }} | ||||||
|       - run: cargo test --features=unstable-doc | #      - run: cargo test | ||||||
|       - run: cargo doc --features=unstable-doc | #      - run: cargo test --features=unstable-doc | ||||||
|  | #      - run: cargo doc --features=unstable-doc | ||||||
|  |  | ||||||
|  | @ -233,16 +233,20 @@ forward_fold!(syn::WherePredicate => fold_where_predicate); | ||||||
| no_op_fold!(syn::parse::Nothing); | no_op_fold!(syn::parse::Nothing); | ||||||
| no_op_fold!(syn::token::Brace); | no_op_fold!(syn::token::Brace); | ||||||
| no_op_fold!(syn::token::Bracket); | no_op_fold!(syn::token::Bracket); | ||||||
|  | no_op_fold!(syn::token::Group); | ||||||
| no_op_fold!(syn::token::Paren); | no_op_fold!(syn::token::Paren); | ||||||
| no_op_fold!(syn::Token![_]); | no_op_fold!(syn::Token![_]); | ||||||
| no_op_fold!(syn::Token![,]); | no_op_fold!(syn::Token![,]); | ||||||
| no_op_fold!(syn::Token![;]); | no_op_fold!(syn::Token![;]); | ||||||
| no_op_fold!(syn::Token![:]); | no_op_fold!(syn::Token![:]); | ||||||
|  | no_op_fold!(syn::Token![::]); | ||||||
| no_op_fold!(syn::Token![..]); | no_op_fold!(syn::Token![..]); | ||||||
| no_op_fold!(syn::Token![.]); | no_op_fold!(syn::Token![.]); | ||||||
| no_op_fold!(syn::Token![#]); | no_op_fold!(syn::Token![#]); | ||||||
|  | no_op_fold!(syn::Token![<]); | ||||||
| no_op_fold!(syn::Token![=]); | no_op_fold!(syn::Token![=]); | ||||||
| no_op_fold!(syn::Token![=>]); | no_op_fold!(syn::Token![=>]); | ||||||
|  | no_op_fold!(syn::Token![>]); | ||||||
| no_op_fold!(syn::Token![|]); | no_op_fold!(syn::Token![|]); | ||||||
| no_op_fold!(syn::Token![enum]); | no_op_fold!(syn::Token![enum]); | ||||||
| no_op_fold!(syn::Token![extern]); | no_op_fold!(syn::Token![extern]); | ||||||
|  | @ -251,3 +255,4 @@ no_op_fold!(syn::Token![mut]); | ||||||
| no_op_fold!(syn::Token![static]); | no_op_fold!(syn::Token![static]); | ||||||
| no_op_fold!(syn::Token![struct]); | no_op_fold!(syn::Token![struct]); | ||||||
| no_op_fold!(syn::Token![where]); | no_op_fold!(syn::Token![where]); | ||||||
|  | no_op_fold!(usize); | ||||||
|  |  | ||||||
							
								
								
									
										857
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										857
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,857 @@ | ||||||
|  | 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: _, | ||||||
|  |             target: _, | ||||||
|  |             custom_bounds, | ||||||
|  |             no_static: _, | ||||||
|  |             no_runtime_generics: _, | ||||||
|  |         } = 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: impl ::fayalite::expr::ToExpr<Type = #ty>, | ||||||
|  |                     ) -> #filled_ty { | ||||||
|  |                         let Self { | ||||||
|  |                             #phantom_field_name: _, | ||||||
|  |                             #(#pat_fields)* | ||||||
|  |                         } = self; | ||||||
|  |                         let #field_ident = ::fayalite::expr::ToExpr::to_expr(&#field_ident); | ||||||
|  |                         #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, | ||||||
|  |                             ::fayalite::intern::Intern::intern(&__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: _, | ||||||
|  |             target, | ||||||
|  |             custom_bounds: _, | ||||||
|  |             no_static, | ||||||
|  |             no_runtime_generics, | ||||||
|  |         } = &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), None) = | ||||||
|  |             (generics, fields, no_runtime_generics) | ||||||
|  |         { | ||||||
|  |             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 BaseType = ::fayalite::bundle::Bundle; | ||||||
|  |                 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>, | ||||||
|  |                     __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)*][..]) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             impl #impl_generics #mask_type_ident #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 #vis fn __bundle_builder() -> #unfilled_mask_type_builder_ty { | ||||||
|  |                     ::fayalite::__std::default::Default::default() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[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 BaseType = ::fayalite::bundle::Bundle; | ||||||
|  |                 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>, | ||||||
|  |                     __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)*][..]) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             impl #impl_generics #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 #vis fn __bundle_builder() -> #unfilled_builder_ty { | ||||||
|  |                     ::fayalite::__std::default::Default::default() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[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: _, | ||||||
|  |             target: _, | ||||||
|  |             custom_bounds, | ||||||
|  |             no_static: _, | ||||||
|  |             no_runtime_generics: _, | ||||||
|  |         } = 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: _, | ||||||
|  |             target, | ||||||
|  |             custom_bounds: _, | ||||||
|  |             no_static, | ||||||
|  |             no_runtime_generics, | ||||||
|  |         } = &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), None) = (generics, no_runtime_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_by_index( | ||||||
|  |                                     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_by_index( | ||||||
|  |                                     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_by_index( | ||||||
|  |                                     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 BaseType = ::fayalite::enum_::Enum; | ||||||
|  |                 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>, | ||||||
|  |                     source_location: ::fayalite::source_location::SourceLocation, | ||||||
|  |                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||||
|  |                     ::fayalite::module::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) | ||||||
|  | } | ||||||
							
								
								
									
										3084
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3084
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -13,15 +13,15 @@ use syn::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| mod fold; | mod fold; | ||||||
|  | mod hdl_bundle; | ||||||
|  | mod hdl_enum; | ||||||
|  | mod hdl_type_common; | ||||||
| mod module; | mod module; | ||||||
| mod value_derive_common; | //mod value_derive_common;
 | ||||||
| mod value_derive_enum; | //mod value_derive_struct;
 | ||||||
| mod value_derive_struct; |  | ||||||
| 
 | 
 | ||||||
| mod kw { | mod kw { | ||||||
|     pub(crate) use syn::token::{ |     pub(crate) use syn::token::Extern as extern_; | ||||||
|         Enum as enum_, Extern as extern_, Static as static_, Struct as struct_, Where as where_, |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     macro_rules! custom_keyword { |     macro_rules! custom_keyword { | ||||||
|         ($kw:ident) => { |         ($kw:ident) => { | ||||||
|  | @ -43,6 +43,7 @@ mod kw { | ||||||
| 
 | 
 | ||||||
|     custom_keyword!(clock_domain); |     custom_keyword!(clock_domain); | ||||||
|     custom_keyword!(connect_inexact); |     custom_keyword!(connect_inexact); | ||||||
|  |     custom_keyword!(custom_bounds); | ||||||
|     custom_keyword!(flip); |     custom_keyword!(flip); | ||||||
|     custom_keyword!(hdl); |     custom_keyword!(hdl); | ||||||
|     custom_keyword!(input); |     custom_keyword!(input); | ||||||
|  | @ -52,6 +53,8 @@ mod kw { | ||||||
|     custom_keyword!(memory_array); |     custom_keyword!(memory_array); | ||||||
|     custom_keyword!(memory_with_init); |     custom_keyword!(memory_with_init); | ||||||
|     custom_keyword!(no_reset); |     custom_keyword!(no_reset); | ||||||
|  |     custom_keyword!(no_runtime_generics); | ||||||
|  |     custom_keyword!(no_static); | ||||||
|     custom_keyword!(outline_generated); |     custom_keyword!(outline_generated); | ||||||
|     custom_keyword!(output); |     custom_keyword!(output); | ||||||
|     custom_keyword!(reg_builder); |     custom_keyword!(reg_builder); | ||||||
|  | @ -519,6 +522,26 @@ macro_rules! impl_extra_traits_for_options { | ||||||
|     ) => { |     ) => { | ||||||
|         impl Copy for $option_enum_name {} |         impl Copy for $option_enum_name {} | ||||||
| 
 | 
 | ||||||
|  |         impl PartialEq for $option_enum_name { | ||||||
|  |             fn eq(&self, other: &Self) -> bool { | ||||||
|  |                 self.cmp(other).is_eq() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl Eq for $option_enum_name {} | ||||||
|  | 
 | ||||||
|  |         impl PartialOrd for $option_enum_name { | ||||||
|  |             fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||||||
|  |                 Some(self.cmp(other)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl Ord for $option_enum_name { | ||||||
|  |             fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||||||
|  |                 self.variant().cmp(&other.variant()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         impl quote::IdentFragment for $option_enum_name { |         impl quote::IdentFragment for $option_enum_name { | ||||||
|             fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |             fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|                 let _ = f; |                 let _ = f; | ||||||
|  | @ -554,6 +577,66 @@ pub(crate) use impl_extra_traits_for_options; | ||||||
| macro_rules! options { | macro_rules! options { | ||||||
|     ( |     ( | ||||||
|         #[options = $options_name:ident] |         #[options = $options_name:ident] | ||||||
|  |         $($tt:tt)* | ||||||
|  |     ) => { | ||||||
|  |         crate::options! { | ||||||
|  |             #[options = $options_name, punct = syn::Token![,], allow_duplicates = false] | ||||||
|  |             $($tt)* | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ( | ||||||
|  |         #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true] | ||||||
|  |         $(#[$($enum_meta:tt)*])* | ||||||
|  |         $enum_vis:vis enum $option_enum_name:ident { | ||||||
|  |             $($Variant:ident($key:ident $(, $value:ty)?),)* | ||||||
|  |         } | ||||||
|  |     ) => { | ||||||
|  |         crate::options! { | ||||||
|  |             #[options = $options_name, punct = $Punct, allow_duplicates = (true)] | ||||||
|  |             $(#[$($enum_meta)*])* | ||||||
|  |             $enum_vis enum $option_enum_name { | ||||||
|  |                 $($Variant($key $(, $value)?),)* | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl Extend<$option_enum_name> for $options_name { | ||||||
|  |             fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) { | ||||||
|  |                 iter.into_iter().for_each(|v| match v { | ||||||
|  |                     $($option_enum_name::$Variant(v) => { | ||||||
|  |                         self.$key = Some(v); | ||||||
|  |                     })* | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl FromIterator<$option_enum_name> for $options_name { | ||||||
|  |             fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self { | ||||||
|  |                 let mut retval = Self::default(); | ||||||
|  |                 retval.extend(iter); | ||||||
|  |                 retval | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl Extend<$options_name> for $options_name { | ||||||
|  |             fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) { | ||||||
|  |                 iter.into_iter().for_each(|v| { | ||||||
|  |                     $(if let Some(v) = v.$key { | ||||||
|  |                         self.$key = Some(v); | ||||||
|  |                     })* | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl FromIterator<$options_name> for $options_name { | ||||||
|  |             fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self { | ||||||
|  |                 let mut retval = Self::default(); | ||||||
|  |                 retval.extend(iter); | ||||||
|  |                 retval | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ( | ||||||
|  |         #[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr] | ||||||
|         $(#[$($enum_meta:tt)*])* |         $(#[$($enum_meta:tt)*])* | ||||||
|         $enum_vis:vis enum $option_enum_name:ident { |         $enum_vis:vis enum $option_enum_name:ident { | ||||||
|             $($Variant:ident($key:ident $(, $value:ty)?),)* |             $($Variant:ident($key:ident $(, $value:ty)?),)* | ||||||
|  | @ -567,8 +650,11 @@ macro_rules! options { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         #[derive(Clone, Debug, Default)] |         #[derive(Clone, Debug, Default)] | ||||||
|  |         #[allow(non_snake_case)] | ||||||
|         $enum_vis struct $options_name { |         $enum_vis struct $options_name { | ||||||
|             $($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)* |             $( | ||||||
|  |                 $enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>, | ||||||
|  |             )* | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         crate::fold::impl_fold! { |         crate::fold::impl_fold! { | ||||||
|  | @ -577,6 +663,43 @@ macro_rules! options { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         const _: () = { | ||||||
|  |             #[derive(Clone, Debug)] | ||||||
|  |             $enum_vis struct Iter($enum_vis $options_name); | ||||||
|  | 
 | ||||||
|  |             impl IntoIterator for $options_name { | ||||||
|  |                 type Item = $option_enum_name; | ||||||
|  |                 type IntoIter = Iter; | ||||||
|  | 
 | ||||||
|  |                 fn into_iter(self) -> Self::IntoIter { | ||||||
|  |                     Iter(self) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             impl Iterator for Iter { | ||||||
|  |                 type Item = $option_enum_name; | ||||||
|  | 
 | ||||||
|  |                 fn next(&mut self) -> Option<Self::Item> { | ||||||
|  |                     $( | ||||||
|  |                         if let Some(value) = self.0.$key.take() { | ||||||
|  |                             return Some($option_enum_name::$Variant(value)); | ||||||
|  |                         } | ||||||
|  |                     )* | ||||||
|  |                     None | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 #[allow(unused_mut, unused_variables)] | ||||||
|  |                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { | ||||||
|  |                     $( | ||||||
|  |                         if let Some(value) = self.0.$key.take() { | ||||||
|  |                             init = f(init, $option_enum_name::$Variant(value)); | ||||||
|  |                         } | ||||||
|  |                     )* | ||||||
|  |                     init | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         impl syn::parse::Parse for $options_name { |         impl syn::parse::Parse for $options_name { | ||||||
|             fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |             fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { | ||||||
|                 #![allow(unused_mut, unused_variables, unreachable_code)] |                 #![allow(unused_mut, unused_variables, unreachable_code)] | ||||||
|  | @ -585,7 +708,7 @@ macro_rules! options { | ||||||
|                     let old_input = input.fork(); |                     let old_input = input.fork(); | ||||||
|                     match input.parse::<$option_enum_name>()? { |                     match input.parse::<$option_enum_name>()? { | ||||||
|                         $($option_enum_name::$Variant(v) => { |                         $($option_enum_name::$Variant(v) => { | ||||||
|                             if retval.$key.replace(v).is_some() { |                             if retval.$key.replace(v).is_some() && !$allow_duplicates { | ||||||
|                                 return Err(old_input.error(concat!("duplicate ", stringify!($key), " option"))); |                                 return Err(old_input.error(concat!("duplicate ", stringify!($key), " option"))); | ||||||
|                             } |                             } | ||||||
|                         })* |                         })* | ||||||
|  | @ -593,7 +716,7 @@ macro_rules! options { | ||||||
|                     if input.is_empty() { |                     if input.is_empty() { | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                     input.parse::<syn::Token![,]>()?; |                     input.parse::<$Punct>()?; | ||||||
|                 } |                 } | ||||||
|                 Ok(retval) |                 Ok(retval) | ||||||
|             } |             } | ||||||
|  | @ -602,7 +725,7 @@ macro_rules! options { | ||||||
|         impl quote::ToTokens for $options_name { |         impl quote::ToTokens for $options_name { | ||||||
|             #[allow(unused_mut, unused_variables, unused_assignments)] |             #[allow(unused_mut, unused_variables, unused_assignments)] | ||||||
|             fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { |             fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { | ||||||
|                 let mut separator: Option<syn::Token![,]> = None; |                 let mut separator: Option<$Punct> = None; | ||||||
|                 $(if let Some(v) = &self.$key { |                 $(if let Some(v) = &self.$key { | ||||||
|                     separator.to_tokens(tokens); |                     separator.to_tokens(tokens); | ||||||
|                     separator = Some(Default::default()); |                     separator = Some(Default::default()); | ||||||
|  | @ -673,6 +796,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 +823,15 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr | ||||||
|         .suffix(".tmp.rs") |         .suffix(".tmp.rs") | ||||||
|         .tempfile_in(out_dir) |         .tempfile_in(out_dir) | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |     struct PrintOnPanic<'a>(&'a TokenStream); | ||||||
|  |     impl Drop for PrintOnPanic<'_> { | ||||||
|  |         fn drop(&mut self) { | ||||||
|  |             if std::thread::panicking() { | ||||||
|  |                 println!("{}", self.0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     let _print_on_panic = PrintOnPanic(&contents); | ||||||
|     let contents = prettyplease::unparse(&parse_quote! { #contents }); |     let contents = prettyplease::unparse(&parse_quote! { #contents }); | ||||||
|     let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents); |     let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents); | ||||||
|     let hash = base16ct::HexDisplay(&hash[..5]); |     let hash = base16ct::HexDisplay(&hash[..5]); | ||||||
|  | @ -706,7 +852,7 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||||
|     let options = syn::parse2::<module::ConfigOptions>(attr)?; |     let options = syn::parse2::<module::ConfigOptions>(attr)?; | ||||||
|     let options = HdlAttr::from(options); |     let options = HdlAttr::from(options); | ||||||
|     let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?; |     let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?; | ||||||
|  | @ -717,14 +863,14 @@ pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> | ||||||
|     Ok(contents) |     Ok(contents) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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>(item)?; |     let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?; | ||||||
|     match item { |     match item { | ||||||
|         Item::Enum(item) => value_derive_enum::value_derive_enum(item), |         Item::Enum(item) => hdl_enum::hdl_enum(item), | ||||||
|         Item::Struct(item) => value_derive_struct::value_derive_struct(item), |         Item::Struct(item) => hdl_bundle::hdl_bundle(item), | ||||||
|         _ => Err(syn::Error::new( |         _ => Err(syn::Error::new( | ||||||
|             Span::call_site(), |             Span::call_site(), | ||||||
|             "derive(Value) can only be used on structs or enums", |             "top-level #[hdl] can only be used on structs or enums", | ||||||
|         )), |         )), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     is_hdl_attr, |     hdl_type_common::{ParsedGenerics, SplitForImpl}, | ||||||
|     module::transform_body::{HdlLet, HdlLetKindIO}, |     module::transform_body::{HdlLet, HdlLetKindIO}, | ||||||
|     options, Errors, HdlAttr, PairsIterExt, |     options, Errors, HdlAttr, PairsIterExt, | ||||||
| }; | }; | ||||||
|  | @ -57,13 +57,6 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn retain_struct_attrs<F: FnMut(&Attribute) -> bool>(item: &mut ItemStruct, mut f: F) { |  | ||||||
|     item.attrs.retain(&mut f); |  | ||||||
|     for field in item.fields.iter_mut() { |  | ||||||
|         field.attrs.retain(&mut f); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>; | pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct ModuleFn { | pub(crate) struct ModuleFn { | ||||||
|  | @ -74,7 +67,7 @@ pub(crate) struct ModuleFn { | ||||||
|     sig: Signature, |     sig: Signature, | ||||||
|     block: Box<Block>, |     block: Box<Block>, | ||||||
|     io: Vec<ModuleIO>, |     io: Vec<ModuleIO>, | ||||||
|     struct_generics: Generics, |     struct_generics: ParsedGenerics, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||||||
|  | @ -220,8 +213,16 @@ impl Parse for ModuleFn { | ||||||
|                 "return type not allowed here", |                 "return type not allowed here", | ||||||
|             )); |             )); | ||||||
|         } |         } | ||||||
|         let body_results = errors.ok(transform_body::transform_body(module_kind, block)); |         let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics })); | ||||||
|  |         let body_results = struct_generics.as_ref().and_then(|struct_generics| { | ||||||
|  |             errors.ok(transform_body::transform_body( | ||||||
|  |                 module_kind, | ||||||
|  |                 block, | ||||||
|  |                 struct_generics, | ||||||
|  |             )) | ||||||
|  |         }); | ||||||
|         errors.finish()?; |         errors.finish()?; | ||||||
|  |         let struct_generics = struct_generics.unwrap(); | ||||||
|         let (block, io) = body_results.unwrap(); |         let (block, io) = body_results.unwrap(); | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             attrs, |             attrs, | ||||||
|  | @ -273,19 +274,18 @@ impl ModuleFn { | ||||||
|                 }); |                 }); | ||||||
|                 name |                 name | ||||||
|             })); |             })); | ||||||
|         let module_kind_ty = match module_kind { |         let module_kind_value = match module_kind { | ||||||
|             ModuleKind::Extern => quote! { ::fayalite::module::ExternModule }, |             ModuleKind::Extern => quote! { ::fayalite::module::ModuleKind::Extern }, | ||||||
|             ModuleKind::Normal => quote! { ::fayalite::module::NormalModule }, |             ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal }, | ||||||
|         }; |         }; | ||||||
|         let fn_name = &outer_sig.ident; |         let fn_name = &outer_sig.ident; | ||||||
|         let (_struct_impl_generics, struct_type_generics, struct_where_clause) = |         let (_struct_impl_generics, struct_type_generics, struct_where_clause) = | ||||||
|             struct_generics.split_for_impl(); |             struct_generics.split_for_impl(); | ||||||
|         let struct_ty = quote! {#fn_name #struct_type_generics}; |         let struct_ty = quote! {#fn_name #struct_type_generics}; | ||||||
|         body_sig.ident = parse_quote! {__body}; |         body_sig.ident = parse_quote! {__body}; | ||||||
|         body_sig.inputs.insert( |         body_sig | ||||||
|             0, |             .inputs | ||||||
|             parse_quote! {m: &mut ::fayalite::module::ModuleBuilder<#struct_ty, #module_kind_ty>}, |             .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); | ||||||
|         ); |  | ||||||
|         let body_fn = ItemFn { |         let body_fn = ItemFn { | ||||||
|             attrs: vec![], |             attrs: vec![], | ||||||
|             vis: Visibility::Inherited, |             vis: Visibility::Inherited, | ||||||
|  | @ -310,34 +310,21 @@ impl ModuleFn { | ||||||
|         let body_turbofish_type_generics = body_type_generics.as_turbofish(); |         let body_turbofish_type_generics = body_type_generics.as_turbofish(); | ||||||
|         let block = parse_quote! {{ |         let block = parse_quote! {{ | ||||||
|             #body_fn |             #body_fn | ||||||
|             ::fayalite::module::ModuleBuilder::run(#fn_name_str, |m| __body #body_turbofish_type_generics(m, #(#param_names,)*)) |             ::fayalite::module::ModuleBuilder::run( | ||||||
|  |                 #fn_name_str, | ||||||
|  |                 #module_kind_value, | ||||||
|  |                 |m| __body #body_turbofish_type_generics(m, #(#param_names,)*), | ||||||
|  |             ) | ||||||
|         }}; |         }}; | ||||||
|         let static_type = io.iter().all(|io| io.kind.ty_expr.is_none()); |  | ||||||
|         let struct_options = if static_type { |  | ||||||
|             quote! { #[hdl(static)] } |  | ||||||
|         } else { |  | ||||||
|             quote! {} |  | ||||||
|         }; |  | ||||||
|         let the_struct: ItemStruct = parse_quote! { |         let the_struct: ItemStruct = parse_quote! { | ||||||
|             #[derive(::fayalite::__std::clone::Clone,
 |  | ||||||
|                 ::fayalite::__std::hash::Hash, |  | ||||||
|                 ::fayalite::__std::cmp::PartialEq, |  | ||||||
|                 ::fayalite::__std::cmp::Eq, |  | ||||||
|                 ::fayalite::__std::fmt::Debug)] |  | ||||||
|             #[allow(non_camel_case_types)] |             #[allow(non_camel_case_types)] | ||||||
|             #struct_options |             #[hdl(no_runtime_generics)] | ||||||
|             #vis struct #fn_name #struct_generics #struct_where_clause { |             #vis struct #fn_name #struct_generics #struct_where_clause { | ||||||
|                 #( |                 #( | ||||||
|                 #io_flips |                 #io_flips | ||||||
|                 #vis #io_names: #io_types,)* |                 #vis #io_names: #io_types,)* | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         let mut struct_without_hdl_attrs = the_struct.clone(); |  | ||||||
|         let mut struct_without_derives = the_struct; |  | ||||||
|         retain_struct_attrs(&mut struct_without_hdl_attrs, |attr| !is_hdl_attr(attr)); |  | ||||||
|         retain_struct_attrs(&mut struct_without_derives, |attr| { |  | ||||||
|             !attr.path().is_ident("derive") |  | ||||||
|         }); |  | ||||||
|         let outer_fn = ItemFn { |         let outer_fn = ItemFn { | ||||||
|             attrs, |             attrs, | ||||||
|             vis, |             vis, | ||||||
|  | @ -345,10 +332,7 @@ impl ModuleFn { | ||||||
|             block, |             block, | ||||||
|         }; |         }; | ||||||
|         let mut retval = outer_fn.into_token_stream(); |         let mut retval = outer_fn.into_token_stream(); | ||||||
|         struct_without_hdl_attrs.to_tokens(&mut retval); |         retval.extend(crate::hdl_bundle::hdl_bundle(the_struct).unwrap()); | ||||||
|         retval.extend( |  | ||||||
|             crate::value_derive_struct::value_derive_struct(struct_without_derives).unwrap(), |  | ||||||
|         ); |  | ||||||
|         retval |         retval | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     fold::{impl_fold, DoFold}, |     fold::{impl_fold, DoFold}, | ||||||
|  |     hdl_type_common::{ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser}, | ||||||
|     is_hdl_attr, kw, |     is_hdl_attr, kw, | ||||||
|     module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind}, |     module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind}, | ||||||
|     options, Errors, HdlAttr, |     options, Errors, HdlAttr, | ||||||
|  | @ -87,9 +88,9 @@ macro_rules! with_debug_clone_and_fold { | ||||||
| pub(crate) use with_debug_clone_and_fold; | pub(crate) use with_debug_clone_and_fold; | ||||||
| 
 | 
 | ||||||
| with_debug_clone_and_fold! { | with_debug_clone_and_fold! { | ||||||
|     pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind> { |     pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind, Ty = ParsedType> { | ||||||
|         pub(crate) colon_token: Token![:], |         pub(crate) colon_token: Token![:], | ||||||
|         pub(crate) ty: Box<Type>, |         pub(crate) ty: Box<Ty>, | ||||||
|         pub(crate) m: kw::m, |         pub(crate) m: kw::m, | ||||||
|         pub(crate) dot_token: Token![.], |         pub(crate) dot_token: Token![.], | ||||||
|         pub(crate) kind: Kind, |         pub(crate) kind: Kind, | ||||||
|  | @ -98,6 +99,32 @@ with_debug_clone_and_fold! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl<Kind: Clone, T: ParseTypes<I>, I> ParseTypes<HdlLetKindIO<Kind, I>> for HdlLetKindIO<Kind, T> { | ||||||
|  |     fn parse_types( | ||||||
|  |         input: &mut HdlLetKindIO<Kind, I>, | ||||||
|  |         parser: &mut TypesParser<'_>, | ||||||
|  |     ) -> Result<Self, ParseFailed> { | ||||||
|  |         let HdlLetKindIO { | ||||||
|  |             colon_token, | ||||||
|  |             ty, | ||||||
|  |             m, | ||||||
|  |             dot_token, | ||||||
|  |             kind, | ||||||
|  |             paren, | ||||||
|  |             ty_expr, | ||||||
|  |         } = input; | ||||||
|  |         Ok(Self { | ||||||
|  |             colon_token: *colon_token, | ||||||
|  |             ty: ParseTypes::parse_types(ty, parser)?, | ||||||
|  |             m: *m, | ||||||
|  |             dot_token: *dot_token, | ||||||
|  |             kind: kind.clone(), | ||||||
|  |             paren: *paren, | ||||||
|  |             ty_expr: ty_expr.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result<Box<Expr>> { | pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result<Box<Expr>> { | ||||||
|     let retval = input.parse()?; |     let retval = input.parse()?; | ||||||
|     let _: Option<Token![,]> = input.parse()?; |     let _: Option<Token![,]> = input.parse()?; | ||||||
|  | @ -145,17 +172,19 @@ impl<Kind: ToTokens> HdlLetKindToTokens for HdlLetKindIO<Kind> { | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) struct HdlLetKindInstance { | pub(crate) struct HdlLetKindInstance { | ||||||
|     pub(crate) m: kw::m, |  | ||||||
|     pub(crate) dot_token: Token![.], |  | ||||||
|     pub(crate) instance: kw::instance, |     pub(crate) instance: kw::instance, | ||||||
|     pub(crate) paren: Paren, |     pub(crate) paren: Paren, | ||||||
|     pub(crate) module: Box<Expr>, |     pub(crate) module: Box<Expr>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl ParseTypes<Self> for HdlLetKindInstance { | ||||||
|  |     fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> { | ||||||
|  |         Ok(input.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|     struct HdlLetKindInstance<> { |     struct HdlLetKindInstance<> { | ||||||
|         m: kw::m, |  | ||||||
|         dot_token: Token![.], |  | ||||||
|         instance: kw::instance, |         instance: kw::instance, | ||||||
|         paren: Paren, |         paren: Paren, | ||||||
|         module: Box<Expr>, |         module: Box<Expr>, | ||||||
|  | @ -167,14 +196,10 @@ impl HdlLetKindToTokens for HdlLetKindInstance { | ||||||
| 
 | 
 | ||||||
|     fn expr_to_tokens(&self, tokens: &mut TokenStream) { |     fn expr_to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             instance, |             instance, | ||||||
|             paren, |             paren, | ||||||
|             module, |             module, | ||||||
|         } = self; |         } = self; | ||||||
|         m.to_tokens(tokens); |  | ||||||
|         dot_token.to_tokens(tokens); |  | ||||||
|         instance.to_tokens(tokens); |         instance.to_tokens(tokens); | ||||||
|         paren.surround(tokens, |tokens| module.to_tokens(tokens)); |         paren.surround(tokens, |tokens| module.to_tokens(tokens)); | ||||||
|     } |     } | ||||||
|  | @ -381,19 +406,21 @@ make_builder_method_enum! { | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) struct HdlLetKindRegBuilder { | pub(crate) struct HdlLetKindRegBuilder { | ||||||
|     pub(crate) ty: Option<(Token![:], Box<Type>)>, |     pub(crate) ty: Option<(Token![:], Box<Type>)>, | ||||||
|     pub(crate) m: kw::m, |  | ||||||
|     pub(crate) dot_token: Token![.], |  | ||||||
|     pub(crate) reg_builder: kw::reg_builder, |     pub(crate) reg_builder: kw::reg_builder, | ||||||
|     pub(crate) reg_builder_paren: Paren, |     pub(crate) reg_builder_paren: Paren, | ||||||
|     pub(crate) clock_domain: Option<RegBuilderClockDomain>, |     pub(crate) clock_domain: Option<RegBuilderClockDomain>, | ||||||
|     pub(crate) reset: RegBuilderReset, |     pub(crate) reset: RegBuilderReset, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl ParseTypes<Self> for HdlLetKindRegBuilder { | ||||||
|  |     fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> { | ||||||
|  |         Ok(input.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|     struct HdlLetKindRegBuilder<> { |     struct HdlLetKindRegBuilder<> { | ||||||
|         ty: Option<(Token![:], Box<Type>)>, |         ty: Option<(Token![:], Box<Type>)>, | ||||||
|         m: kw::m, |  | ||||||
|         dot_token: Token![.], |  | ||||||
|         reg_builder: kw::reg_builder, |         reg_builder: kw::reg_builder, | ||||||
|         reg_builder_paren: Paren, |         reg_builder_paren: Paren, | ||||||
|         clock_domain: Option<RegBuilderClockDomain>, |         clock_domain: Option<RegBuilderClockDomain>, | ||||||
|  | @ -406,10 +433,10 @@ impl HdlLetKindRegBuilder { | ||||||
|         input: ParseStream, |         input: ParseStream, | ||||||
|         parsed_ty: Option<(Token![:], Box<Type>)>, |         parsed_ty: Option<(Token![:], Box<Type>)>, | ||||||
|         _after_ty: Token![=], |         _after_ty: Token![=], | ||||||
|         m: kw::m, |         m_dot: Option<(kw::m, Token![.])>, | ||||||
|         dot_token: Token![.], |  | ||||||
|         reg_builder: kw::reg_builder, |         reg_builder: kw::reg_builder, | ||||||
|     ) -> syn::Result<Self> { |     ) -> syn::Result<Self> { | ||||||
|  |         check_empty_m_dot(m_dot, reg_builder)?; | ||||||
|         let _reg_builder_paren_inner; |         let _reg_builder_paren_inner; | ||||||
|         let reg_builder_paren = parenthesized!(_reg_builder_paren_inner in input); |         let reg_builder_paren = parenthesized!(_reg_builder_paren_inner in input); | ||||||
|         let mut clock_domain = None; |         let mut clock_domain = None; | ||||||
|  | @ -430,8 +457,6 @@ impl HdlLetKindRegBuilder { | ||||||
|         } |         } | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             ty: parsed_ty, |             ty: parsed_ty, | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             reg_builder, |             reg_builder, | ||||||
|             reg_builder_paren, |             reg_builder_paren, | ||||||
|             clock_domain, |             clock_domain, | ||||||
|  | @ -451,15 +476,11 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder { | ||||||
|     fn expr_to_tokens(&self, tokens: &mut TokenStream) { |     fn expr_to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { | ||||||
|             ty: _, |             ty: _, | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             reg_builder, |             reg_builder, | ||||||
|             reg_builder_paren, |             reg_builder_paren, | ||||||
|             clock_domain, |             clock_domain, | ||||||
|             reset, |             reset, | ||||||
|         } = self; |         } = self; | ||||||
|         m.to_tokens(tokens); |  | ||||||
|         dot_token.to_tokens(tokens); |  | ||||||
|         reg_builder.to_tokens(tokens); |         reg_builder.to_tokens(tokens); | ||||||
|         reg_builder_paren.surround(tokens, |_tokens| {}); |         reg_builder_paren.surround(tokens, |_tokens| {}); | ||||||
|         clock_domain.to_tokens(tokens); |         clock_domain.to_tokens(tokens); | ||||||
|  | @ -470,18 +491,20 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder { | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) struct HdlLetKindWire { | pub(crate) struct HdlLetKindWire { | ||||||
|     pub(crate) ty: Option<(Token![:], Box<Type>)>, |     pub(crate) ty: Option<(Token![:], Box<Type>)>, | ||||||
|     pub(crate) m: kw::m, |  | ||||||
|     pub(crate) dot_token: Token![.], |  | ||||||
|     pub(crate) wire: kw::wire, |     pub(crate) wire: kw::wire, | ||||||
|     pub(crate) paren: Paren, |     pub(crate) paren: Paren, | ||||||
|     pub(crate) ty_expr: Option<Box<Expr>>, |     pub(crate) ty_expr: Option<Box<Expr>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl ParseTypes<Self> for HdlLetKindWire { | ||||||
|  |     fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> { | ||||||
|  |         Ok(input.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|     struct HdlLetKindWire<> { |     struct HdlLetKindWire<> { | ||||||
|         ty: Option<(Token![:], Box<Type>)>, |         ty: Option<(Token![:], Box<Type>)>, | ||||||
|         m: kw::m, |  | ||||||
|         dot_token: Token![.], |  | ||||||
|         wire: kw::wire, |         wire: kw::wire, | ||||||
|         paren: Paren, |         paren: Paren, | ||||||
|         ty_expr: Option<Box<Expr>>, |         ty_expr: Option<Box<Expr>>, | ||||||
|  | @ -499,14 +522,10 @@ impl HdlLetKindToTokens for HdlLetKindWire { | ||||||
|     fn expr_to_tokens(&self, tokens: &mut TokenStream) { |     fn expr_to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { | ||||||
|             ty: _, |             ty: _, | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             wire, |             wire, | ||||||
|             paren, |             paren, | ||||||
|             ty_expr, |             ty_expr, | ||||||
|         } = self; |         } = self; | ||||||
|         m.to_tokens(tokens); |  | ||||||
|         dot_token.to_tokens(tokens); |  | ||||||
|         wire.to_tokens(tokens); |         wire.to_tokens(tokens); | ||||||
|         paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens)); |         paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens)); | ||||||
|     } |     } | ||||||
|  | @ -627,16 +646,18 @@ impl ToTokens for MemoryFn { | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) struct HdlLetKindMemory { | pub(crate) struct HdlLetKindMemory { | ||||||
|     pub(crate) ty: Option<(Token![:], Box<Type>)>, |     pub(crate) ty: Option<(Token![:], Box<Type>)>, | ||||||
|     pub(crate) m: kw::m, |  | ||||||
|     pub(crate) dot_token: Token![.], |  | ||||||
|     pub(crate) memory_fn: MemoryFn, |     pub(crate) memory_fn: MemoryFn, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl ParseTypes<Self> for HdlLetKindMemory { | ||||||
|  |     fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> { | ||||||
|  |         Ok(input.clone()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|     struct HdlLetKindMemory<> { |     struct HdlLetKindMemory<> { | ||||||
|         ty: Option<(Token![:], Box<Type>)>, |         ty: Option<(Token![:], Box<Type>)>, | ||||||
|         m: kw::m, |  | ||||||
|         dot_token: Token![.], |  | ||||||
|         memory_fn: MemoryFn, |         memory_fn: MemoryFn, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -650,14 +671,7 @@ impl HdlLetKindToTokens for HdlLetKindMemory { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn expr_to_tokens(&self, tokens: &mut TokenStream) { |     fn expr_to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { ty: _, memory_fn } = self; | ||||||
|             ty: _, |  | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             memory_fn, |  | ||||||
|         } = self; |  | ||||||
|         m.to_tokens(tokens); |  | ||||||
|         dot_token.to_tokens(tokens); |  | ||||||
|         memory_fn.to_tokens(tokens); |         memory_fn.to_tokens(tokens); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -667,22 +681,20 @@ impl HdlLetKindMemory { | ||||||
|         input: ParseStream, |         input: ParseStream, | ||||||
|         parsed_ty: Option<(Token![:], Box<Type>)>, |         parsed_ty: Option<(Token![:], Box<Type>)>, | ||||||
|         _after_ty: Token![=], |         _after_ty: Token![=], | ||||||
|         m: kw::m, |         m_dot: Option<(kw::m, Token![.])>, | ||||||
|         dot_token: Token![.], |  | ||||||
|         memory_fn_name: MemoryFnName, |         memory_fn_name: MemoryFnName, | ||||||
|     ) -> syn::Result<Self> { |     ) -> syn::Result<Self> { | ||||||
|  |         check_empty_m_dot(m_dot, memory_fn_name)?; | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             ty: parsed_ty, |             ty: parsed_ty, | ||||||
|             m, |  | ||||||
|             dot_token, |  | ||||||
|             memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?, |             memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) enum HdlLetKind { | pub(crate) enum HdlLetKind<IOType = ParsedType> { | ||||||
|     IO(HdlLetKindIO), |     IO(HdlLetKindIO<ModuleIOKind, IOType>), | ||||||
|     Instance(HdlLetKindInstance), |     Instance(HdlLetKindInstance), | ||||||
|     RegBuilder(HdlLetKindRegBuilder), |     RegBuilder(HdlLetKindRegBuilder), | ||||||
|     Wire(HdlLetKindWire), |     Wire(HdlLetKindWire), | ||||||
|  | @ -690,8 +702,8 @@ pub(crate) enum HdlLetKind { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|     enum HdlLetKind<> { |     enum HdlLetKind<IOType,> { | ||||||
|         IO(HdlLetKindIO), |         IO(HdlLetKindIO<ModuleIOKind, IOType>), | ||||||
|         Instance(HdlLetKindInstance), |         Instance(HdlLetKindInstance), | ||||||
|         RegBuilder(HdlLetKindRegBuilder), |         RegBuilder(HdlLetKindRegBuilder), | ||||||
|         Wire(HdlLetKindWire), |         Wire(HdlLetKindWire), | ||||||
|  | @ -699,6 +711,27 @@ impl_fold! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> { | ||||||
|  |     fn parse_types( | ||||||
|  |         input: &mut HdlLetKind<I>, | ||||||
|  |         parser: &mut TypesParser<'_>, | ||||||
|  |     ) -> Result<Self, ParseFailed> { | ||||||
|  |         match input { | ||||||
|  |             HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO), | ||||||
|  |             HdlLetKind::Instance(input) => { | ||||||
|  |                 ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance) | ||||||
|  |             } | ||||||
|  |             HdlLetKind::RegBuilder(input) => { | ||||||
|  |                 ParseTypes::parse_types(input, parser).map(HdlLetKind::RegBuilder) | ||||||
|  |             } | ||||||
|  |             HdlLetKind::Wire(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::Wire), | ||||||
|  |             HdlLetKind::Memory(input) => { | ||||||
|  |                 ParseTypes::parse_types(input, parser).map(HdlLetKind::Memory) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn parsed_ty_or_err( | fn parsed_ty_or_err( | ||||||
|     parsed_ty: Option<(Token![:], Box<Type>)>, |     parsed_ty: Option<(Token![:], Box<Type>)>, | ||||||
|     after_ty: Token![=], |     after_ty: Token![=], | ||||||
|  | @ -710,15 +743,15 @@ fn parsed_ty_or_err( | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl HdlLetKindIO { | impl HdlLetKindIO<ModuleIOKind, Type> { | ||||||
|     fn rest_of_parse( |     fn rest_of_parse( | ||||||
|         input: ParseStream, |         input: ParseStream, | ||||||
|         parsed_ty: Option<(Token![:], Box<Type>)>, |         parsed_ty: Option<(Token![:], Box<Type>)>, | ||||||
|         after_ty: Token![=], |         after_ty: Token![=], | ||||||
|         m: kw::m, |         m_dot: Option<(kw::m, Token![.])>, | ||||||
|         dot_token: Token![.], |  | ||||||
|         kind: ModuleIOKind, |         kind: ModuleIOKind, | ||||||
|     ) -> syn::Result<Self> { |     ) -> syn::Result<Self> { | ||||||
|  |         let (m, dot_token) = unwrap_m_dot(m_dot, kind)?; | ||||||
|         let (colon_token, ty) = parsed_ty_or_err(parsed_ty, after_ty)?; |         let (colon_token, ty) = parsed_ty_or_err(parsed_ty, after_ty)?; | ||||||
|         let paren_contents; |         let paren_contents; | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|  | @ -733,7 +766,36 @@ impl HdlLetKindIO { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl HdlLetKindParse for HdlLetKind { | fn check_empty_m_dot(m_dot: Option<(kw::m, Token![.])>, kind: impl ToTokens) -> syn::Result<()> { | ||||||
|  |     if let Some((m, dot_token)) = m_dot { | ||||||
|  |         Err(Error::new_spanned( | ||||||
|  |             quote! {#m #dot_token #kind}, | ||||||
|  |             format_args!( | ||||||
|  |                 "{} is a free function, not a method of ModuleBuilder: try removing the `m.`", | ||||||
|  |                 kind.to_token_stream() | ||||||
|  |             ), | ||||||
|  |         )) | ||||||
|  |     } else { | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn unwrap_m_dot( | ||||||
|  |     m_dot: Option<(kw::m, Token![.])>, | ||||||
|  |     kind: impl ToTokens, | ||||||
|  | ) -> syn::Result<(kw::m, Token![.])> { | ||||||
|  |     m_dot.ok_or_else(|| { | ||||||
|  |         Error::new_spanned( | ||||||
|  |             &kind, | ||||||
|  |             format_args!( | ||||||
|  |                 "{} is a ModuleBuilder method, not a free function: try prefixing it with `m.`", | ||||||
|  |                 kind.to_token_stream() | ||||||
|  |             ), | ||||||
|  |         ) | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl HdlLetKindParse for HdlLetKind<Type> { | ||||||
|     type ParsedTy = Option<(Token![:], Box<Type>)>; |     type ParsedTy = Option<(Token![:], Box<Type>)>; | ||||||
| 
 | 
 | ||||||
|     fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy> { |     fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy> { | ||||||
|  | @ -753,16 +815,20 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|         after_ty: Token![=], |         after_ty: Token![=], | ||||||
|         input: ParseStream, |         input: ParseStream, | ||||||
|     ) -> syn::Result<Self> { |     ) -> syn::Result<Self> { | ||||||
|  |         let m_dot = if input.peek(kw::m) && input.peek2(Token![.]) { | ||||||
|             let m = input.parse()?; |             let m = input.parse()?; | ||||||
|             let dot_token = input.parse()?; |             let dot_token = input.parse()?; | ||||||
|  |             Some((m, dot_token)) | ||||||
|  |         } else { | ||||||
|  |             None | ||||||
|  |         }; | ||||||
|         let kind: LetFnKind = input.parse()?; |         let kind: LetFnKind = input.parse()?; | ||||||
|         match kind { |         match kind { | ||||||
|             LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse( |             LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse( | ||||||
|                 input, |                 input, | ||||||
|                 parsed_ty, |                 parsed_ty, | ||||||
|                 after_ty, |                 after_ty, | ||||||
|                 m, |                 m_dot, | ||||||
|                 dot_token, |  | ||||||
|                 ModuleIOKind::Input(input_token), |                 ModuleIOKind::Input(input_token), | ||||||
|             ) |             ) | ||||||
|             .map(Self::IO), |             .map(Self::IO), | ||||||
|  | @ -770,8 +836,7 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|                 input, |                 input, | ||||||
|                 parsed_ty, |                 parsed_ty, | ||||||
|                 after_ty, |                 after_ty, | ||||||
|                 m, |                 m_dot, | ||||||
|                 dot_token, |  | ||||||
|                 ModuleIOKind::Output(output), |                 ModuleIOKind::Output(output), | ||||||
|             ) |             ) | ||||||
|             .map(Self::IO), |             .map(Self::IO), | ||||||
|  | @ -782,30 +847,23 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|                         "type annotation not allowed for instance", |                         "type annotation not allowed for instance", | ||||||
|                     )); |                     )); | ||||||
|                 } |                 } | ||||||
|  |                 check_empty_m_dot(m_dot, kind)?; | ||||||
|                 let paren_contents; |                 let paren_contents; | ||||||
|                 Ok(Self::Instance(HdlLetKindInstance { |                 Ok(Self::Instance(HdlLetKindInstance { | ||||||
|                     m, |  | ||||||
|                     dot_token, |  | ||||||
|                     instance, |                     instance, | ||||||
|                     paren: parenthesized!(paren_contents in input), |                     paren: parenthesized!(paren_contents in input), | ||||||
|                     module: paren_contents.call(parse_single_fn_arg)?, |                     module: paren_contents.call(parse_single_fn_arg)?, | ||||||
|                 })) |                 })) | ||||||
|             } |             } | ||||||
|             LetFnKind::RegBuilder((reg_builder,)) => HdlLetKindRegBuilder::rest_of_parse( |             LetFnKind::RegBuilder((reg_builder,)) => { | ||||||
|                 input, |                 HdlLetKindRegBuilder::rest_of_parse(input, parsed_ty, after_ty, m_dot, reg_builder) | ||||||
|                 parsed_ty, |                     .map(Self::RegBuilder) | ||||||
|                 after_ty, |             } | ||||||
|                 m, |  | ||||||
|                 dot_token, |  | ||||||
|                 reg_builder, |  | ||||||
|             ) |  | ||||||
|             .map(Self::RegBuilder), |  | ||||||
|             LetFnKind::Wire((wire,)) => { |             LetFnKind::Wire((wire,)) => { | ||||||
|  |                 check_empty_m_dot(m_dot, wire)?; | ||||||
|                 let paren_contents; |                 let paren_contents; | ||||||
|                 Ok(Self::Wire(HdlLetKindWire { |                 Ok(Self::Wire(HdlLetKindWire { | ||||||
|                     ty: parsed_ty, |                     ty: parsed_ty, | ||||||
|                     m, |  | ||||||
|                     dot_token, |  | ||||||
|                     wire, |                     wire, | ||||||
|                     paren: parenthesized!(paren_contents in input), |                     paren: parenthesized!(paren_contents in input), | ||||||
|                     ty_expr: paren_contents.call(parse_optional_fn_arg)?, |                     ty_expr: paren_contents.call(parse_optional_fn_arg)?, | ||||||
|  | @ -815,8 +873,7 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|                 input, |                 input, | ||||||
|                 parsed_ty, |                 parsed_ty, | ||||||
|                 after_ty, |                 after_ty, | ||||||
|                 m, |                 m_dot, | ||||||
|                 dot_token, |  | ||||||
|                 MemoryFnName::Memory(fn_name), |                 MemoryFnName::Memory(fn_name), | ||||||
|             ) |             ) | ||||||
|             .map(Self::Memory), |             .map(Self::Memory), | ||||||
|  | @ -824,8 +881,7 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|                 input, |                 input, | ||||||
|                 parsed_ty, |                 parsed_ty, | ||||||
|                 after_ty, |                 after_ty, | ||||||
|                 m, |                 m_dot, | ||||||
|                 dot_token, |  | ||||||
|                 MemoryFnName::MemoryArray(fn_name), |                 MemoryFnName::MemoryArray(fn_name), | ||||||
|             ) |             ) | ||||||
|             .map(Self::Memory), |             .map(Self::Memory), | ||||||
|  | @ -833,8 +889,7 @@ impl HdlLetKindParse for HdlLetKind { | ||||||
|                 input, |                 input, | ||||||
|                 parsed_ty, |                 parsed_ty, | ||||||
|                 after_ty, |                 after_ty, | ||||||
|                 m, |                 m_dot, | ||||||
|                 dot_token, |  | ||||||
|                 MemoryFnName::MemoryWithInit(fn_name), |                 MemoryFnName::MemoryWithInit(fn_name), | ||||||
|             ) |             ) | ||||||
|             .map(Self::Memory), |             .map(Self::Memory), | ||||||
|  | @ -878,6 +933,34 @@ with_debug_clone_and_fold! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl<T: ParseTypes<I>, I> ParseTypes<HdlLet<I>> for HdlLet<T> { | ||||||
|  |     fn parse_types( | ||||||
|  |         input: &mut HdlLet<I>, | ||||||
|  |         parser: &mut TypesParser<'_>, | ||||||
|  |     ) -> Result<Self, ParseFailed> { | ||||||
|  |         let HdlLet { | ||||||
|  |             attrs, | ||||||
|  |             hdl_attr, | ||||||
|  |             let_token, | ||||||
|  |             mut_token, | ||||||
|  |             name, | ||||||
|  |             eq_token, | ||||||
|  |             kind, | ||||||
|  |             semi_token, | ||||||
|  |         } = input; | ||||||
|  |         Ok(Self { | ||||||
|  |             attrs: attrs.clone(), | ||||||
|  |             hdl_attr: hdl_attr.clone(), | ||||||
|  |             let_token: *let_token, | ||||||
|  |             mut_token: *mut_token, | ||||||
|  |             name: name.clone(), | ||||||
|  |             eq_token: *eq_token, | ||||||
|  |             kind: T::parse_types(kind, parser)?, | ||||||
|  |             semi_token: *semi_token, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl<Kind> HdlLet<Kind> { | impl<Kind> HdlLet<Kind> { | ||||||
|     pub(crate) fn try_map<Kind2, E>( |     pub(crate) fn try_map<Kind2, E>( | ||||||
|         self, |         self, | ||||||
|  | @ -1006,7 +1089,7 @@ fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { | ||||||
| fn unwrap_or_static_type(expr: Option<impl ToTokens>, span: Span) -> TokenStream { | fn unwrap_or_static_type(expr: Option<impl ToTokens>, span: Span) -> TokenStream { | ||||||
|     expr.map(ToTokens::into_token_stream).unwrap_or_else(|| { |     expr.map(ToTokens::into_token_stream).unwrap_or_else(|| { | ||||||
|         quote_spanned! {span=> |         quote_spanned! {span=> | ||||||
|             ::fayalite::ty::StaticType::static_type() |             ::fayalite::ty::StaticType::TYPE | ||||||
|         } |         } | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  | @ -1026,14 +1109,15 @@ impl<T: ToString> ToTokens for ImplicitName<T> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Visitor { | struct Visitor<'a> { | ||||||
|     module_kind: ModuleKind, |     module_kind: ModuleKind, | ||||||
|     errors: Errors, |     errors: Errors, | ||||||
|     io: Vec<ModuleIO>, |     io: Vec<ModuleIO>, | ||||||
|     block_depth: usize, |     block_depth: usize, | ||||||
|  |     parsed_generics: &'a ParsedGenerics, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Visitor { | impl Visitor<'_> { | ||||||
|     fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> { |     fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> { | ||||||
|         self.errors.unwrap_or( |         self.errors.unwrap_or( | ||||||
|             HdlAttr::parse_and_take_attr(attrs), |             HdlAttr::parse_and_take_attr(attrs), | ||||||
|  | @ -1157,8 +1241,6 @@ impl Visitor { | ||||||
|             eq_token, |             eq_token, | ||||||
|             kind: |             kind: | ||||||
|                 HdlLetKindInstance { |                 HdlLetKindInstance { | ||||||
|                     m, |  | ||||||
|                     dot_token, |  | ||||||
|                     instance, |                     instance, | ||||||
|                     paren, |                     paren, | ||||||
|                     module, |                     module, | ||||||
|  | @ -1166,7 +1248,7 @@ impl Visitor { | ||||||
|             semi_token, |             semi_token, | ||||||
|         } = hdl_let; |         } = hdl_let; | ||||||
|         self.require_normal_module(instance); |         self.require_normal_module(instance); | ||||||
|         let mut expr = quote! {#m #dot_token #instance}; |         let mut expr = instance.to_token_stream(); | ||||||
|         paren.surround(&mut expr, |expr| { |         paren.surround(&mut expr, |expr| { | ||||||
|             let name_str = ImplicitName { |             let name_str = ImplicitName { | ||||||
|                 name: &name, |                 name: &name, | ||||||
|  | @ -1191,11 +1273,9 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local { |     fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local { | ||||||
|         let name = &hdl_let.name; |         let name = &hdl_let.name; | ||||||
|         let m = hdl_let.kind.m; |  | ||||||
|         let dot = hdl_let.kind.dot_token; |  | ||||||
|         let reg_builder = hdl_let.kind.reg_builder; |         let reg_builder = hdl_let.kind.reg_builder; | ||||||
|         self.require_normal_module(reg_builder); |         self.require_normal_module(reg_builder); | ||||||
|         let mut expr = quote! {#m #dot #reg_builder}; |         let mut expr = reg_builder.to_token_stream(); | ||||||
|         hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { |         hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { | ||||||
|             let name_str = ImplicitName { |             let name_str = ImplicitName { | ||||||
|                 name, |                 name, | ||||||
|  | @ -1244,12 +1324,10 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local { |     fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local { | ||||||
|         let name = &hdl_let.name; |         let name = &hdl_let.name; | ||||||
|         let m = hdl_let.kind.m; |  | ||||||
|         let dot = hdl_let.kind.dot_token; |  | ||||||
|         let wire = hdl_let.kind.wire; |         let wire = hdl_let.kind.wire; | ||||||
|         self.require_normal_module(wire); |         self.require_normal_module(wire); | ||||||
|         let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); |         let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); | ||||||
|         let mut expr = quote! {#m #dot #wire}; |         let mut expr = wire.to_token_stream(); | ||||||
|         hdl_let.kind.paren.surround(&mut expr, |expr| { |         hdl_let.kind.paren.surround(&mut expr, |expr| { | ||||||
|             let name_str = ImplicitName { |             let name_str = ImplicitName { | ||||||
|                 name, |                 name, | ||||||
|  | @ -1279,18 +1357,19 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local { |     fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local { | ||||||
|         let name = &hdl_let.name; |         let name = &hdl_let.name; | ||||||
|         let m = hdl_let.kind.m; |  | ||||||
|         let dot = hdl_let.kind.dot_token; |  | ||||||
|         let memory_fn = hdl_let.kind.memory_fn; |         let memory_fn = hdl_let.kind.memory_fn; | ||||||
|         let memory_fn_name = memory_fn.name(); |         let memory_fn_name = memory_fn.name(); | ||||||
|         self.require_normal_module(memory_fn_name); |         self.require_normal_module(memory_fn_name); | ||||||
|         let mut expr = quote! {#m #dot #memory_fn_name}; |         let mut expr = memory_fn_name.to_token_stream(); | ||||||
|         let (paren, arg) = match memory_fn { |         let (paren, arg) = match memory_fn { | ||||||
|             MemoryFn::Memory { |             MemoryFn::Memory { | ||||||
|                 memory, |                 memory, | ||||||
|                 paren, |                 paren, | ||||||
|                 ty_expr, |                 ty_expr, | ||||||
|             } => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())), |             } => ( | ||||||
|  |                 paren, | ||||||
|  |                 unwrap_or_static_type(ty_expr.as_ref(), memory.span()), | ||||||
|  |             ), | ||||||
|             MemoryFn::MemoryArray { |             MemoryFn::MemoryArray { | ||||||
|                 memory_array, |                 memory_array, | ||||||
|                 paren, |                 paren, | ||||||
|  | @ -1449,7 +1528,7 @@ fn empty_let() -> Local { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Fold for Visitor { | impl Fold for Visitor<'_> { | ||||||
|     fn fold_item(&mut self, item: Item) -> Item { |     fn fold_item(&mut self, item: Item) -> Item { | ||||||
|         // don't process item interiors
 |         // don't process item interiors
 | ||||||
|         item |         item | ||||||
|  | @ -1536,11 +1615,16 @@ impl Fold for Visitor { | ||||||
|             Some(None) => return fold_local(self, let_stmt), |             Some(None) => return fold_local(self, let_stmt), | ||||||
|             Some(Some(HdlAttr { .. })) => {} |             Some(Some(HdlAttr { .. })) => {} | ||||||
|         }; |         }; | ||||||
|         let hdl_let = syn::parse2::<HdlLet>(let_stmt.into_token_stream()); |         let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream()); | ||||||
|         let Some(hdl_let) = self.errors.ok(hdl_let) else { |         let Some(hdl_let) = self.errors.ok(hdl_let) else { | ||||||
|             return empty_let(); |             return empty_let(); | ||||||
|         }; |         }; | ||||||
|         let hdl_let = hdl_let.do_fold(self); |         let mut hdl_let = hdl_let.do_fold(self); | ||||||
|  |         let Ok(hdl_let) = | ||||||
|  |             TypesParser::run_with_errors(self.parsed_generics, &mut hdl_let, &mut self.errors) | ||||||
|  |         else { | ||||||
|  |             return empty_let(); | ||||||
|  |         }; | ||||||
|         self.process_hdl_let(hdl_let) |         self.process_hdl_let(hdl_let) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1563,12 +1647,14 @@ impl Fold for Visitor { | ||||||
| pub(crate) fn transform_body( | pub(crate) fn transform_body( | ||||||
|     module_kind: ModuleKind, |     module_kind: ModuleKind, | ||||||
|     mut body: Box<Block>, |     mut body: Box<Block>, | ||||||
|  |     parsed_generics: &ParsedGenerics, | ||||||
| ) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> { | ) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> { | ||||||
|     let mut visitor = Visitor { |     let mut visitor = Visitor { | ||||||
|         module_kind, |         module_kind, | ||||||
|         errors: Errors::new(), |         errors: Errors::new(), | ||||||
|         io: vec![], |         io: vec![], | ||||||
|         block_depth: 0, |         block_depth: 0, | ||||||
|  |         parsed_generics, | ||||||
|     }; |     }; | ||||||
|     *body = syn::fold::fold_block(&mut visitor, *body); |     *body = syn::fold::fold_block(&mut visitor, *body); | ||||||
|     visitor.errors.finish()?; |     visitor.errors.finish()?; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{module::transform_body::Visitor, options, Errors, HdlAttr, PairsIterExt}; | use crate::{module::transform_body::Visitor, Errors, HdlAttr, PairsIterExt}; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | ||||||
| use syn::{ | use syn::{ | ||||||
|  | @ -13,15 +13,6 @@ use syn::{ | ||||||
|     FieldValue, Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath, |     FieldValue, Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| options! { |  | ||||||
|     #[options = AggregateLiteralOptions] |  | ||||||
|     #[no_ident_fragment] |  | ||||||
|     pub(crate) enum AggregateLiteralOption { |  | ||||||
|         Struct(struct_), |  | ||||||
|         Enum(enum_), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub(crate) struct StructOrEnumPath { | pub(crate) struct StructOrEnumPath { | ||||||
|     pub(crate) ty: TypePath, |     pub(crate) ty: TypePath, | ||||||
|  | @ -79,11 +70,7 @@ impl StructOrEnumPath { | ||||||
|             }, |             }, | ||||||
|         ] |         ] | ||||||
|     }; |     }; | ||||||
|     pub(crate) fn new( |     pub(crate) fn new(errors: &mut Errors, path: TypePath) -> Result<Self, ()> { | ||||||
|         errors: &mut Errors, |  | ||||||
|         path: TypePath, |  | ||||||
|         options: &AggregateLiteralOptions, |  | ||||||
|     ) -> Result<Self, ()> { |  | ||||||
|         let Path { |         let Path { | ||||||
|             leading_colon, |             leading_colon, | ||||||
|             segments, |             segments, | ||||||
|  | @ -95,18 +82,6 @@ impl StructOrEnumPath { | ||||||
|             None |             None | ||||||
|         }; |         }; | ||||||
|         let enum_type = 'guess_enum_type: { |         let enum_type = 'guess_enum_type: { | ||||||
|             if options.enum_.is_some() { |  | ||||||
|                 if let Some((struct_,)) = options.struct_ { |  | ||||||
|                     errors.error( |  | ||||||
|                         struct_, |  | ||||||
|                         "can't specify both #[hdl(enum)] and #[hdl(struct)]", |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|                 break 'guess_enum_type Some(None); |  | ||||||
|             } |  | ||||||
|             if options.struct_.is_some() { |  | ||||||
|                 break 'guess_enum_type None; |  | ||||||
|             } |  | ||||||
|             if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 { |             if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 { | ||||||
|                 let PathSegment { ident, arguments } = &segments[0]; |                 let PathSegment { ident, arguments } = &segments[0]; | ||||||
|                 for &SingleSegmentVariant { |                 for &SingleSegmentVariant { | ||||||
|  | @ -330,7 +305,7 @@ impl ToTokens for StructOrEnumLiteral { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Visitor { | impl Visitor<'_> { | ||||||
|     pub(crate) fn process_hdl_array( |     pub(crate) fn process_hdl_array( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<Nothing>, |         hdl_attr: HdlAttr<Nothing>, | ||||||
|  | @ -358,7 +333,7 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_struct_enum( |     pub(crate) fn process_struct_enum( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<AggregateLiteralOptions>, |         hdl_attr: HdlAttr<Nothing>, | ||||||
|         mut literal: StructOrEnumLiteral, |         mut literal: StructOrEnumLiteral, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         let span = hdl_attr.hdl.span; |         let span = hdl_attr.hdl.span; | ||||||
|  | @ -384,7 +359,7 @@ impl Visitor { | ||||||
|             parse_quote! { #field_var } |             parse_quote! { #field_var } | ||||||
|         }); |         }); | ||||||
|         let Ok(StructOrEnumPath { ty, variant }) = |         let Ok(StructOrEnumPath { ty, variant }) = | ||||||
|             StructOrEnumPath::new(&mut self.errors, literal.path.clone(), &hdl_attr.body) |             StructOrEnumPath::new(&mut self.errors, literal.path.clone()) | ||||||
|         else { |         else { | ||||||
|             return parse_quote_spanned! {span=> |             return parse_quote_spanned! {span=> | ||||||
|                 {} |                 {} | ||||||
|  | @ -445,11 +420,59 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_struct( |     pub(crate) fn process_hdl_struct( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<AggregateLiteralOptions>, |         hdl_attr: HdlAttr<Nothing>, | ||||||
|         expr_struct: ExprStruct, |         expr_struct: ExprStruct, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module(&hdl_attr); |         self.require_normal_module(&hdl_attr); | ||||||
|         self.process_struct_enum(hdl_attr, expr_struct.into()) |         let name_span = expr_struct.path.segments.last().unwrap().ident.span(); | ||||||
|  |         let builder_ident = format_ident!("__builder", span = name_span); | ||||||
|  |         let empty_builder = if expr_struct.qself.is_some() | ||||||
|  |             || expr_struct | ||||||
|  |                 .path | ||||||
|  |                 .segments | ||||||
|  |                 .iter() | ||||||
|  |                 .any(|seg| !seg.arguments.is_none()) | ||||||
|  |         { | ||||||
|  |             let ty = TypePath { | ||||||
|  |                 qself: expr_struct.qself, | ||||||
|  |                 path: expr_struct.path, | ||||||
|  |             }; | ||||||
|  |             let builder_ty = quote_spanned! {name_span=> | ||||||
|  |                 <#ty as ::fayalite::bundle::BundleType>::Builder | ||||||
|  |             }; | ||||||
|  |             quote_spanned! {name_span=> | ||||||
|  |                 <#builder_ty as ::fayalite::__std::default::Default>::default() | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             let path = ExprPath { | ||||||
|  |                 attrs: vec![], | ||||||
|  |                 qself: expr_struct.qself, | ||||||
|  |                 path: expr_struct.path, | ||||||
|  |             }; | ||||||
|  |             quote_spanned! {name_span=> | ||||||
|  |                 #path::__bundle_builder() | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         let field_calls = Vec::from_iter(expr_struct.fields.iter().map( | ||||||
|  |             |FieldValue { | ||||||
|  |                  attrs: _, | ||||||
|  |                  member, | ||||||
|  |                  colon_token: _, | ||||||
|  |                  expr, | ||||||
|  |              }| { | ||||||
|  |                 let field_fn = format_ident!("field_{}", member); | ||||||
|  |                 quote_spanned! {member.span()=> | ||||||
|  |                     let #builder_ident = #builder_ident.#field_fn(#expr); | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         )); | ||||||
|  |         parse_quote_spanned! {name_span=> | ||||||
|  |             { | ||||||
|  |                 let #builder_ident = #empty_builder; | ||||||
|  |                 #(#field_calls)* | ||||||
|  |                 ::fayalite::expr::ToExpr::to_expr(&#builder_ident) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_tuple( |     pub(crate) fn process_hdl_tuple( | ||||||
|         &mut self, |         &mut self, | ||||||
|  | @ -473,7 +496,7 @@ impl Visitor { | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_call( |     pub(crate) fn process_hdl_call( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<AggregateLiteralOptions>, |         hdl_attr: HdlAttr<Nothing>, | ||||||
|         expr_call: ExprCall, |         expr_call: ExprCall, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module(&hdl_attr); |         self.require_normal_module(&hdl_attr); | ||||||
|  |  | ||||||
|  | @ -3,8 +3,7 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     fold::impl_fold, |     fold::impl_fold, | ||||||
|     module::transform_body::{ |     module::transform_body::{ | ||||||
|         expand_aggregate_literals::{AggregateLiteralOptions, StructOrEnumPath}, |         expand_aggregate_literals::StructOrEnumPath, with_debug_clone_and_fold, Visitor, | ||||||
|         with_debug_clone_and_fold, Visitor, |  | ||||||
|     }, |     }, | ||||||
|     Errors, HdlAttr, PairsIterExt, |     Errors, HdlAttr, PairsIterExt, | ||||||
| }; | }; | ||||||
|  | @ -561,8 +560,7 @@ struct HdlMatchParseState<'a> { | ||||||
| 
 | 
 | ||||||
| impl HdlMatchParseState<'_> { | impl HdlMatchParseState<'_> { | ||||||
|     fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> { |     fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> { | ||||||
|         let StructOrEnumPath { ty, variant } = |         let StructOrEnumPath { ty, variant } = StructOrEnumPath::new(self.errors, path)?; | ||||||
|             StructOrEnumPath::new(self.errors, path, &AggregateLiteralOptions::default())?; |  | ||||||
|         Ok(if let Some((_variant_path, variant_name)) = variant { |         Ok(if let Some((_variant_path, variant_name)) = variant { | ||||||
|             parse_quote_spanned! {self.span=> |             parse_quote_spanned! {self.span=> | ||||||
|                 __MatchTy::<#ty>::#variant_name |                 __MatchTy::<#ty>::#variant_name | ||||||
|  | @ -575,7 +573,7 @@ impl HdlMatchParseState<'_> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Visitor { | impl Visitor<'_> { | ||||||
|     pub(crate) fn process_hdl_match( |     pub(crate) fn process_hdl_match( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _hdl_attr: HdlAttr<Nothing>, |         _hdl_attr: HdlAttr<Nothing>, | ||||||
|  |  | ||||||
|  | @ -1,761 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| use crate::{fold::impl_fold, kw, Errors, HdlAttr}; |  | ||||||
| use proc_macro2::{Span, TokenStream}; |  | ||||||
| use quote::{format_ident, quote, quote_spanned, ToTokens}; |  | ||||||
| use std::collections::{BTreeMap, HashMap, HashSet}; |  | ||||||
| use syn::{ |  | ||||||
|     fold::{fold_generics, Fold}, |  | ||||||
|     parse::{Parse, ParseStream}, |  | ||||||
|     parse_quote, parse_quote_spanned, |  | ||||||
|     punctuated::Punctuated, |  | ||||||
|     spanned::Spanned, |  | ||||||
|     token::{Brace, Paren, Where}, |  | ||||||
|     Block, ConstParam, Expr, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics, |  | ||||||
|     Ident, Index, ItemImpl, Lifetime, LifetimeParam, Member, Path, Token, Type, TypeParam, |  | ||||||
|     TypePath, Visibility, WhereClause, WherePredicate, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub(crate) struct Bounds(pub(crate) Punctuated<WherePredicate, Token![,]>); |  | ||||||
| 
 |  | ||||||
| impl_fold! { |  | ||||||
|     struct Bounds<>(Punctuated<WherePredicate, Token![,]>); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Parse for Bounds { |  | ||||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { |  | ||||||
|         Ok(Bounds(Punctuated::parse_terminated(input)?)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<Option<WhereClause>> for Bounds { |  | ||||||
|     fn from(value: Option<WhereClause>) -> Self { |  | ||||||
|         Self(value.map_or_else(Punctuated::new, |v| v.predicates)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for Bounds { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         self.0.to_tokens(tokens) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone)] |  | ||||||
| pub(crate) struct ParsedField<O> { |  | ||||||
|     pub(crate) options: HdlAttr<O>, |  | ||||||
|     pub(crate) vis: Visibility, |  | ||||||
|     pub(crate) name: Member, |  | ||||||
|     pub(crate) ty: Type, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<O> ParsedField<O> { |  | ||||||
|     pub(crate) fn var_name(&self) -> Ident { |  | ||||||
|         format_ident!("__v_{}", self.name) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn get_field_name( |  | ||||||
|     index: usize, |  | ||||||
|     name: Option<Ident>, |  | ||||||
|     ty_span: impl FnOnce() -> Span, |  | ||||||
| ) -> Member { |  | ||||||
|     match name { |  | ||||||
|         Some(name) => Member::Named(name), |  | ||||||
|         None => Member::Unnamed(Index { |  | ||||||
|             index: index as _, |  | ||||||
|             span: ty_span(), |  | ||||||
|         }), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn get_field_names(fields: &Fields) -> impl Iterator<Item = Member> + '_ { |  | ||||||
|     fields |  | ||||||
|         .iter() |  | ||||||
|         .enumerate() |  | ||||||
|         .map(|(index, field)| get_field_name(index, field.ident.clone(), || field.ty.span())) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<O: Parse + Default> ParsedField<O> { |  | ||||||
|     pub(crate) fn parse_fields( |  | ||||||
|         errors: &mut Errors, |  | ||||||
|         fields: &mut Fields, |  | ||||||
|         in_enum: bool, |  | ||||||
|     ) -> (FieldsKind, Vec<ParsedField<O>>) { |  | ||||||
|         let mut unit_fields = Punctuated::new(); |  | ||||||
|         let (fields_kind, fields) = match fields { |  | ||||||
|             Fields::Named(fields) => (FieldsKind::Named(fields.brace_token), &mut fields.named), |  | ||||||
|             Fields::Unnamed(fields) => { |  | ||||||
|                 (FieldsKind::Unnamed(fields.paren_token), &mut fields.unnamed) |  | ||||||
|             } |  | ||||||
|             Fields::Unit => (FieldsKind::Unit, &mut unit_fields), |  | ||||||
|         }; |  | ||||||
|         let fields = fields |  | ||||||
|             .iter_mut() |  | ||||||
|             .enumerate() |  | ||||||
|             .map(|(index, field)| { |  | ||||||
|                 let options = errors |  | ||||||
|                     .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut field.attrs)) |  | ||||||
|                     .unwrap_or_default(); |  | ||||||
|                 let name = get_field_name(index, field.ident.clone(), || field.ty.span()); |  | ||||||
|                 if in_enum && !matches!(field.vis, Visibility::Inherited) { |  | ||||||
|                     errors.error(&field.vis, "field visibility not allowed in enums"); |  | ||||||
|                 } |  | ||||||
|                 ParsedField { |  | ||||||
|                     options, |  | ||||||
|                     vis: field.vis.clone(), |  | ||||||
|                     name, |  | ||||||
|                     ty: field.ty.clone(), |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|             .collect(); |  | ||||||
|         (fields_kind, fields) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug)] |  | ||||||
| pub(crate) enum FieldsKind { |  | ||||||
|     Unit, |  | ||||||
|     Named(Brace), |  | ||||||
|     Unnamed(Paren), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl FieldsKind { |  | ||||||
|     pub(crate) fn into_fields_named( |  | ||||||
|         brace_token: Brace, |  | ||||||
|         fields: impl IntoIterator<Item = syn::Field>, |  | ||||||
|     ) -> Fields { |  | ||||||
|         Fields::Named(FieldsNamed { |  | ||||||
|             brace_token, |  | ||||||
|             named: Punctuated::from_iter(fields), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn into_fields_unnamed( |  | ||||||
|         paren_token: Paren, |  | ||||||
|         fields: impl IntoIterator<Item = syn::Field>, |  | ||||||
|     ) -> Fields { |  | ||||||
|         Fields::Unnamed(FieldsUnnamed { |  | ||||||
|             paren_token, |  | ||||||
|             unnamed: Punctuated::from_iter(fields), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn into_fields(self, fields: impl IntoIterator<Item = syn::Field>) -> Fields { |  | ||||||
|         match self { |  | ||||||
|             FieldsKind::Unit => { |  | ||||||
|                 let mut fields = fields.into_iter().peekable(); |  | ||||||
|                 let Some(first_field) = fields.peek() else { |  | ||||||
|                     return Fields::Unit; |  | ||||||
|                 }; |  | ||||||
|                 if first_field.ident.is_some() { |  | ||||||
|                     Self::into_fields_named(Default::default(), fields) |  | ||||||
|                 } else { |  | ||||||
|                     Self::into_fields_unnamed(Default::default(), fields) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             FieldsKind::Named(brace_token) => Self::into_fields_named(brace_token, fields), |  | ||||||
|             FieldsKind::Unnamed(paren_token) => Self::into_fields_unnamed(paren_token, fields), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path { |  | ||||||
|     match target { |  | ||||||
|         Some((_, _, target)) => target.clone(), |  | ||||||
|         None => item_ident.clone().into(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) struct ValueDeriveGenerics { |  | ||||||
|     pub(crate) generics: Generics, |  | ||||||
|     pub(crate) static_type_generics: Generics, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ValueDeriveGenerics { |  | ||||||
|     pub(crate) fn get(mut generics: Generics, where_: &Option<(Where, Paren, Bounds)>) -> Self { |  | ||||||
|         let mut static_type_generics = generics.clone(); |  | ||||||
|         if let Some((_, _, bounds)) = where_ { |  | ||||||
|             generics |  | ||||||
|                 .make_where_clause() |  | ||||||
|                 .predicates |  | ||||||
|                 .extend(bounds.0.iter().cloned()); |  | ||||||
|             static_type_generics |  | ||||||
|                 .where_clause |  | ||||||
|                 .clone_from(&generics.where_clause); |  | ||||||
|         } else { |  | ||||||
|             let type_params = Vec::from_iter(generics.type_params().map(|v| v.ident.clone())); |  | ||||||
|             let predicates = &mut generics.make_where_clause().predicates; |  | ||||||
|             let static_type_predicates = &mut static_type_generics.make_where_clause().predicates; |  | ||||||
|             for type_param in type_params { |  | ||||||
|                 predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value<Type: ::fayalite::ty::Type<Value = #type_param>>}); |  | ||||||
|                 static_type_predicates |  | ||||||
|                     .push(parse_quote! {#type_param: ::fayalite::ty::StaticValue}); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Self { |  | ||||||
|             generics, |  | ||||||
|             static_type_generics, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn derive_clone_hash_eq_partialeq_for_struct<Name: ToTokens>( |  | ||||||
|     the_struct_ident: &Ident, |  | ||||||
|     generics: &Generics, |  | ||||||
|     field_names: &[Name], |  | ||||||
| ) -> TokenStream { |  | ||||||
|     let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); |  | ||||||
|     quote! { |  | ||||||
|         #[automatically_derived] |  | ||||||
|         impl #impl_generics ::fayalite::__std::clone::Clone for #the_struct_ident #type_generics |  | ||||||
|         #where_clause |  | ||||||
|         { |  | ||||||
|             fn clone(&self) -> Self { |  | ||||||
|                 Self { |  | ||||||
|                     #(#field_names: ::fayalite::__std::clone::Clone::clone(&self.#field_names),)* |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[automatically_derived] |  | ||||||
|         impl #impl_generics ::fayalite::__std::hash::Hash for #the_struct_ident #type_generics |  | ||||||
|         #where_clause |  | ||||||
|         { |  | ||||||
|             #[allow(unused_variables)] |  | ||||||
|             fn hash<__H: ::fayalite::__std::hash::Hasher>(&self, hasher: &mut __H) { |  | ||||||
|                 #(::fayalite::__std::hash::Hash::hash(&self.#field_names, hasher);)* |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[automatically_derived] |  | ||||||
|         impl #impl_generics ::fayalite::__std::cmp::Eq for #the_struct_ident #type_generics |  | ||||||
|         #where_clause |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #[automatically_derived] |  | ||||||
|         impl #impl_generics ::fayalite::__std::cmp::PartialEq for #the_struct_ident #type_generics |  | ||||||
|         #where_clause |  | ||||||
|         { |  | ||||||
|             #[allow(unused_variables)] |  | ||||||
|             #[allow(clippy::nonminimal_bool)] |  | ||||||
|             fn eq(&self, other: &Self) -> ::fayalite::__std::primitive::bool { |  | ||||||
|                 true #(&& ::fayalite::__std::cmp::PartialEq::eq( |  | ||||||
|                     &self.#field_names, |  | ||||||
|                     &other.#field_names, |  | ||||||
|                 ))* |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn append_field(fields: &mut Fields, mut field: Field) -> Member { |  | ||||||
|     let ident = field.ident.clone().expect("ident is supplied"); |  | ||||||
|     match fields { |  | ||||||
|         Fields::Named(FieldsNamed { named, .. }) => { |  | ||||||
|             named.push(field); |  | ||||||
|             Member::Named(ident) |  | ||||||
|         } |  | ||||||
|         Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { |  | ||||||
|             field.ident = None; |  | ||||||
|             field.colon_token = None; |  | ||||||
|             let index = unnamed.len(); |  | ||||||
|             unnamed.push(field); |  | ||||||
|             Member::Unnamed(index.into()) |  | ||||||
|         } |  | ||||||
|         Fields::Unit => { |  | ||||||
|             *fields = Fields::Named(FieldsNamed { |  | ||||||
|                 brace_token: Default::default(), |  | ||||||
|                 named: Punctuated::from_iter([field]), |  | ||||||
|             }); |  | ||||||
|             Member::Named(ident) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub(crate) struct BuilderField { |  | ||||||
|     pub(crate) names: HashSet<Member>, |  | ||||||
|     pub(crate) mapped_value: Expr, |  | ||||||
|     pub(crate) mapped_type: Type, |  | ||||||
|     pub(crate) where_clause: Option<WhereClause>, |  | ||||||
|     pub(crate) builder_field_name: Ident, |  | ||||||
|     pub(crate) type_param: Ident, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug)] |  | ||||||
| pub(crate) struct Builder { |  | ||||||
|     struct_name: Ident, |  | ||||||
|     vis: Visibility, |  | ||||||
|     fields: BTreeMap<String, BuilderField>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug)] |  | ||||||
| pub(crate) struct BuilderWithFields { |  | ||||||
|     struct_name: Ident, |  | ||||||
|     vis: Visibility, |  | ||||||
|     phantom_type_param: Ident, |  | ||||||
|     phantom_type_field: Ident, |  | ||||||
|     fields: Vec<(String, BuilderField)>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Builder { |  | ||||||
|     pub(crate) fn new(struct_name: Ident, vis: Visibility) -> Self { |  | ||||||
|         Self { |  | ||||||
|             struct_name, |  | ||||||
|             vis, |  | ||||||
|             fields: BTreeMap::new(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub(crate) fn insert_field( |  | ||||||
|         &mut self, |  | ||||||
|         name: Member, |  | ||||||
|         map_value: impl FnOnce(&Ident) -> Expr, |  | ||||||
|         map_type: impl FnOnce(&Ident) -> Type, |  | ||||||
|         where_clause: impl FnOnce(&Ident) -> Option<WhereClause>, |  | ||||||
|     ) { |  | ||||||
|         self.fields |  | ||||||
|             .entry(name.to_token_stream().to_string()) |  | ||||||
|             .or_insert_with_key(|name| { |  | ||||||
|                 let builder_field_name = |  | ||||||
|                     format_ident!("field_{}", name, span = self.struct_name.span()); |  | ||||||
|                 let type_param = format_ident!("__T_{}", name, span = self.struct_name.span()); |  | ||||||
|                 BuilderField { |  | ||||||
|                     names: HashSet::new(), |  | ||||||
|                     mapped_value: map_value(&builder_field_name), |  | ||||||
|                     mapped_type: map_type(&type_param), |  | ||||||
|                     where_clause: where_clause(&type_param), |  | ||||||
|                     builder_field_name, |  | ||||||
|                     type_param, |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|             .names |  | ||||||
|             .insert(name); |  | ||||||
|     } |  | ||||||
|     pub(crate) fn finish_filling_in_fields(self) -> BuilderWithFields { |  | ||||||
|         let Self { |  | ||||||
|             struct_name, |  | ||||||
|             vis, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         let fields = Vec::from_iter(fields); |  | ||||||
|         BuilderWithFields { |  | ||||||
|             phantom_type_param: Ident::new("__Phantom", struct_name.span()), |  | ||||||
|             phantom_type_field: Ident::new("__phantom", struct_name.span()), |  | ||||||
|             struct_name, |  | ||||||
|             vis, |  | ||||||
|             fields, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl BuilderWithFields { |  | ||||||
|     pub(crate) fn get_field(&self, name: &Member) -> Option<(usize, &BuilderField)> { |  | ||||||
|         let index = self |  | ||||||
|             .fields |  | ||||||
|             .binary_search_by_key(&&*name.to_token_stream().to_string(), |v| &*v.0) |  | ||||||
|             .ok()?; |  | ||||||
|         Some((index, &self.fields[index].1)) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn ty( |  | ||||||
|         &self, |  | ||||||
|         specified_fields: impl IntoIterator<Item = (Member, Type)>, |  | ||||||
|         phantom_type: Option<&Type>, |  | ||||||
|         other_fields_are_any_type: bool, |  | ||||||
|     ) -> TypePath { |  | ||||||
|         let Self { |  | ||||||
|             struct_name, |  | ||||||
|             vis: _, |  | ||||||
|             phantom_type_param, |  | ||||||
|             phantom_type_field: _, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         let span = struct_name.span(); |  | ||||||
|         let mut arguments = |  | ||||||
|             Vec::from_iter(fields.iter().map(|(_, BuilderField { type_param, .. })| { |  | ||||||
|                 if other_fields_are_any_type { |  | ||||||
|                     parse_quote_spanned! {span=> |  | ||||||
|                         #type_param |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     parse_quote_spanned! {span=> |  | ||||||
|                         () |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             })); |  | ||||||
|         for (name, ty) in specified_fields { |  | ||||||
|             let Some((index, _)) = self.get_field(&name) else { |  | ||||||
|                 panic!("field not found: {}", name.to_token_stream()); |  | ||||||
|             }; |  | ||||||
|             arguments[index] = ty; |  | ||||||
|         } |  | ||||||
|         let phantom_type_param = phantom_type.is_none().then_some(phantom_type_param); |  | ||||||
|         parse_quote_spanned! {span=> |  | ||||||
|             #struct_name::<#phantom_type_param #phantom_type #(, #arguments)*> |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub(crate) fn append_generics( |  | ||||||
|         &self, |  | ||||||
|         specified_fields: impl IntoIterator<Item = Member>, |  | ||||||
|         has_phantom_type_param: bool, |  | ||||||
|         other_fields_are_any_type: bool, |  | ||||||
|         generics: &mut Generics, |  | ||||||
|     ) { |  | ||||||
|         let Self { |  | ||||||
|             struct_name: _, |  | ||||||
|             vis: _, |  | ||||||
|             phantom_type_param, |  | ||||||
|             phantom_type_field: _, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         if has_phantom_type_param { |  | ||||||
|             generics.params.push(GenericParam::from(TypeParam::from( |  | ||||||
|                 phantom_type_param.clone(), |  | ||||||
|             ))); |  | ||||||
|         } |  | ||||||
|         if !other_fields_are_any_type { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         let mut type_params = Vec::from_iter( |  | ||||||
|             fields |  | ||||||
|                 .iter() |  | ||||||
|                 .map(|(_, BuilderField { type_param, .. })| Some(type_param)), |  | ||||||
|         ); |  | ||||||
|         for name in specified_fields { |  | ||||||
|             let Some((index, _)) = self.get_field(&name) else { |  | ||||||
|                 panic!("field not found: {}", name.to_token_stream()); |  | ||||||
|             }; |  | ||||||
|             type_params[index] = None; |  | ||||||
|         } |  | ||||||
|         generics.params.extend( |  | ||||||
|             type_params |  | ||||||
|                 .into_iter() |  | ||||||
|                 .filter_map(|v| Some(GenericParam::from(TypeParam::from(v?.clone())))), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     pub(crate) fn make_build_method( |  | ||||||
|         &self, |  | ||||||
|         build_fn_name: &Ident, |  | ||||||
|         specified_fields: impl IntoIterator<Item = (Member, Type)>, |  | ||||||
|         generics: &Generics, |  | ||||||
|         phantom_type: &Type, |  | ||||||
|         return_ty: &Type, |  | ||||||
|         mut body: Block, |  | ||||||
|     ) -> ItemImpl { |  | ||||||
|         let Self { |  | ||||||
|             struct_name, |  | ||||||
|             vis, |  | ||||||
|             phantom_type_param: _, |  | ||||||
|             phantom_type_field, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         let span = struct_name.span(); |  | ||||||
|         let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name)); |  | ||||||
|         let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); |  | ||||||
|         let empty_arg = parse_quote_spanned! {span=> |  | ||||||
|             () |  | ||||||
|         }; |  | ||||||
|         let mut ty_arguments = vec![empty_arg; fields.len()]; |  | ||||||
|         let empty_field_pat = quote_spanned! {span=> |  | ||||||
|             : _ |  | ||||||
|         }; |  | ||||||
|         let mut field_pats = vec![Some(empty_field_pat); fields.len()]; |  | ||||||
|         for (name, ty) in specified_fields { |  | ||||||
|             let Some((index, _)) = self.get_field(&name) else { |  | ||||||
|                 panic!("field not found: {}", name.to_token_stream()); |  | ||||||
|             }; |  | ||||||
|             ty_arguments[index] = ty; |  | ||||||
|             field_pats[index] = None; |  | ||||||
|         } |  | ||||||
|         body.stmts.insert( |  | ||||||
|             0, |  | ||||||
|             parse_quote_spanned! {span=> |  | ||||||
|                 let Self { |  | ||||||
|                     #(#field_names #field_pats,)* |  | ||||||
|                     #phantom_type_field: _, |  | ||||||
|                 } = self; |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|         parse_quote_spanned! {span=> |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics #struct_name<#phantom_type #(, #ty_arguments)*> |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 #[allow(non_snake_case, dead_code)] |  | ||||||
|                 #vis fn #build_fn_name(self) -> #return_ty |  | ||||||
|                 #body |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for BuilderWithFields { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         let Self { |  | ||||||
|             struct_name, |  | ||||||
|             vis, |  | ||||||
|             phantom_type_param, |  | ||||||
|             phantom_type_field, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         let span = struct_name.span(); |  | ||||||
|         let mut any_generics = Generics::default(); |  | ||||||
|         self.append_generics([], true, true, &mut any_generics); |  | ||||||
|         let empty_ty = self.ty([], None, false); |  | ||||||
|         let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name)); |  | ||||||
|         let field_type_params = Vec::from_iter(fields.iter().map(|v| &v.1.type_param)); |  | ||||||
|         quote_spanned! {span=> |  | ||||||
|             #[allow(non_camel_case_types)] |  | ||||||
|             #[non_exhaustive] |  | ||||||
|             #vis struct #struct_name #any_generics { |  | ||||||
|                 #(#field_names: #field_type_params,)* |  | ||||||
|                 #phantom_type_field: ::fayalite::__std::marker::PhantomData<#phantom_type_param>, |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl<#phantom_type_param> #empty_ty { |  | ||||||
|                 fn new() -> Self { |  | ||||||
|                     Self { |  | ||||||
|                         #(#field_names: (),)* |  | ||||||
|                         #phantom_type_field: ::fayalite::__std::marker::PhantomData, |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|         for (field_index, (_, field)) in self.fields.iter().enumerate() { |  | ||||||
|             let initial_fields = &fields[..field_index]; |  | ||||||
|             let final_fields = &fields[field_index..][1..]; |  | ||||||
|             let initial_type_params = |  | ||||||
|                 Vec::from_iter(initial_fields.iter().map(|v| &v.1.type_param)); |  | ||||||
|             let final_type_params = Vec::from_iter(final_fields.iter().map(|v| &v.1.type_param)); |  | ||||||
|             let initial_field_names = |  | ||||||
|                 Vec::from_iter(initial_fields.iter().map(|v| &v.1.builder_field_name)); |  | ||||||
|             let final_field_names = |  | ||||||
|                 Vec::from_iter(final_fields.iter().map(|v| &v.1.builder_field_name)); |  | ||||||
|             let BuilderField { |  | ||||||
|                 names: _, |  | ||||||
|                 mapped_value, |  | ||||||
|                 mapped_type, |  | ||||||
|                 where_clause, |  | ||||||
|                 builder_field_name, |  | ||||||
|                 type_param, |  | ||||||
|             } = field; |  | ||||||
|             quote_spanned! {span=> |  | ||||||
|                 #[automatically_derived] |  | ||||||
|                 #[allow(non_camel_case_types, dead_code)] |  | ||||||
|                 impl<#phantom_type_param #(, #initial_type_params)* #(, #final_type_params)*> |  | ||||||
|                     #struct_name< |  | ||||||
|                         #phantom_type_param, |  | ||||||
|                         #(#initial_type_params,)* |  | ||||||
|                         (), #(#final_type_params,)* |  | ||||||
|                     > |  | ||||||
|                 { |  | ||||||
|                     #vis fn #builder_field_name<#type_param>( |  | ||||||
|                         self, |  | ||||||
|                         #builder_field_name: #type_param, |  | ||||||
|                     ) -> #struct_name< |  | ||||||
|                             #phantom_type_param, |  | ||||||
|                             #(#initial_type_params,)* |  | ||||||
|                             #mapped_type, |  | ||||||
|                             #(#final_type_params,)* |  | ||||||
|                         > |  | ||||||
|                     #where_clause |  | ||||||
|                     { |  | ||||||
|                         let Self { |  | ||||||
|                             #(#initial_field_names,)* |  | ||||||
|                             #builder_field_name: (), |  | ||||||
|                             #(#final_field_names,)* |  | ||||||
|                             #phantom_type_field: _, |  | ||||||
|                         } = self; |  | ||||||
|                         let #builder_field_name = #mapped_value; |  | ||||||
|                         #struct_name { |  | ||||||
|                             #(#field_names,)* |  | ||||||
|                             #phantom_type_field: ::fayalite::__std::marker::PhantomData, |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) struct MapIdents { |  | ||||||
|     pub(crate) map: HashMap<Ident, Ident>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Fold for &MapIdents { |  | ||||||
|     fn fold_ident(&mut self, i: Ident) -> Ident { |  | ||||||
|         self.map.get(&i).cloned().unwrap_or(i) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) struct DupGenerics<M> { |  | ||||||
|     pub(crate) combined: Generics, |  | ||||||
|     pub(crate) maps: M, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn merge_punctuated<T, P: Default>( |  | ||||||
|     target: &mut Punctuated<T, P>, |  | ||||||
|     source: Punctuated<T, P>, |  | ||||||
|     make_punct: impl FnOnce() -> P, |  | ||||||
| ) { |  | ||||||
|     if source.is_empty() { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     if target.is_empty() { |  | ||||||
|         *target = source; |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     if !target.trailing_punct() { |  | ||||||
|         target.push_punct(make_punct()); |  | ||||||
|     } |  | ||||||
|     target.extend(source.into_pairs()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn merge_generics(target: &mut Generics, source: Generics) { |  | ||||||
|     let Generics { |  | ||||||
|         lt_token, |  | ||||||
|         params, |  | ||||||
|         gt_token, |  | ||||||
|         where_clause, |  | ||||||
|     } = source; |  | ||||||
|     let span = lt_token.map(|v| v.span).unwrap_or_else(|| params.span()); |  | ||||||
|     target.lt_token = target.lt_token.or(lt_token); |  | ||||||
|     merge_punctuated(&mut target.params, params, || Token); |  | ||||||
|     target.gt_token = target.gt_token.or(gt_token); |  | ||||||
|     if let Some(where_clause) = where_clause { |  | ||||||
|         if let Some(target_where_clause) = &mut target.where_clause { |  | ||||||
|             let WhereClause { |  | ||||||
|                 where_token, |  | ||||||
|                 predicates, |  | ||||||
|             } = where_clause; |  | ||||||
|             let span = where_token.span; |  | ||||||
|             target_where_clause.where_token = where_token; |  | ||||||
|             merge_punctuated(&mut target_where_clause.predicates, predicates, || { |  | ||||||
|                 Token |  | ||||||
|             }); |  | ||||||
|         } else { |  | ||||||
|             target.where_clause = Some(where_clause); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl DupGenerics<Vec<MapIdents>> { |  | ||||||
|     pub(crate) fn new_dyn(generics: &Generics, count: usize) -> Self { |  | ||||||
|         let mut maps = Vec::from_iter((0..count).map(|_| MapIdents { |  | ||||||
|             map: HashMap::new(), |  | ||||||
|         })); |  | ||||||
|         for param in &generics.params { |  | ||||||
|             let (GenericParam::Lifetime(LifetimeParam { |  | ||||||
|                 lifetime: Lifetime { ident, .. }, |  | ||||||
|                 .. |  | ||||||
|             }) |  | ||||||
|             | GenericParam::Type(TypeParam { ident, .. }) |  | ||||||
|             | GenericParam::Const(ConstParam { ident, .. })) = param; |  | ||||||
|             for (i, map_idents) in maps.iter_mut().enumerate() { |  | ||||||
|                 map_idents |  | ||||||
|                     .map |  | ||||||
|                     .insert(ident.clone(), format_ident!("__{}_{}", ident, i)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         let mut combined = Generics::default(); |  | ||||||
|         for map_idents in maps.iter() { |  | ||||||
|             merge_generics( |  | ||||||
|                 &mut combined, |  | ||||||
|                 fold_generics(&mut { map_idents }, generics.clone()), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         Self { combined, maps } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<const COUNT: usize> DupGenerics<[MapIdents; COUNT]> { |  | ||||||
|     pub(crate) fn new(generics: &Generics) -> Self { |  | ||||||
|         let DupGenerics { combined, maps } = DupGenerics::new_dyn(generics, COUNT); |  | ||||||
|         Self { |  | ||||||
|             combined, |  | ||||||
|             maps: maps.try_into().ok().unwrap(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn add_where_predicate( |  | ||||||
|     target: &mut Generics, |  | ||||||
|     span: Span, |  | ||||||
|     where_predicate: WherePredicate, |  | ||||||
| ) { |  | ||||||
|     let WhereClause { |  | ||||||
|         where_token: _, |  | ||||||
|         predicates, |  | ||||||
|     } = target.where_clause.get_or_insert_with(|| WhereClause { |  | ||||||
|         where_token: Token, |  | ||||||
|         predicates: Punctuated::new(), |  | ||||||
|     }); |  | ||||||
|     if !predicates.empty_or_trailing() { |  | ||||||
|         predicates.push_punct(Token); |  | ||||||
|     } |  | ||||||
|     predicates.push_value(where_predicate); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn make_connect_impl( |  | ||||||
|     connect_inexact: Option<(crate::kw::connect_inexact,)>, |  | ||||||
|     generics: &Generics, |  | ||||||
|     ty_ident: &Ident, |  | ||||||
|     field_types: impl IntoIterator<Item = Type>, |  | ||||||
| ) -> TokenStream { |  | ||||||
|     let span = ty_ident.span(); |  | ||||||
|     let impl_generics; |  | ||||||
|     let combined_generics; |  | ||||||
|     let where_clause; |  | ||||||
|     let lhs_generics; |  | ||||||
|     let lhs_type_generics; |  | ||||||
|     let rhs_generics; |  | ||||||
|     let rhs_type_generics; |  | ||||||
|     if connect_inexact.is_some() { |  | ||||||
|         let DupGenerics { |  | ||||||
|             mut combined, |  | ||||||
|             maps: [lhs_map, rhs_map], |  | ||||||
|         } = DupGenerics::new(generics); |  | ||||||
|         for field_type in field_types { |  | ||||||
|             let lhs_type = (&lhs_map).fold_type(field_type.clone()); |  | ||||||
|             let rhs_type = (&rhs_map).fold_type(field_type); |  | ||||||
|             add_where_predicate( |  | ||||||
|                 &mut combined, |  | ||||||
|                 span, |  | ||||||
|                 parse_quote_spanned! {span=> |  | ||||||
|                     #lhs_type: ::fayalite::ty::Connect<#rhs_type> |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         combined_generics = combined; |  | ||||||
|         (impl_generics, _, where_clause) = combined_generics.split_for_impl(); |  | ||||||
|         lhs_generics = (&lhs_map).fold_generics(generics.clone()); |  | ||||||
|         (_, lhs_type_generics, _) = lhs_generics.split_for_impl(); |  | ||||||
|         rhs_generics = (&rhs_map).fold_generics(generics.clone()); |  | ||||||
|         (_, rhs_type_generics, _) = rhs_generics.split_for_impl(); |  | ||||||
|     } else { |  | ||||||
|         let mut generics = generics.clone(); |  | ||||||
|         for field_type in field_types { |  | ||||||
|             add_where_predicate( |  | ||||||
|                 &mut generics, |  | ||||||
|                 span, |  | ||||||
|                 parse_quote_spanned! {span=> |  | ||||||
|                     #field_type: ::fayalite::ty::Connect<#field_type> |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         combined_generics = generics; |  | ||||||
|         (impl_generics, lhs_type_generics, where_clause) = combined_generics.split_for_impl(); |  | ||||||
|         rhs_type_generics = lhs_type_generics.clone(); |  | ||||||
|     } |  | ||||||
|     quote_spanned! {span=> |  | ||||||
|         #[automatically_derived] |  | ||||||
|         #[allow(non_camel_case_types)] |  | ||||||
|         impl #impl_generics ::fayalite::ty::Connect<#ty_ident #rhs_type_generics> |  | ||||||
|             for #ty_ident #lhs_type_generics |  | ||||||
|         #where_clause |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,969 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| use crate::{ |  | ||||||
|     value_derive_common::{ |  | ||||||
|         append_field, derive_clone_hash_eq_partialeq_for_struct, get_field_names, get_target, |  | ||||||
|         make_connect_impl, Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics, |  | ||||||
|     }, |  | ||||||
|     value_derive_struct::{self, ParsedStruct, ParsedStructNames, StructOptions}, |  | ||||||
|     Errors, HdlAttr, |  | ||||||
| }; |  | ||||||
| use proc_macro2::TokenStream; |  | ||||||
| use quote::{format_ident, quote, quote_spanned, ToTokens}; |  | ||||||
| use syn::{ |  | ||||||
|     parse_quote, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace, |  | ||||||
|     Field, FieldMutability, Fields, FieldsNamed, Generics, Ident, Index, ItemEnum, ItemStruct, |  | ||||||
|     Member, Path, Token, Type, Variant, Visibility, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| crate::options! { |  | ||||||
|     #[options = EnumOptions] |  | ||||||
|     enum EnumOption { |  | ||||||
|         OutlineGenerated(outline_generated), |  | ||||||
|         ConnectInexact(connect_inexact), |  | ||||||
|         Bounds(where_, Bounds), |  | ||||||
|         Target(target, Path), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| crate::options! { |  | ||||||
|     #[options = VariantOptions] |  | ||||||
|     enum VariantOption {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| crate::options! { |  | ||||||
|     #[options = FieldOptions] |  | ||||||
|     enum FieldOption {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| enum VariantValue { |  | ||||||
|     None, |  | ||||||
|     Direct { |  | ||||||
|         value_type: Type, |  | ||||||
|     }, |  | ||||||
|     Struct { |  | ||||||
|         value_struct: ItemStruct, |  | ||||||
|         parsed_struct: ParsedStruct, |  | ||||||
|     }, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl VariantValue { |  | ||||||
|     fn is_none(&self) -> bool { |  | ||||||
|         matches!(self, Self::None) |  | ||||||
|     } |  | ||||||
|     fn value_ty(&self) -> Option<Type> { |  | ||||||
|         match self { |  | ||||||
|             VariantValue::None => None, |  | ||||||
|             VariantValue::Direct { value_type } => Some(value_type.clone()), |  | ||||||
|             VariantValue::Struct { value_struct, .. } => { |  | ||||||
|                 let (_, type_generics, _) = value_struct.generics.split_for_impl(); |  | ||||||
|                 let ident = &value_struct.ident; |  | ||||||
|                 Some(parse_quote! { #ident #type_generics }) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct ParsedVariant { |  | ||||||
|     options: HdlAttr<VariantOptions>, |  | ||||||
|     ident: Ident, |  | ||||||
|     fields_kind: FieldsKind, |  | ||||||
|     fields: Vec<ParsedField<FieldOptions>>, |  | ||||||
|     value: VariantValue, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ParsedVariant { |  | ||||||
|     fn parse( |  | ||||||
|         errors: &mut Errors, |  | ||||||
|         variant: Variant, |  | ||||||
|         enum_options: &EnumOptions, |  | ||||||
|         enum_vis: &Visibility, |  | ||||||
|         enum_ident: &Ident, |  | ||||||
|         enum_generics: &Generics, |  | ||||||
|     ) -> Self { |  | ||||||
|         let target = get_target(&enum_options.target, enum_ident); |  | ||||||
|         let Variant { |  | ||||||
|             mut attrs, |  | ||||||
|             ident, |  | ||||||
|             fields, |  | ||||||
|             discriminant, |  | ||||||
|         } = variant; |  | ||||||
|         if let Some((eq, _)) = discriminant { |  | ||||||
|             errors.error(eq, "#[derive(Value)]: discriminants not allowed"); |  | ||||||
|         } |  | ||||||
|         let variant_options = errors |  | ||||||
|             .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) |  | ||||||
|             .unwrap_or_default(); |  | ||||||
|         let (fields_kind, parsed_fields) = |  | ||||||
|             ParsedField::parse_fields(errors, &mut fields.clone(), true); |  | ||||||
|         let value = match (&fields_kind, &*parsed_fields) { |  | ||||||
|             (FieldsKind::Unit, _) => VariantValue::None, |  | ||||||
|             ( |  | ||||||
|                 FieldsKind::Unnamed(_), |  | ||||||
|                 [ParsedField { |  | ||||||
|                     options, |  | ||||||
|                     vis: _, |  | ||||||
|                     name: Member::Unnamed(Index { index: 0, span: _ }), |  | ||||||
|                     ty, |  | ||||||
|                 }], |  | ||||||
|             ) => { |  | ||||||
|                 let FieldOptions {} = options.body; |  | ||||||
|                 VariantValue::Direct { |  | ||||||
|                     value_type: ty.clone(), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             _ => { |  | ||||||
|                 let variant_value_struct_ident = |  | ||||||
|                     format_ident!("__{}__{}", enum_ident, ident, span = ident.span()); |  | ||||||
|                 let variant_type_struct_ident = |  | ||||||
|                     format_ident!("__{}__{}__Type", enum_ident, ident, span = ident.span()); |  | ||||||
|                 let mut value_struct_fields = fields.clone(); |  | ||||||
|                 let (_, type_generics, _) = enum_generics.split_for_impl(); |  | ||||||
|                 append_field( |  | ||||||
|                     &mut value_struct_fields, |  | ||||||
|                     Field { |  | ||||||
|                         attrs: vec![HdlAttr::from(value_derive_struct::FieldOptions { |  | ||||||
|                             flip: None, |  | ||||||
|                             skip: Some(Default::default()), |  | ||||||
|                         }) |  | ||||||
|                         .to_attr()], |  | ||||||
|                         vis: enum_vis.clone(), |  | ||||||
|                         mutability: FieldMutability::None, |  | ||||||
|                         ident: Some(Ident::new("__phantom", ident.span())), |  | ||||||
|                         colon_token: None, |  | ||||||
|                         ty: parse_quote_spanned! {ident.span()=> |  | ||||||
|                             ::fayalite::__std::marker::PhantomData<#target #type_generics> |  | ||||||
|                         }, |  | ||||||
|                     }, |  | ||||||
|                 ); |  | ||||||
|                 let (value_struct_fields_kind, value_struct_parsed_fields) = |  | ||||||
|                     ParsedField::parse_fields(errors, &mut value_struct_fields, false); |  | ||||||
|                 let value_struct = ItemStruct { |  | ||||||
|                     attrs: vec![parse_quote! { #[allow(non_camel_case_types)] }], |  | ||||||
|                     vis: enum_vis.clone(), |  | ||||||
|                     struct_token: Token), |  | ||||||
|                     ident: variant_value_struct_ident.clone(), |  | ||||||
|                     generics: enum_generics.clone(), |  | ||||||
|                     fields: value_struct_fields, |  | ||||||
|                     semi_token: None, |  | ||||||
|                 }; |  | ||||||
|                 VariantValue::Struct { |  | ||||||
|                     value_struct, |  | ||||||
|                     parsed_struct: ParsedStruct { |  | ||||||
|                         options: StructOptions { |  | ||||||
|                             outline_generated: None, |  | ||||||
|                             static_: Some(Default::default()), |  | ||||||
|                             where_: Some(( |  | ||||||
|                                 Default::default(), |  | ||||||
|                                 Default::default(), |  | ||||||
|                                 ValueDeriveGenerics::get( |  | ||||||
|                                     enum_generics.clone(), |  | ||||||
|                                     &enum_options.where_, |  | ||||||
|                                 ) |  | ||||||
|                                 .static_type_generics |  | ||||||
|                                 .where_clause |  | ||||||
|                                 .into(), |  | ||||||
|                             )), |  | ||||||
|                             target: None, |  | ||||||
|                             connect_inexact: enum_options.connect_inexact, |  | ||||||
|                         } |  | ||||||
|                         .into(), |  | ||||||
|                         vis: enum_vis.clone(), |  | ||||||
|                         struct_token: Default::default(), |  | ||||||
|                         generics: enum_generics.clone(), |  | ||||||
|                         fields_kind: value_struct_fields_kind, |  | ||||||
|                         fields: value_struct_parsed_fields, |  | ||||||
|                         semi_token: None, // it will fill in the semicolon if needed
 |  | ||||||
|                         skip_check_fields: true, |  | ||||||
|                         names: ParsedStructNames { |  | ||||||
|                             ident: variant_value_struct_ident.clone(), |  | ||||||
|                             type_struct_debug_ident: Some(format!("{enum_ident}::{ident}::Type")), |  | ||||||
|                             type_struct_ident: variant_type_struct_ident, |  | ||||||
|                             match_variant_ident: None, |  | ||||||
|                             builder_struct_ident: None, |  | ||||||
|                             mask_match_variant_ident: None, |  | ||||||
|                             mask_type_ident: None, |  | ||||||
|                             mask_type_debug_ident: Some(format!( |  | ||||||
|                                 "AsMask<{enum_ident}::{ident}>::Type" |  | ||||||
|                             )), |  | ||||||
|                             mask_value_ident: None, |  | ||||||
|                             mask_value_debug_ident: Some(format!("AsMask<{enum_ident}::{ident}>")), |  | ||||||
|                             mask_builder_struct_ident: None, |  | ||||||
|                         }, |  | ||||||
|                     }, |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         ParsedVariant { |  | ||||||
|             options: variant_options, |  | ||||||
|             ident, |  | ||||||
|             fields_kind, |  | ||||||
|             fields: parsed_fields, |  | ||||||
|             value, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct ParsedEnum { |  | ||||||
|     options: HdlAttr<EnumOptions>, |  | ||||||
|     vis: Visibility, |  | ||||||
|     enum_token: Token![enum], |  | ||||||
|     ident: Ident, |  | ||||||
|     generics: Generics, |  | ||||||
|     brace_token: Brace, |  | ||||||
|     variants: Vec<ParsedVariant>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ParsedEnum { |  | ||||||
|     fn parse(item: ItemEnum) -> syn::Result<Self> { |  | ||||||
|         let ItemEnum { |  | ||||||
|             mut attrs, |  | ||||||
|             vis, |  | ||||||
|             enum_token, |  | ||||||
|             ident, |  | ||||||
|             generics, |  | ||||||
|             brace_token, |  | ||||||
|             variants, |  | ||||||
|         } = item; |  | ||||||
|         let mut errors = Errors::new(); |  | ||||||
|         let enum_options = errors |  | ||||||
|             .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) |  | ||||||
|             .unwrap_or_default(); |  | ||||||
|         let variants = variants |  | ||||||
|             .into_iter() |  | ||||||
|             .map(|variant| { |  | ||||||
|                 ParsedVariant::parse( |  | ||||||
|                     &mut errors, |  | ||||||
|                     variant, |  | ||||||
|                     &enum_options.body, |  | ||||||
|                     &vis, |  | ||||||
|                     &ident, |  | ||||||
|                     &generics, |  | ||||||
|                 ) |  | ||||||
|             }) |  | ||||||
|             .collect(); |  | ||||||
|         errors.finish()?; |  | ||||||
|         Ok(ParsedEnum { |  | ||||||
|             options: enum_options, |  | ||||||
|             vis, |  | ||||||
|             enum_token, |  | ||||||
|             ident, |  | ||||||
|             generics, |  | ||||||
|             brace_token, |  | ||||||
|             variants, |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for ParsedEnum { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         let Self { |  | ||||||
|             options, |  | ||||||
|             vis, |  | ||||||
|             enum_token, |  | ||||||
|             ident: enum_ident, |  | ||||||
|             generics: enum_generics, |  | ||||||
|             brace_token, |  | ||||||
|             variants, |  | ||||||
|         } = self; |  | ||||||
|         let EnumOptions { |  | ||||||
|             outline_generated: _, |  | ||||||
|             connect_inexact, |  | ||||||
|             where_, |  | ||||||
|             target, |  | ||||||
|         } = &options.body; |  | ||||||
|         let target = get_target(target, enum_ident); |  | ||||||
|         let ValueDeriveGenerics { |  | ||||||
|             generics: _, |  | ||||||
|             static_type_generics, |  | ||||||
|         } = ValueDeriveGenerics::get(enum_generics.clone(), where_); |  | ||||||
|         let (static_type_impl_generics, static_type_type_generics, static_type_where_clause) = |  | ||||||
|             static_type_generics.split_for_impl(); |  | ||||||
|         let type_struct_ident = format_ident!("__{}__Type", enum_ident); |  | ||||||
|         let mut field_checks = vec![]; |  | ||||||
|         let mut make_type_struct_variant_type = |variant: &ParsedVariant| { |  | ||||||
|             let VariantOptions {} = variant.options.body; |  | ||||||
|             let (value_struct, parsed_struct) = match &variant.value { |  | ||||||
|                 VariantValue::None => { |  | ||||||
|                     return None; |  | ||||||
|                 } |  | ||||||
|                 VariantValue::Direct { value_type } => { |  | ||||||
|                     field_checks.push(quote_spanned! {value_type.span()=> |  | ||||||
|                         __check_field::<#value_type>(); |  | ||||||
|                     }); |  | ||||||
|                     return Some(parse_quote! { <#value_type as ::fayalite::expr::ToExpr>::Type }); |  | ||||||
|                 } |  | ||||||
|                 VariantValue::Struct { |  | ||||||
|                     value_struct, |  | ||||||
|                     parsed_struct, |  | ||||||
|                 } => (value_struct, parsed_struct), |  | ||||||
|             }; |  | ||||||
|             value_struct.to_tokens(tokens); |  | ||||||
|             parsed_struct.to_tokens(tokens); |  | ||||||
|             let mut field_names = Vec::from_iter(get_field_names(&value_struct.fields)); |  | ||||||
|             derive_clone_hash_eq_partialeq_for_struct( |  | ||||||
|                 &value_struct.ident, |  | ||||||
|                 &static_type_generics, |  | ||||||
|                 &field_names, |  | ||||||
|             ) |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|             field_names = Vec::from_iter( |  | ||||||
|                 field_names |  | ||||||
|                     .into_iter() |  | ||||||
|                     .zip(parsed_struct.fields.iter()) |  | ||||||
|                     .filter_map(|(member, field)| { |  | ||||||
|                         field.options.body.skip.is_none().then_some(member) |  | ||||||
|                     }), |  | ||||||
|             ); |  | ||||||
|             let field_name_strs = |  | ||||||
|                 Vec::from_iter(field_names.iter().map(|v| v.to_token_stream().to_string())); |  | ||||||
|             let debug_ident = format!("{enum_ident}::{}", variant.ident); |  | ||||||
|             let debug_body = match variant.fields_kind { |  | ||||||
|                 FieldsKind::Unit => quote! { |  | ||||||
|                     f.debug_struct(#debug_ident).finish() |  | ||||||
|                 }, |  | ||||||
|                 FieldsKind::Named(_) => quote! { |  | ||||||
|                     f.debug_struct(#debug_ident) |  | ||||||
|                         #(.field(#field_name_strs, &self.#field_names))* |  | ||||||
|                         .finish() |  | ||||||
|                 }, |  | ||||||
|                 FieldsKind::Unnamed(_) => quote! { |  | ||||||
|                     f.debug_tuple(#debug_ident)#(.field(&self.#field_names))*.finish() |  | ||||||
|                 }, |  | ||||||
|             }; |  | ||||||
|             let value_struct_ident = &value_struct.ident; |  | ||||||
|             quote! { |  | ||||||
|                 #[automatically_derived] |  | ||||||
|                 impl #static_type_impl_generics ::fayalite::__std::fmt::Debug |  | ||||||
|                     for #value_struct_ident #static_type_type_generics |  | ||||||
|                 #static_type_where_clause |  | ||||||
|                 { |  | ||||||
|                     fn fmt( |  | ||||||
|                         &self, |  | ||||||
|                         f: &mut ::fayalite::__std::fmt::Formatter<'_>, |  | ||||||
|                     ) -> ::fayalite::__std::fmt::Result { |  | ||||||
|                         #debug_body |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|             Some(parse_quote! { |  | ||||||
|                 < |  | ||||||
|                     #value_struct_ident #static_type_type_generics |  | ||||||
|                     as ::fayalite::expr::ToExpr |  | ||||||
|                 >::Type |  | ||||||
|             }) |  | ||||||
|         }; |  | ||||||
|         let type_struct_variants = Punctuated::from_iter(variants.iter().filter_map(|variant| { |  | ||||||
|             let VariantOptions {} = variant.options.body; |  | ||||||
|             Some(Field { |  | ||||||
|                 attrs: vec![], |  | ||||||
|                 vis: vis.clone(), |  | ||||||
|                 mutability: FieldMutability::None, |  | ||||||
|                 ident: Some(variant.ident.clone()), |  | ||||||
|                 colon_token: None, // it will fill in the colon if needed
 |  | ||||||
|                 ty: make_type_struct_variant_type(variant)?, |  | ||||||
|             }) |  | ||||||
|         })); |  | ||||||
|         let type_struct = ItemStruct { |  | ||||||
|             attrs: vec![ |  | ||||||
|                 parse_quote! {#[allow(non_camel_case_types)]}, |  | ||||||
|                 parse_quote! {#[allow(non_snake_case)]}, |  | ||||||
|             ], |  | ||||||
|             vis: vis.clone(), |  | ||||||
|             struct_token: Token, |  | ||||||
|             ident: type_struct_ident, |  | ||||||
|             generics: static_type_generics.clone(), |  | ||||||
|             fields: Fields::Named(FieldsNamed { |  | ||||||
|                 brace_token: *brace_token, |  | ||||||
|                 named: type_struct_variants, |  | ||||||
|             }), |  | ||||||
|             semi_token: None, |  | ||||||
|         }; |  | ||||||
|         let type_struct_ident = &type_struct.ident; |  | ||||||
|         let type_struct_debug_ident = format!("{enum_ident}::Type"); |  | ||||||
|         type_struct.to_tokens(tokens); |  | ||||||
|         let non_empty_variant_names = Vec::from_iter( |  | ||||||
|             variants |  | ||||||
|                 .iter() |  | ||||||
|                 .filter(|v| !v.value.is_none()) |  | ||||||
|                 .map(|v| v.ident.clone()), |  | ||||||
|         ); |  | ||||||
|         let non_empty_variant_name_strs = |  | ||||||
|             Vec::from_iter(non_empty_variant_names.iter().map(|v| v.to_string())); |  | ||||||
|         let debug_type_body = quote! { |  | ||||||
|             f.debug_struct(#type_struct_debug_ident) |  | ||||||
|                 #(.field(#non_empty_variant_name_strs, &self.#non_empty_variant_names))* |  | ||||||
|                 .finish() |  | ||||||
|         }; |  | ||||||
|         derive_clone_hash_eq_partialeq_for_struct( |  | ||||||
|             type_struct_ident, |  | ||||||
|             &static_type_generics, |  | ||||||
|             &non_empty_variant_names, |  | ||||||
|         ) |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|         let variant_names = Vec::from_iter(variants.iter().map(|v| &v.ident)); |  | ||||||
|         let variant_name_strs = Vec::from_iter(variant_names.iter().map(|v| v.to_string())); |  | ||||||
|         let (variant_field_pats, variant_to_canonical_values): (Vec<_>, Vec<_>) = variants |  | ||||||
|             .iter() |  | ||||||
|             .map(|v| { |  | ||||||
|                 let field_names: Vec<_> = v.fields.iter().map(|field| &field.name).collect(); |  | ||||||
|                 let var_names: Vec<_> = v.fields.iter().map(|field| field.var_name()).collect(); |  | ||||||
|                 let field_pats = quote! { |  | ||||||
|                     #(#field_names: #var_names,)* |  | ||||||
|                 }; |  | ||||||
|                 let to_canonical_value = match &v.value { |  | ||||||
|                     VariantValue::None => quote! { ::fayalite::__std::option::Option::None }, |  | ||||||
|                     VariantValue::Direct { .. } => { |  | ||||||
|                         debug_assert_eq!(var_names.len(), 1); |  | ||||||
|                         quote! { |  | ||||||
|                             ::fayalite::__std::option::Option::Some( |  | ||||||
|                                 ::fayalite::ty::DynValueTrait::to_canonical_dyn(#(#var_names)*), |  | ||||||
|                             ) |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     VariantValue::Struct { |  | ||||||
|                         value_struct, |  | ||||||
|                         parsed_struct, |  | ||||||
|                     } => { |  | ||||||
|                         let value_struct_ident = &value_struct.ident; |  | ||||||
|                         let phantom_field_name = &parsed_struct |  | ||||||
|                             .fields |  | ||||||
|                             .last() |  | ||||||
|                             .expect("missing phantom field") |  | ||||||
|                             .name; |  | ||||||
|                         let type_generics = static_type_type_generics.as_turbofish(); |  | ||||||
|                         quote! { |  | ||||||
|                             ::fayalite::__std::option::Option::Some( |  | ||||||
|                                 ::fayalite::ty::DynValueTrait::to_canonical_dyn( |  | ||||||
|                                     &#value_struct_ident #type_generics { |  | ||||||
|                                         #(#field_names: |  | ||||||
|                                             ::fayalite::__std::clone::Clone::clone(#var_names),)* |  | ||||||
|                                         #phantom_field_name: ::fayalite::__std::marker::PhantomData, |  | ||||||
|                                     }, |  | ||||||
|                                 ), |  | ||||||
|                             ) |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }; |  | ||||||
|                 (field_pats, to_canonical_value) |  | ||||||
|             }) |  | ||||||
|             .unzip(); |  | ||||||
|         let mut match_enum_variants = Punctuated::new(); |  | ||||||
|         let mut match_enum_debug_arms = vec![]; |  | ||||||
|         let mut match_enum_arms = vec![]; |  | ||||||
|         let mut variant_vars = vec![]; |  | ||||||
|         let mut from_canonical_type_variant_lets = vec![]; |  | ||||||
|         let mut non_empty_variant_vars = vec![]; |  | ||||||
|         let mut enum_type_variants = vec![]; |  | ||||||
|         let mut enum_type_variants_hint = vec![]; |  | ||||||
|         let match_enum_ident = format_ident!("__{}__MatchEnum", enum_ident); |  | ||||||
|         let mut builder = Builder::new(format_ident!("__{}__Builder", enum_ident), vis.clone()); |  | ||||||
|         for variant in variants.iter() { |  | ||||||
|             for field in variant.fields.iter() { |  | ||||||
|                 builder.insert_field( |  | ||||||
|                     field.name.clone(), |  | ||||||
|                     |v| { |  | ||||||
|                         parse_quote_spanned! {v.span()=> |  | ||||||
|                             ::fayalite::expr::ToExpr::to_expr(&#v) |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                     |t| { |  | ||||||
|                         parse_quote_spanned! {t.span()=> |  | ||||||
|                             ::fayalite::expr::Expr<< |  | ||||||
|                                 <#t as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                                 as ::fayalite::ty::Type |  | ||||||
|                             >::Value> |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                     |t| { |  | ||||||
|                         parse_quote_spanned! {t.span()=> |  | ||||||
|                             where |  | ||||||
|                                 #t: ::fayalite::expr::ToExpr, |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         let builder = builder.finish_filling_in_fields(); |  | ||||||
|         builder.to_tokens(tokens); |  | ||||||
|         for (variant_index, variant) in variants.iter().enumerate() { |  | ||||||
|             let variant_var = format_ident!("__v_{}", variant.ident); |  | ||||||
|             let variant_name = &variant.ident; |  | ||||||
|             let variant_name_str = variant.ident.to_string(); |  | ||||||
|             match_enum_variants.push(Variant { |  | ||||||
|                 attrs: vec![], |  | ||||||
|                 ident: variant.ident.clone(), |  | ||||||
|                 fields: variant.fields_kind.into_fields(variant.fields.iter().map( |  | ||||||
|                     |ParsedField { |  | ||||||
|                          options, |  | ||||||
|                          vis, |  | ||||||
|                          name, |  | ||||||
|                          ty, |  | ||||||
|                      }| { |  | ||||||
|                         let FieldOptions {} = options.body; |  | ||||||
|                         Field { |  | ||||||
|                             attrs: vec![], |  | ||||||
|                             vis: vis.clone(), |  | ||||||
|                             mutability: FieldMutability::None, |  | ||||||
|                             ident: if let Member::Named(name) = name { |  | ||||||
|                                 Some(name.clone()) |  | ||||||
|                             } else { |  | ||||||
|                                 None |  | ||||||
|                             }, |  | ||||||
|                             colon_token: None, |  | ||||||
|                             ty: parse_quote! { ::fayalite::expr::Expr<#ty> }, |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                 )), |  | ||||||
|                 discriminant: None, |  | ||||||
|             }); |  | ||||||
|             let match_enum_field_names = Vec::from_iter(variant.fields.iter().map( |  | ||||||
|                 |ParsedField { |  | ||||||
|                      options, |  | ||||||
|                      vis: _, |  | ||||||
|                      name, |  | ||||||
|                      ty: _, |  | ||||||
|                  }| { |  | ||||||
|                     let FieldOptions {} = options.body; |  | ||||||
|                     name |  | ||||||
|                 }, |  | ||||||
|             )); |  | ||||||
|             let match_enum_field_name_strs = Vec::from_iter(variant.fields.iter().map( |  | ||||||
|                 |ParsedField { |  | ||||||
|                      options, |  | ||||||
|                      vis: _, |  | ||||||
|                      name, |  | ||||||
|                      ty: _, |  | ||||||
|                  }| { |  | ||||||
|                     let FieldOptions {} = options.body; |  | ||||||
|                     name.to_token_stream().to_string() |  | ||||||
|                 }, |  | ||||||
|             )); |  | ||||||
|             let match_enum_debug_vars = Vec::from_iter(variant.fields.iter().map( |  | ||||||
|                 |ParsedField { |  | ||||||
|                      options, |  | ||||||
|                      vis: _, |  | ||||||
|                      name, |  | ||||||
|                      ty: _, |  | ||||||
|                  }| { |  | ||||||
|                     let FieldOptions {} = options.body; |  | ||||||
|                     format_ident!("__v_{}", name) |  | ||||||
|                 }, |  | ||||||
|             )); |  | ||||||
|             match_enum_debug_arms.push(match variant.fields_kind { |  | ||||||
|                 FieldsKind::Unit | FieldsKind::Named(_) => quote! { |  | ||||||
|                     Self::#variant_name { |  | ||||||
|                         #(#match_enum_field_names: ref #match_enum_debug_vars,)* |  | ||||||
|                     } => f.debug_struct(#variant_name_str) |  | ||||||
|                         #(.field(#match_enum_field_name_strs, #match_enum_debug_vars))* |  | ||||||
|                         .finish(), |  | ||||||
|                 }, |  | ||||||
|                 FieldsKind::Unnamed(_) => quote! { |  | ||||||
|                     Self::#variant_name( |  | ||||||
|                         #(ref #match_enum_debug_vars,)* |  | ||||||
|                     ) => f.debug_tuple(#variant_name_str) |  | ||||||
|                         #(.field(#match_enum_debug_vars))* |  | ||||||
|                         .finish(), |  | ||||||
|                 }, |  | ||||||
|             }); |  | ||||||
|             if let Some(value_ty) = variant.value.value_ty() { |  | ||||||
|                 from_canonical_type_variant_lets.push(quote! { |  | ||||||
|                     let #variant_var = |  | ||||||
|                         #variant_var.from_canonical_type_helper_has_value(#variant_name_str); |  | ||||||
|                 }); |  | ||||||
|                 non_empty_variant_vars.push(variant_var.clone()); |  | ||||||
|                 enum_type_variants.push(quote! { |  | ||||||
|                     ::fayalite::enum_::VariantType { |  | ||||||
|                         name: ::fayalite::intern::Intern::intern(#variant_name_str), |  | ||||||
|                         ty: ::fayalite::__std::option::Option::Some( |  | ||||||
|                             ::fayalite::ty::DynType::canonical_dyn(&self.#variant_name), |  | ||||||
|                         ), |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 enum_type_variants_hint.push(quote! { |  | ||||||
|                     ::fayalite::enum_::VariantType { |  | ||||||
|                         name: ::fayalite::intern::Intern::intern(#variant_name_str), |  | ||||||
|                         ty: ::fayalite::__std::option::Option::Some( |  | ||||||
|                             ::fayalite::bundle::TypeHint::< |  | ||||||
|                                 <#value_ty as ::fayalite::expr::ToExpr>::Type, |  | ||||||
|                             >::intern_dyn(), |  | ||||||
|                         ), |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } else { |  | ||||||
|                 from_canonical_type_variant_lets.push(quote! { |  | ||||||
|                     #variant_var.from_canonical_type_helper_no_value(#variant_name_str); |  | ||||||
|                 }); |  | ||||||
|                 enum_type_variants.push(quote! { |  | ||||||
|                     ::fayalite::enum_::VariantType { |  | ||||||
|                         name: ::fayalite::intern::Intern::intern(#variant_name_str), |  | ||||||
|                         ty: ::fayalite::__std::option::Option::None, |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 enum_type_variants_hint.push(quote! { |  | ||||||
|                     ::fayalite::enum_::VariantType { |  | ||||||
|                         name: ::fayalite::intern::Intern::intern(#variant_name_str), |  | ||||||
|                         ty: ::fayalite::__std::option::Option::None, |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             variant_vars.push(variant_var); |  | ||||||
|             match_enum_arms.push(match &variant.value { |  | ||||||
|                 VariantValue::None => quote! { |  | ||||||
|                     #variant_index => #match_enum_ident::#variant_name, |  | ||||||
|                 }, |  | ||||||
|                 VariantValue::Direct { value_type } => quote! { |  | ||||||
|                     #variant_index => #match_enum_ident::#variant_name { |  | ||||||
|                         #(#match_enum_field_names)*: ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                             &__variant_access.downcast_unchecked::< |  | ||||||
|                                 <#value_type as ::fayalite::expr::ToExpr>::Type>(), |  | ||||||
|                         ), |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 VariantValue::Struct { |  | ||||||
|                     value_struct: ItemStruct { ident, .. }, |  | ||||||
|                     .. |  | ||||||
|                 } => quote! { |  | ||||||
|                     #variant_index => { |  | ||||||
|                         let __variant_access = ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                             &__variant_access.downcast_unchecked::<< |  | ||||||
|                                 #ident #static_type_type_generics |  | ||||||
|                                 as ::fayalite::expr::ToExpr |  | ||||||
|                             >::Type>(), |  | ||||||
|                         ); |  | ||||||
|                         #match_enum_ident::#variant_name { |  | ||||||
|                             #(#match_enum_field_names: |  | ||||||
|                                 (*__variant_access).#match_enum_field_names,)* |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|             }); |  | ||||||
|             let builder_field_and_types = Vec::from_iter(variant.fields.iter().map( |  | ||||||
|                 |ParsedField { |  | ||||||
|                      options, |  | ||||||
|                      vis: _, |  | ||||||
|                      name, |  | ||||||
|                      ty, |  | ||||||
|                  }| { |  | ||||||
|                     let FieldOptions {} = options.body; |  | ||||||
|                     (name, ty) |  | ||||||
|                 }, |  | ||||||
|             )); |  | ||||||
|             let builder_field_vars = Vec::from_iter( |  | ||||||
|                 builder_field_and_types |  | ||||||
|                     .iter() |  | ||||||
|                     .map(|(name, _)| &builder.get_field(name).unwrap().1.builder_field_name), |  | ||||||
|             ); |  | ||||||
|             let build_body = match &variant.value { |  | ||||||
|                 VariantValue::None => parse_quote! { |  | ||||||
|                     { |  | ||||||
|                         ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                             &::fayalite::expr::ops::EnumLiteral::< |  | ||||||
|                                 #type_struct_ident #static_type_type_generics |  | ||||||
|                             >::new_unchecked( |  | ||||||
|                                 ::fayalite::__std::option::Option::None, |  | ||||||
|                                 #variant_index, |  | ||||||
|                                 ::fayalite::ty::StaticType::static_type(), |  | ||||||
|                             ), |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 VariantValue::Direct { value_type: _ } => parse_quote! { |  | ||||||
|                     { |  | ||||||
|                         ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                             &::fayalite::expr::ops::EnumLiteral::< |  | ||||||
|                                 #type_struct_ident #static_type_type_generics |  | ||||||
|                             >::new_unchecked( |  | ||||||
|                                 ::fayalite::__std::option::Option::Some( |  | ||||||
|                                     #(#builder_field_vars)*.to_canonical_dyn(), |  | ||||||
|                                 ), |  | ||||||
|                                 #variant_index, |  | ||||||
|                                 ::fayalite::ty::StaticType::static_type(), |  | ||||||
|                             ), |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 VariantValue::Struct { |  | ||||||
|                     parsed_struct: |  | ||||||
|                         ParsedStruct { |  | ||||||
|                             names: |  | ||||||
|                                 ParsedStructNames { |  | ||||||
|                                     type_struct_ident: field_type_struct_ident, |  | ||||||
|                                     .. |  | ||||||
|                                 }, |  | ||||||
|                             .. |  | ||||||
|                         }, |  | ||||||
|                     .. |  | ||||||
|                 } => parse_quote! { |  | ||||||
|                     { |  | ||||||
|                         let __builder = < |  | ||||||
|                             #field_type_struct_ident #static_type_type_generics |  | ||||||
|                             as ::fayalite::bundle::BundleType |  | ||||||
|                         >::builder(); |  | ||||||
|                         #(let __builder = __builder.#builder_field_vars(#builder_field_vars);)* |  | ||||||
|                         ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                             &::fayalite::expr::ops::EnumLiteral::< |  | ||||||
|                                 #type_struct_ident #static_type_type_generics |  | ||||||
|                             >::new_unchecked( |  | ||||||
|                                 ::fayalite::__std::option::Option::Some( |  | ||||||
|                                     __builder.build().to_canonical_dyn(), |  | ||||||
|                                 ), |  | ||||||
|                                 #variant_index, |  | ||||||
|                                 ::fayalite::ty::StaticType::static_type(), |  | ||||||
|                             ), |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|             }; |  | ||||||
|             builder |  | ||||||
|                 .make_build_method( |  | ||||||
|                     &format_ident!("variant_{}", variant_name), |  | ||||||
|                     variant.fields.iter().map( |  | ||||||
|                         |ParsedField { |  | ||||||
|                              options, |  | ||||||
|                              vis: _, |  | ||||||
|                              name, |  | ||||||
|                              ty, |  | ||||||
|                          }| { |  | ||||||
|                             let FieldOptions {} = options.body; |  | ||||||
|                             (name.clone(), parse_quote! { ::fayalite::expr::Expr<#ty> }) |  | ||||||
|                         }, |  | ||||||
|                     ), |  | ||||||
|                     &static_type_generics, |  | ||||||
|                     &parse_quote! {#type_struct_ident #static_type_type_generics}, |  | ||||||
|                     &parse_quote! { ::fayalite::expr::Expr<#target #static_type_type_generics> }, |  | ||||||
|                     build_body, |  | ||||||
|                 ) |  | ||||||
|                 .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|         let match_enum = ItemEnum { |  | ||||||
|             attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], |  | ||||||
|             vis: vis.clone(), |  | ||||||
|             enum_token: *enum_token, |  | ||||||
|             ident: match_enum_ident, |  | ||||||
|             generics: static_type_generics.clone(), |  | ||||||
|             brace_token: *brace_token, |  | ||||||
|             variants: match_enum_variants, |  | ||||||
|         }; |  | ||||||
|         let match_enum_ident = &match_enum.ident; |  | ||||||
|         match_enum.to_tokens(tokens); |  | ||||||
|         make_connect_impl( |  | ||||||
|             *connect_inexact, |  | ||||||
|             &static_type_generics, |  | ||||||
|             type_struct_ident, |  | ||||||
|             variants.iter().flat_map(|variant| { |  | ||||||
|                 variant.fields.iter().map(|field| { |  | ||||||
|                     let ty = &field.ty; |  | ||||||
|                     parse_quote_spanned! {field.name.span()=> |  | ||||||
|                         <#ty as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                     } |  | ||||||
|                 }) |  | ||||||
|             }), |  | ||||||
|         ) |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|         let variant_count = variants.len(); |  | ||||||
|         let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false); |  | ||||||
|         quote! { |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::__std::fmt::Debug |  | ||||||
|                 for #match_enum_ident #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 fn fmt( |  | ||||||
|                     &self, |  | ||||||
|                     f: &mut ::fayalite::__std::fmt::Formatter<'_>, |  | ||||||
|                 ) -> ::fayalite::__std::fmt::Result { |  | ||||||
|                     match *self { |  | ||||||
|                         #(#match_enum_debug_arms)* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::ty::StaticType |  | ||||||
|                 for #type_struct_ident #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 fn static_type() -> Self { |  | ||||||
|                     Self { |  | ||||||
|                         #(#non_empty_variant_names: ::fayalite::ty::StaticType::static_type(),)* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             fn __check_field<T: ::fayalite::ty::Value>() |  | ||||||
|             where |  | ||||||
|                 <T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>, |  | ||||||
|             {} |  | ||||||
|             fn __check_fields #static_type_impl_generics(_: #target #static_type_type_generics) |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 #(#field_checks)* |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::__std::fmt::Debug |  | ||||||
|                 for #type_struct_ident #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 fn fmt( |  | ||||||
|                     &self, |  | ||||||
|                     f: &mut ::fayalite::__std::fmt::Formatter<'_>, |  | ||||||
|                 ) -> ::fayalite::__std::fmt::Result { |  | ||||||
|                     #debug_type_body |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::ty::Type |  | ||||||
|                 for #type_struct_ident #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 type CanonicalType = ::fayalite::enum_::DynEnumType; |  | ||||||
|                 type Value = #target #static_type_type_generics; |  | ||||||
|                 type CanonicalValue = ::fayalite::enum_::DynEnum; |  | ||||||
|                 type MaskType = ::fayalite::int::UIntType<1>; |  | ||||||
|                 type MaskValue = ::fayalite::int::UInt<1>; |  | ||||||
|                 type MatchVariant = #match_enum_ident #static_type_type_generics; |  | ||||||
|                 type MatchActiveScope = ::fayalite::module::Scope; |  | ||||||
|                 type MatchVariantAndInactiveScope = |  | ||||||
|                     ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; |  | ||||||
|                 type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>; |  | ||||||
|                 fn match_variants<IO: ::fayalite::bundle::BundleValue>( |  | ||||||
|                     this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>, |  | ||||||
|                     module_builder: &mut ::fayalite::module::ModuleBuilder< |  | ||||||
|                         IO, |  | ||||||
|                         ::fayalite::module::NormalModule, |  | ||||||
|                     >, |  | ||||||
|                     source_location: ::fayalite::source_location::SourceLocation, |  | ||||||
|                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter |  | ||||||
|                 where |  | ||||||
|                     <IO as ::fayalite::expr::ToExpr>::Type: |  | ||||||
|                         ::fayalite::bundle::BundleType<Value = IO>, |  | ||||||
|                 { |  | ||||||
|                     module_builder.enum_match_variants_helper(this, source_location) |  | ||||||
|                 } |  | ||||||
|                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { |  | ||||||
|                     ::fayalite::int::UIntType::new() |  | ||||||
|                 } |  | ||||||
|                 fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType { |  | ||||||
|                     let variants = ::fayalite::enum_::EnumType::variants(self); |  | ||||||
|                     ::fayalite::enum_::DynEnumType::new(variants) |  | ||||||
|                 } |  | ||||||
|                 fn source_location(&self) -> ::fayalite::source_location::SourceLocation { |  | ||||||
|                     ::fayalite::source_location::SourceLocation::caller() |  | ||||||
|                 } |  | ||||||
|                 fn type_enum(&self) -> ::fayalite::ty::TypeEnum { |  | ||||||
|                     ::fayalite::ty::TypeEnum::EnumType(::fayalite::ty::Type::canonical(self)) |  | ||||||
|                 } |  | ||||||
|                 #[allow(non_snake_case)] |  | ||||||
|                 fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self { |  | ||||||
|                     let [#(#variant_vars),*] = *::fayalite::enum_::EnumType::variants(&t) else { |  | ||||||
|                         ::fayalite::__std::panic!("wrong number of variants"); |  | ||||||
|                     }; |  | ||||||
|                     #(#from_canonical_type_variant_lets)* |  | ||||||
|                     Self { |  | ||||||
|                         #(#non_empty_variant_names: #non_empty_variant_vars,)* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             #[allow(clippy::init_numbered_fields)] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::enum_::EnumType |  | ||||||
|                 for #type_struct_ident #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 type Builder = #empty_builder_ty; |  | ||||||
|                 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 ::fayalite::expr::ops::VariantAccess::variant_index( |  | ||||||
|                             &*__variant_access, |  | ||||||
|                         ) { |  | ||||||
|                             #(#match_enum_arms)* |  | ||||||
|                             #variant_count.. => ::fayalite::__std::panic!("invalid variant index"), |  | ||||||
|                         }, |  | ||||||
|                         __scope, |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|                 fn builder() -> <Self as ::fayalite::enum_::EnumType>::Builder { |  | ||||||
|                     #empty_builder_ty::new() |  | ||||||
|                 } |  | ||||||
|                 fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::VariantType< |  | ||||||
|                     ::fayalite::intern::Interned<dyn ::fayalite::ty::DynCanonicalType>, |  | ||||||
|                 >]> { |  | ||||||
|                     ::fayalite::intern::Intern::intern(&[#(#enum_type_variants,)*][..]) |  | ||||||
|                 } |  | ||||||
|                 fn variants_hint() -> ::fayalite::enum_::VariantsHint { |  | ||||||
|                     ::fayalite::enum_::VariantsHint::new([#(#enum_type_variants_hint,)*], false) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::expr::ToExpr |  | ||||||
|                 for #target #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 type Type = #type_struct_ident #static_type_type_generics; |  | ||||||
|                 fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type { |  | ||||||
|                     ::fayalite::ty::StaticType::static_type() |  | ||||||
|                 } |  | ||||||
|                 fn to_expr(&self) -> ::fayalite::expr::Expr<Self> { |  | ||||||
|                     ::fayalite::expr::Expr::from_value(self) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::ty::Value |  | ||||||
|                 for #target #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|                 fn to_canonical(&self) -> < |  | ||||||
|                         <Self as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                         as ::fayalite::ty::Type |  | ||||||
|                     >::CanonicalValue |  | ||||||
|                 { |  | ||||||
|                     let __ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self)); |  | ||||||
|                     match self { |  | ||||||
|                         #(Self::#variant_names { #variant_field_pats } => { |  | ||||||
|                             ::fayalite::enum_::DynEnum::new_by_name( |  | ||||||
|                                 __ty, |  | ||||||
|                                 ::fayalite::intern::Intern::intern(#variant_name_strs), |  | ||||||
|                                 #variant_to_canonical_values, |  | ||||||
|                             ) |  | ||||||
|                         })* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #static_type_impl_generics ::fayalite::enum_::EnumValue |  | ||||||
|                 for #target #static_type_type_generics |  | ||||||
|             #static_type_where_clause |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn value_derive_enum(item: ItemEnum) -> syn::Result<TokenStream> { |  | ||||||
|     let item = ParsedEnum::parse(item)?; |  | ||||||
|     let outline_generated = item.options.body.outline_generated; |  | ||||||
|     let mut contents = quote! { |  | ||||||
|         const _: () = { |  | ||||||
|             #item |  | ||||||
|         }; |  | ||||||
|     }; |  | ||||||
|     if outline_generated.is_some() { |  | ||||||
|         contents = crate::outline_generated(contents, "value-enum-"); |  | ||||||
|     } |  | ||||||
|     Ok(contents) |  | ||||||
| } |  | ||||||
|  | @ -1,765 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| use crate::{ |  | ||||||
|     value_derive_common::{ |  | ||||||
|         append_field, derive_clone_hash_eq_partialeq_for_struct, get_target, make_connect_impl, |  | ||||||
|         Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics, |  | ||||||
|     }, |  | ||||||
|     Errors, HdlAttr, |  | ||||||
| }; |  | ||||||
| use proc_macro2::TokenStream; |  | ||||||
| use quote::{format_ident, quote, quote_spanned, ToTokens}; |  | ||||||
| use syn::{ |  | ||||||
|     parse_quote, parse_quote_spanned, spanned::Spanned, FieldMutability, Generics, Ident, |  | ||||||
|     ItemStruct, Member, Path, Token, Visibility, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| crate::options! { |  | ||||||
|     #[options = StructOptions] |  | ||||||
|     pub(crate) enum StructOption { |  | ||||||
|         OutlineGenerated(outline_generated), |  | ||||||
|         Static(static_), |  | ||||||
|         ConnectInexact(connect_inexact), |  | ||||||
|         Bounds(where_, Bounds), |  | ||||||
|         Target(target, Path), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| crate::options! { |  | ||||||
|     #[options = FieldOptions] |  | ||||||
|     pub(crate) enum FieldOption { |  | ||||||
|         Flip(flip), |  | ||||||
|         Skip(skip), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) struct ParsedStructNames<I, S> { |  | ||||||
|     pub(crate) ident: Ident, |  | ||||||
|     pub(crate) type_struct_debug_ident: S, |  | ||||||
|     pub(crate) type_struct_ident: Ident, |  | ||||||
|     pub(crate) match_variant_ident: I, |  | ||||||
|     pub(crate) builder_struct_ident: I, |  | ||||||
|     pub(crate) mask_match_variant_ident: I, |  | ||||||
|     pub(crate) mask_type_ident: I, |  | ||||||
|     pub(crate) mask_type_debug_ident: S, |  | ||||||
|     pub(crate) mask_value_ident: I, |  | ||||||
|     pub(crate) mask_value_debug_ident: S, |  | ||||||
|     pub(crate) mask_builder_struct_ident: I, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) struct ParsedStruct { |  | ||||||
|     pub(crate) options: HdlAttr<StructOptions>, |  | ||||||
|     pub(crate) vis: Visibility, |  | ||||||
|     pub(crate) struct_token: Token![struct], |  | ||||||
|     pub(crate) generics: Generics, |  | ||||||
|     pub(crate) fields_kind: FieldsKind, |  | ||||||
|     pub(crate) fields: Vec<ParsedField<FieldOptions>>, |  | ||||||
|     pub(crate) semi_token: Option<Token![;]>, |  | ||||||
|     pub(crate) skip_check_fields: bool, |  | ||||||
|     pub(crate) names: ParsedStructNames<Option<Ident>, Option<String>>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ParsedStruct { |  | ||||||
|     pub(crate) fn parse(item: &mut ItemStruct) -> syn::Result<Self> { |  | ||||||
|         let ItemStruct { |  | ||||||
|             attrs, |  | ||||||
|             vis, |  | ||||||
|             struct_token, |  | ||||||
|             ident, |  | ||||||
|             generics, |  | ||||||
|             fields, |  | ||||||
|             semi_token, |  | ||||||
|         } = item; |  | ||||||
|         let mut errors = Errors::new(); |  | ||||||
|         let struct_options = errors |  | ||||||
|             .unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)) |  | ||||||
|             .unwrap_or_default(); |  | ||||||
|         let (fields_kind, fields) = ParsedField::parse_fields(&mut errors, fields, false); |  | ||||||
|         errors.finish()?; |  | ||||||
|         Ok(ParsedStruct { |  | ||||||
|             options: struct_options, |  | ||||||
|             vis: vis.clone(), |  | ||||||
|             struct_token: *struct_token, |  | ||||||
|             generics: generics.clone(), |  | ||||||
|             fields_kind, |  | ||||||
|             fields, |  | ||||||
|             semi_token: *semi_token, |  | ||||||
|             skip_check_fields: false, |  | ||||||
|             names: ParsedStructNames { |  | ||||||
|                 ident: ident.clone(), |  | ||||||
|                 type_struct_debug_ident: None, |  | ||||||
|                 type_struct_ident: format_ident!("__{}__Type", ident), |  | ||||||
|                 match_variant_ident: None, |  | ||||||
|                 builder_struct_ident: None, |  | ||||||
|                 mask_match_variant_ident: None, |  | ||||||
|                 mask_type_ident: None, |  | ||||||
|                 mask_type_debug_ident: None, |  | ||||||
|                 mask_value_ident: None, |  | ||||||
|                 mask_value_debug_ident: None, |  | ||||||
|                 mask_builder_struct_ident: None, |  | ||||||
|             }, |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn write_body( |  | ||||||
|         &self, |  | ||||||
|         target: Path, |  | ||||||
|         names: ParsedStructNames<&Ident, &String>, |  | ||||||
|         is_for_mask: bool, |  | ||||||
|         tokens: &mut TokenStream, |  | ||||||
|     ) { |  | ||||||
|         let Self { |  | ||||||
|             options, |  | ||||||
|             vis, |  | ||||||
|             struct_token, |  | ||||||
|             generics, |  | ||||||
|             fields_kind, |  | ||||||
|             fields, |  | ||||||
|             semi_token, |  | ||||||
|             skip_check_fields, |  | ||||||
|             names: _, |  | ||||||
|         } = self; |  | ||||||
|         let skip_check_fields = *skip_check_fields || is_for_mask; |  | ||||||
|         let ParsedStructNames { |  | ||||||
|             ident: struct_ident, |  | ||||||
|             type_struct_debug_ident, |  | ||||||
|             type_struct_ident, |  | ||||||
|             match_variant_ident, |  | ||||||
|             builder_struct_ident, |  | ||||||
|             mask_match_variant_ident: _, |  | ||||||
|             mask_type_ident, |  | ||||||
|             mask_type_debug_ident: _, |  | ||||||
|             mask_value_ident, |  | ||||||
|             mask_value_debug_ident, |  | ||||||
|             mask_builder_struct_ident: _, |  | ||||||
|         } = names; |  | ||||||
|         let StructOptions { |  | ||||||
|             outline_generated: _, |  | ||||||
|             where_, |  | ||||||
|             target: _, |  | ||||||
|             static_, |  | ||||||
|             connect_inexact, |  | ||||||
|         } = &options.body; |  | ||||||
|         let ValueDeriveGenerics { |  | ||||||
|             generics, |  | ||||||
|             static_type_generics, |  | ||||||
|         } = ValueDeriveGenerics::get(generics.clone(), where_); |  | ||||||
|         let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); |  | ||||||
|         let unskipped_fields = fields |  | ||||||
|             .iter() |  | ||||||
|             .filter(|field| field.options.body.skip.is_none()); |  | ||||||
|         let _field_names = Vec::from_iter(fields.iter().map(|field| field.name.clone())); |  | ||||||
|         let unskipped_field_names = |  | ||||||
|             Vec::from_iter(unskipped_fields.clone().map(|field| field.name.clone())); |  | ||||||
|         let unskipped_field_name_strs = Vec::from_iter( |  | ||||||
|             unskipped_field_names |  | ||||||
|                 .iter() |  | ||||||
|                 .map(|field_name| field_name.to_token_stream().to_string()), |  | ||||||
|         ); |  | ||||||
|         let unskipped_field_vars = Vec::from_iter( |  | ||||||
|             unskipped_field_names |  | ||||||
|                 .iter() |  | ||||||
|                 .map(|field_name| format_ident!("__v_{}", field_name)), |  | ||||||
|         ); |  | ||||||
|         let unskipped_field_flips = Vec::from_iter( |  | ||||||
|             unskipped_fields |  | ||||||
|                 .clone() |  | ||||||
|                 .map(|field| field.options.body.flip.is_some()), |  | ||||||
|         ); |  | ||||||
|         let mut any_fields_skipped = false; |  | ||||||
|         let type_fields = Vec::from_iter(fields.iter().filter_map(|field| { |  | ||||||
|             let ParsedField { |  | ||||||
|                 options, |  | ||||||
|                 vis, |  | ||||||
|                 name, |  | ||||||
|                 ty, |  | ||||||
|             } = field; |  | ||||||
|             let FieldOptions { flip: _, skip } = &options.body; |  | ||||||
|             if skip.is_some() { |  | ||||||
|                 any_fields_skipped = true; |  | ||||||
|                 return None; |  | ||||||
|             } |  | ||||||
|             let ty = if is_for_mask { |  | ||||||
|                 parse_quote! { ::fayalite::ty::AsMask<#ty> } |  | ||||||
|             } else { |  | ||||||
|                 ty.to_token_stream() |  | ||||||
|             }; |  | ||||||
|             Some(syn::Field { |  | ||||||
|                 attrs: vec![], |  | ||||||
|                 vis: vis.clone(), |  | ||||||
|                 mutability: FieldMutability::None, |  | ||||||
|                 ident: match name.clone() { |  | ||||||
|                     Member::Named(name) => Some(name), |  | ||||||
|                     Member::Unnamed(_) => None, |  | ||||||
|                 }, |  | ||||||
|                 colon_token: None, |  | ||||||
|                 ty: parse_quote! { <#ty as ::fayalite::expr::ToExpr>::Type }, |  | ||||||
|             }) |  | ||||||
|         })); |  | ||||||
|         let field_types = Vec::from_iter(type_fields.iter().map(|field| field.ty.clone())); |  | ||||||
|         let match_variant_fields = Vec::from_iter(fields.iter().zip(&type_fields).map( |  | ||||||
|             |(parsed_field, type_field)| { |  | ||||||
|                 let field_ty = &parsed_field.ty; |  | ||||||
|                 syn::Field { |  | ||||||
|                     ty: parse_quote! { ::fayalite::expr::Expr<#field_ty> }, |  | ||||||
|                     ..type_field.clone() |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         let mask_value_fields = Vec::from_iter(fields.iter().zip(&type_fields).map( |  | ||||||
|             |(parsed_field, type_field)| { |  | ||||||
|                 let field_ty = &parsed_field.ty; |  | ||||||
|                 syn::Field { |  | ||||||
|                     ty: parse_quote! { ::fayalite::ty::AsMask<#field_ty> }, |  | ||||||
|                     ..type_field.clone() |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         )); |  | ||||||
| 
 |  | ||||||
|         let mut type_struct_fields = fields_kind.into_fields(type_fields); |  | ||||||
|         let mut match_variant_fields = fields_kind.into_fields(match_variant_fields); |  | ||||||
|         let mut mask_value_fields = fields_kind.into_fields(mask_value_fields); |  | ||||||
|         let phantom_data_field_name = any_fields_skipped.then(|| { |  | ||||||
|             let phantom_data_field_name = Ident::new("__phantom_data", type_struct_ident.span()); |  | ||||||
|             let member = append_field( |  | ||||||
|                 &mut type_struct_fields, |  | ||||||
|                 syn::Field { |  | ||||||
|                     attrs: vec![], |  | ||||||
|                     vis: vis.clone(), |  | ||||||
|                     mutability: FieldMutability::None, |  | ||||||
|                     ident: Some(phantom_data_field_name.clone()), |  | ||||||
|                     colon_token: None, |  | ||||||
|                     ty: parse_quote_spanned! {type_struct_ident.span()=> |  | ||||||
|                         ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             append_field( |  | ||||||
|                 &mut match_variant_fields, |  | ||||||
|                 syn::Field { |  | ||||||
|                     attrs: vec![], |  | ||||||
|                     vis: Visibility::Inherited, |  | ||||||
|                     mutability: FieldMutability::None, |  | ||||||
|                     ident: Some(phantom_data_field_name.clone()), |  | ||||||
|                     colon_token: None, |  | ||||||
|                     ty: parse_quote_spanned! {type_struct_ident.span()=> |  | ||||||
|                         ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             append_field( |  | ||||||
|                 &mut mask_value_fields, |  | ||||||
|                 syn::Field { |  | ||||||
|                     attrs: vec![], |  | ||||||
|                     vis: Visibility::Inherited, |  | ||||||
|                     mutability: FieldMutability::None, |  | ||||||
|                     ident: Some(phantom_data_field_name), |  | ||||||
|                     colon_token: None, |  | ||||||
|                     ty: parse_quote_spanned! {type_struct_ident.span()=> |  | ||||||
|                         ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics> |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|             member |  | ||||||
|         }); |  | ||||||
|         let phantom_data_field_name_slice = phantom_data_field_name.as_slice(); |  | ||||||
|         let type_struct = ItemStruct { |  | ||||||
|             attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], |  | ||||||
|             vis: vis.clone(), |  | ||||||
|             struct_token: *struct_token, |  | ||||||
|             ident: type_struct_ident.clone(), |  | ||||||
|             generics: generics.clone(), |  | ||||||
|             fields: type_struct_fields, |  | ||||||
|             semi_token: *semi_token, |  | ||||||
|         }; |  | ||||||
|         type_struct.to_tokens(tokens); |  | ||||||
|         let match_variant_struct = ItemStruct { |  | ||||||
|             attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], |  | ||||||
|             vis: vis.clone(), |  | ||||||
|             struct_token: *struct_token, |  | ||||||
|             ident: match_variant_ident.clone(), |  | ||||||
|             generics: generics.clone(), |  | ||||||
|             fields: match_variant_fields, |  | ||||||
|             semi_token: *semi_token, |  | ||||||
|         }; |  | ||||||
|         match_variant_struct.to_tokens(tokens); |  | ||||||
|         let mask_type_body = if is_for_mask { |  | ||||||
|             quote! { |  | ||||||
|                 ::fayalite::__std::clone::Clone::clone(self) |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             let mask_value_struct = ItemStruct { |  | ||||||
|                 attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}], |  | ||||||
|                 vis: vis.clone(), |  | ||||||
|                 struct_token: *struct_token, |  | ||||||
|                 ident: mask_value_ident.clone(), |  | ||||||
|                 generics: generics.clone(), |  | ||||||
|                 fields: mask_value_fields, |  | ||||||
|                 semi_token: *semi_token, |  | ||||||
|             }; |  | ||||||
|             mask_value_struct.to_tokens(tokens); |  | ||||||
|             let debug_mask_value_body = match fields_kind { |  | ||||||
|                 FieldsKind::Unit => quote! { |  | ||||||
|                     f.debug_struct(#mask_value_debug_ident).finish() |  | ||||||
|                 }, |  | ||||||
|                 FieldsKind::Named(_) => quote! { |  | ||||||
|                     f.debug_struct(#mask_value_debug_ident) |  | ||||||
|                         #(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))* |  | ||||||
|                         .finish() |  | ||||||
|                 }, |  | ||||||
|                 FieldsKind::Unnamed(_) => quote! { |  | ||||||
|                     f.debug_tuple(#mask_value_debug_ident) |  | ||||||
|                         #(.field(&self.#unskipped_field_names))* |  | ||||||
|                         .finish() |  | ||||||
|                 }, |  | ||||||
|             }; |  | ||||||
|             quote! { |  | ||||||
|                 #[automatically_derived] |  | ||||||
|                 impl #impl_generics ::fayalite::__std::fmt::Debug |  | ||||||
|                     for #mask_value_ident #type_generics |  | ||||||
|                 #where_clause |  | ||||||
|                 { |  | ||||||
|                     fn fmt( |  | ||||||
|                         &self, |  | ||||||
|                         f: &mut ::fayalite::__std::fmt::Formatter<'_>, |  | ||||||
|                     ) -> ::fayalite::__std::fmt::Result { |  | ||||||
|                         #debug_mask_value_body |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|             quote! { |  | ||||||
|                 #mask_type_ident { |  | ||||||
|                     #(#unskipped_field_names: |  | ||||||
|                         ::fayalite::ty::Type::mask_type(&self.#unskipped_field_names),)* |  | ||||||
|                     #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         let debug_type_body = match fields_kind { |  | ||||||
|             FieldsKind::Unit => quote! { |  | ||||||
|                 f.debug_struct(#type_struct_debug_ident).finish() |  | ||||||
|             }, |  | ||||||
|             FieldsKind::Named(_) => quote! { |  | ||||||
|                 f.debug_struct(#type_struct_debug_ident) |  | ||||||
|                     #(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))* |  | ||||||
|                     .finish() |  | ||||||
|             }, |  | ||||||
|             FieldsKind::Unnamed(_) => quote! { |  | ||||||
|                 f.debug_tuple(#type_struct_debug_ident) |  | ||||||
|                     #(.field(&self.#unskipped_field_names))* |  | ||||||
|                     .finish() |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|         for the_struct_ident in [&type_struct_ident, match_variant_ident] |  | ||||||
|             .into_iter() |  | ||||||
|             .chain(is_for_mask.then_some(mask_value_ident)) |  | ||||||
|         { |  | ||||||
|             derive_clone_hash_eq_partialeq_for_struct( |  | ||||||
|                 the_struct_ident, |  | ||||||
|                 &generics, |  | ||||||
|                 &Vec::from_iter( |  | ||||||
|                     unskipped_field_names |  | ||||||
|                         .iter() |  | ||||||
|                         .cloned() |  | ||||||
|                         .chain(phantom_data_field_name.clone()), |  | ||||||
|                 ), |  | ||||||
|             ) |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|         let check_v = format_ident!("__v"); |  | ||||||
|         let field_checks = Vec::from_iter(fields.iter().map(|ParsedField { ty, name, .. }| { |  | ||||||
|             quote_spanned! {ty.span()=> |  | ||||||
|                 __check_field(#check_v.#name); |  | ||||||
|             } |  | ||||||
|         })); |  | ||||||
|         if static_.is_some() { |  | ||||||
|             let (impl_generics, type_generics, where_clause) = |  | ||||||
|                 static_type_generics.split_for_impl(); |  | ||||||
|             quote! { |  | ||||||
|                 #[automatically_derived] |  | ||||||
|                 impl #impl_generics ::fayalite::ty::StaticType for #type_struct_ident #type_generics |  | ||||||
|                 #where_clause |  | ||||||
|                 { |  | ||||||
|                     fn static_type() -> Self { |  | ||||||
|                         Self { |  | ||||||
|                             #(#unskipped_field_names: ::fayalite::ty::StaticType::static_type(),)* |  | ||||||
|                             #(#phantom_data_field_name_slice: |  | ||||||
|                                 ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|         if !skip_check_fields { |  | ||||||
|             quote! { |  | ||||||
|                 fn __check_field<T: ::fayalite::ty::Value>(_v: T) |  | ||||||
|                 where |  | ||||||
|                     <T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>, |  | ||||||
|                 {} |  | ||||||
|                 fn __check_fields #impl_generics(#check_v: #target #type_generics) |  | ||||||
|                 #where_clause |  | ||||||
|                 { |  | ||||||
|                     #(#field_checks)* |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|         let mut builder = Builder::new(builder_struct_ident.clone(), vis.clone()); |  | ||||||
|         for field in unskipped_fields.clone() { |  | ||||||
|             builder.insert_field( |  | ||||||
|                 field.name.clone(), |  | ||||||
|                 |v| { |  | ||||||
|                     parse_quote_spanned! {v.span()=> |  | ||||||
|                         ::fayalite::expr::ToExpr::to_expr(&#v) |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 |t| { |  | ||||||
|                     parse_quote_spanned! {t.span()=> |  | ||||||
|                         ::fayalite::expr::Expr<< |  | ||||||
|                             <#t as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                             as ::fayalite::ty::Type |  | ||||||
|                         >::Value> |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 |t| { |  | ||||||
|                     parse_quote_spanned! {t.span()=> |  | ||||||
|                         where |  | ||||||
|                             #t: ::fayalite::expr::ToExpr, |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         let builder = builder.finish_filling_in_fields(); |  | ||||||
|         builder.to_tokens(tokens); |  | ||||||
|         let build_type_fields = |  | ||||||
|             Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| { |  | ||||||
|                 let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name; |  | ||||||
|                 quote_spanned! {struct_ident.span()=> |  | ||||||
|                     #name: ::fayalite::expr::ToExpr::ty(&#builder_field_name) |  | ||||||
|                 } |  | ||||||
|             })); |  | ||||||
|         let build_expr_fields = |  | ||||||
|             Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| { |  | ||||||
|                 let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name; |  | ||||||
|                 quote_spanned! {struct_ident.span()=> |  | ||||||
|                     #builder_field_name.to_canonical_dyn() |  | ||||||
|                 } |  | ||||||
|             })); |  | ||||||
|         let build_specified_fields = unskipped_fields.clone().map( |  | ||||||
|             |ParsedField { |  | ||||||
|                  options: _, |  | ||||||
|                  vis: _, |  | ||||||
|                  name, |  | ||||||
|                  ty, |  | ||||||
|              }| { |  | ||||||
|                 let ty = if is_for_mask { |  | ||||||
|                     parse_quote_spanned! {name.span()=> |  | ||||||
|                         ::fayalite::expr::Expr<::fayalite::ty::AsMask<#ty>> |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     parse_quote_spanned! {name.span()=> |  | ||||||
|                         ::fayalite::expr::Expr<#ty> |  | ||||||
|                     } |  | ||||||
|                 }; |  | ||||||
|                 (name.clone(), ty) |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|         let build_body = parse_quote_spanned! {struct_ident.span()=> |  | ||||||
|             { |  | ||||||
|                 ::fayalite::expr::ToExpr::to_expr( |  | ||||||
|                     &::fayalite::expr::ops::BundleLiteral::new_unchecked( |  | ||||||
|                         ::fayalite::intern::Intern::intern(&[#( |  | ||||||
|                             #build_expr_fields, |  | ||||||
|                         )*][..]), |  | ||||||
|                         #type_struct_ident { |  | ||||||
|                             #(#build_type_fields,)* |  | ||||||
|                             #(#phantom_data_field_name_slice: |  | ||||||
|                                 ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                         }, |  | ||||||
|                     ), |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         builder |  | ||||||
|             .make_build_method( |  | ||||||
|                 &Ident::new("build", struct_ident.span()), |  | ||||||
|                 build_specified_fields, |  | ||||||
|                 &generics, |  | ||||||
|                 &parse_quote_spanned! {struct_ident.span()=> |  | ||||||
|                     #type_struct_ident #type_generics |  | ||||||
|                 }, |  | ||||||
|                 &parse_quote_spanned! {struct_ident.span()=> |  | ||||||
|                     ::fayalite::expr::Expr<#target #type_generics> |  | ||||||
|                 }, |  | ||||||
|                 build_body, |  | ||||||
|             ) |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         make_connect_impl( |  | ||||||
|             *connect_inexact, |  | ||||||
|             &generics, |  | ||||||
|             &type_struct_ident, |  | ||||||
|             unskipped_fields.clone().map(|field| { |  | ||||||
|                 let ty = &field.ty; |  | ||||||
|                 parse_quote_spanned! {field.name.span()=> |  | ||||||
|                     <#ty as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                 } |  | ||||||
|             }), |  | ||||||
|         ) |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|         let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false); |  | ||||||
|         quote! { |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::__std::fmt::Debug for #type_struct_ident #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 fn fmt( |  | ||||||
|                     &self, |  | ||||||
|                     f: &mut ::fayalite::__std::fmt::Formatter<'_>, |  | ||||||
|                 ) -> ::fayalite::__std::fmt::Result { |  | ||||||
|                     #debug_type_body |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 type CanonicalType = ::fayalite::bundle::DynBundleType; |  | ||||||
|                 type Value = #target #type_generics; |  | ||||||
|                 type CanonicalValue = ::fayalite::bundle::DynBundle; |  | ||||||
|                 type MaskType = #mask_type_ident #type_generics; |  | ||||||
|                 type MaskValue = #mask_value_ident #type_generics; |  | ||||||
|                 type MatchVariant = #match_variant_ident #type_generics; |  | ||||||
|                 type MatchActiveScope = (); |  | ||||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< |  | ||||||
|                     #match_variant_ident #type_generics, |  | ||||||
|                 >; |  | ||||||
|                 type MatchVariantsIter = ::fayalite::__std::iter::Once< |  | ||||||
|                     <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, |  | ||||||
|                 >; |  | ||||||
|                 #[allow(unused_variables)] |  | ||||||
|                 fn match_variants<IO: ::fayalite::bundle::BundleValue>( |  | ||||||
|                     this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>, |  | ||||||
|                     module_builder: &mut ::fayalite::module::ModuleBuilder< |  | ||||||
|                         IO, |  | ||||||
|                         ::fayalite::module::NormalModule, |  | ||||||
|                     >, |  | ||||||
|                     source_location: ::fayalite::source_location::SourceLocation, |  | ||||||
|                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter |  | ||||||
|                 where |  | ||||||
|                     <IO as ::fayalite::expr::ToExpr>::Type: |  | ||||||
|                         ::fayalite::bundle::BundleType<Value = IO>, |  | ||||||
|                 { |  | ||||||
|                     ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope( |  | ||||||
|                         #match_variant_ident { |  | ||||||
|                             #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)* |  | ||||||
|                             #(#phantom_data_field_name_slice: |  | ||||||
|                                 ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                         }, |  | ||||||
|                     )) |  | ||||||
|                 } |  | ||||||
|                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { |  | ||||||
|                     #mask_type_body |  | ||||||
|                 } |  | ||||||
|                 fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType { |  | ||||||
|                     let fields = ::fayalite::bundle::BundleType::fields(self); |  | ||||||
|                     ::fayalite::bundle::DynBundleType::new(fields) |  | ||||||
|                 } |  | ||||||
|                 fn source_location(&self) -> ::fayalite::source_location::SourceLocation { |  | ||||||
|                     ::fayalite::source_location::SourceLocation::caller() |  | ||||||
|                 } |  | ||||||
|                 fn type_enum(&self) -> ::fayalite::ty::TypeEnum { |  | ||||||
|                     ::fayalite::ty::TypeEnum::BundleType(::fayalite::ty::Type::canonical(self)) |  | ||||||
|                 } |  | ||||||
|                 fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self { |  | ||||||
|                     let [#(#unskipped_field_vars),*] = *::fayalite::bundle::BundleType::fields(&t) |  | ||||||
|                     else { |  | ||||||
|                         ::fayalite::__std::panic!("wrong number of fields"); |  | ||||||
|                     }; |  | ||||||
|                     Self { |  | ||||||
|                         #(#unskipped_field_names: #unskipped_field_vars.from_canonical_type_helper( |  | ||||||
|                             #unskipped_field_name_strs, |  | ||||||
|                             #unskipped_field_flips, |  | ||||||
|                         ),)* |  | ||||||
|                         #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::ty::TypeWithDeref for #type_struct_ident #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 #[allow(unused_variables)] |  | ||||||
|                 fn expr_deref(this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>) |  | ||||||
|                     -> &<Self as ::fayalite::ty::Type>::MatchVariant |  | ||||||
|                 { |  | ||||||
|                     ::fayalite::intern::Interned::<_>::into_inner( |  | ||||||
|                         ::fayalite::intern::Intern::intern_sized(#match_variant_ident { |  | ||||||
|                             #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)* |  | ||||||
|                             #(#phantom_data_field_name_slice: |  | ||||||
|                                 ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                         }), |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #type_struct_ident #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 type Builder = #empty_builder_ty; |  | ||||||
|                 fn builder() -> <Self as ::fayalite::bundle::BundleType>::Builder { |  | ||||||
|                     #empty_builder_ty::new() |  | ||||||
|                 } |  | ||||||
|                 fn fields(&self) -> ::fayalite::intern::Interned< |  | ||||||
|                         [::fayalite::bundle::FieldType<::fayalite::intern::Interned< |  | ||||||
|                             dyn ::fayalite::ty::DynCanonicalType, |  | ||||||
|                         >>], |  | ||||||
|                     > |  | ||||||
|                 { |  | ||||||
|                     ::fayalite::intern::Intern::intern(&[#( |  | ||||||
|                         ::fayalite::bundle::FieldType { |  | ||||||
|                             name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs), |  | ||||||
|                             flipped: #unskipped_field_flips, |  | ||||||
|                             ty: ::fayalite::ty::DynType::canonical_dyn( |  | ||||||
|                                 &self.#unskipped_field_names, |  | ||||||
|                             ), |  | ||||||
|                         }, |  | ||||||
|                     )*][..]) |  | ||||||
|                 } |  | ||||||
|                 fn fields_hint() -> ::fayalite::bundle::FieldsHint { |  | ||||||
|                     ::fayalite::bundle::FieldsHint::new([#( |  | ||||||
|                         ::fayalite::bundle::FieldType { |  | ||||||
|                             name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs), |  | ||||||
|                             flipped: #unskipped_field_flips, |  | ||||||
|                             ty: ::fayalite::bundle::TypeHint::<#field_types>::intern_dyn(), |  | ||||||
|                         }, |  | ||||||
|                     )*], false) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::expr::ToExpr for #target #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 type Type = #type_struct_ident #type_generics; |  | ||||||
|                 fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type { |  | ||||||
|                     #type_struct_ident { |  | ||||||
|                         #(#unskipped_field_names: ::fayalite::expr::ToExpr::ty( |  | ||||||
|                             &self.#unskipped_field_names, |  | ||||||
|                         ),)* |  | ||||||
|                         #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)* |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 fn to_expr(&self) -> ::fayalite::expr::Expr<Self> { |  | ||||||
|                     ::fayalite::expr::Expr::from_value(self) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::ty::Value for #target #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|                 fn to_canonical(&self) -> < |  | ||||||
|                         <Self as ::fayalite::expr::ToExpr>::Type |  | ||||||
|                         as ::fayalite::ty::Type |  | ||||||
|                     >::CanonicalValue |  | ||||||
|                 { |  | ||||||
|                     let ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self)); |  | ||||||
|                     ::fayalite::bundle::DynBundle::new(ty, ::fayalite::__std::sync::Arc::new([ |  | ||||||
|                         #(::fayalite::ty::DynValueTrait::to_canonical_dyn( |  | ||||||
|                             &self.#unskipped_field_names, |  | ||||||
|                         ),)* |  | ||||||
|                     ])) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #[automatically_derived] |  | ||||||
|             impl #impl_generics ::fayalite::bundle::BundleValue for #target #type_generics |  | ||||||
|             #where_clause |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         .to_tokens(tokens); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for ParsedStruct { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         let ParsedStructNames { |  | ||||||
|             ident: struct_ident, |  | ||||||
|             type_struct_debug_ident, |  | ||||||
|             type_struct_ident, |  | ||||||
|             match_variant_ident, |  | ||||||
|             builder_struct_ident, |  | ||||||
|             mask_match_variant_ident, |  | ||||||
|             mask_type_ident, |  | ||||||
|             mask_type_debug_ident, |  | ||||||
|             mask_value_ident, |  | ||||||
|             mask_value_debug_ident, |  | ||||||
|             mask_builder_struct_ident, |  | ||||||
|         } = &self.names; |  | ||||||
|         macro_rules! unwrap_or_set { |  | ||||||
|             ($(let $var:ident =? $fallback_value:expr;)*) => { |  | ||||||
|                 $(let $var = $var.clone().unwrap_or_else(|| $fallback_value);)* |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|         unwrap_or_set! { |  | ||||||
|             let type_struct_debug_ident =? format!("{struct_ident}::Type"); |  | ||||||
|             let match_variant_ident =? format_ident!("__{}__MatchVariant", struct_ident); |  | ||||||
|             let builder_struct_ident =? format_ident!("__{}__Builder", struct_ident); |  | ||||||
|             let mask_match_variant_ident =? format_ident!("__AsMask__{}__MatchVariant", struct_ident); |  | ||||||
|             let mask_type_ident =? format_ident!("__AsMask__{}__Type", struct_ident); |  | ||||||
|             let mask_type_debug_ident =? format!("AsMask<{struct_ident}>::Type"); |  | ||||||
|             let mask_value_ident =? format_ident!("__AsMask__{}", struct_ident); |  | ||||||
|             let mask_value_debug_ident =? format!("AsMask<{struct_ident}>"); |  | ||||||
|             let mask_builder_struct_ident =? format_ident!("__AsMask__{}__Builder", struct_ident); |  | ||||||
|         } |  | ||||||
|         let target = get_target(&self.options.body.target, struct_ident); |  | ||||||
|         let names = ParsedStructNames { |  | ||||||
|             ident: struct_ident.clone(), |  | ||||||
|             type_struct_debug_ident: &type_struct_debug_ident, |  | ||||||
|             type_struct_ident: type_struct_ident.clone(), |  | ||||||
|             match_variant_ident: &match_variant_ident, |  | ||||||
|             builder_struct_ident: &builder_struct_ident, |  | ||||||
|             mask_match_variant_ident: &mask_match_variant_ident, |  | ||||||
|             mask_type_ident: &mask_type_ident, |  | ||||||
|             mask_type_debug_ident: &mask_type_debug_ident, |  | ||||||
|             mask_value_ident: &mask_value_ident, |  | ||||||
|             mask_value_debug_ident: &mask_value_debug_ident, |  | ||||||
|             mask_builder_struct_ident: &mask_builder_struct_ident, |  | ||||||
|         }; |  | ||||||
|         self.write_body(target, names, false, tokens); |  | ||||||
|         let mask_names = ParsedStructNames { |  | ||||||
|             ident: mask_value_ident.clone(), |  | ||||||
|             type_struct_debug_ident: &mask_type_debug_ident, |  | ||||||
|             type_struct_ident: mask_type_ident.clone(), |  | ||||||
|             match_variant_ident: &mask_match_variant_ident, |  | ||||||
|             builder_struct_ident: &mask_builder_struct_ident, |  | ||||||
|             mask_match_variant_ident: &mask_match_variant_ident, |  | ||||||
|             mask_type_ident: &mask_type_ident, |  | ||||||
|             mask_type_debug_ident: &mask_type_debug_ident, |  | ||||||
|             mask_value_ident: &mask_value_ident, |  | ||||||
|             mask_value_debug_ident: &mask_value_debug_ident, |  | ||||||
|             mask_builder_struct_ident: &mask_builder_struct_ident, |  | ||||||
|         }; |  | ||||||
|         self.write_body(mask_value_ident.clone().into(), mask_names, true, tokens); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub(crate) fn value_derive_struct(mut item: ItemStruct) -> syn::Result<TokenStream> { |  | ||||||
|     let item = ParsedStruct::parse(&mut item)?; |  | ||||||
|     let outline_generated = item.options.body.outline_generated; |  | ||||||
|     let mut contents = quote! { |  | ||||||
|         const _: () = { |  | ||||||
|             #item |  | ||||||
|         }; |  | ||||||
|     }; |  | ||||||
|     if outline_generated.is_some() { |  | ||||||
|         contents = crate::outline_generated(contents, "value-struct-"); |  | ||||||
|     } |  | ||||||
|     Ok(contents) |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| //! proc macros for `fayalite`
 | //! proc macros for `fayalite`
 | ||||||
| //!
 | //!
 | ||||||
| //! see `fayalite::hdl_module` and `fayalite::ty::Value` for docs
 | //! see `fayalite::hdl_module` and `fayalite::hdl` for docs
 | ||||||
| 
 | 
 | ||||||
| // intentionally not documented here, see `fayalite::hdl_module` for docs
 | // intentionally not documented here, see `fayalite::hdl_module` for docs
 | ||||||
| #[proc_macro_attribute] | #[proc_macro_attribute] | ||||||
|  | @ -10,16 +10,19 @@ pub fn hdl_module( | ||||||
|     attr: proc_macro::TokenStream, |     attr: proc_macro::TokenStream, | ||||||
|     item: proc_macro::TokenStream, |     item: proc_macro::TokenStream, | ||||||
| ) -> proc_macro::TokenStream { | ) -> proc_macro::TokenStream { | ||||||
|     match fayalite_proc_macros_impl::module(attr.into(), item.into()) { |     match fayalite_proc_macros_impl::hdl_module(attr.into(), item.into()) { | ||||||
|         Ok(retval) => retval.into(), |         Ok(retval) => retval.into(), | ||||||
|         Err(err) => err.into_compile_error().into(), |         Err(err) => err.into_compile_error().into(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // intentionally not documented here, see `fayalite::ty::Value` for docs
 | // intentionally not documented here, see `fayalite::hdl` for docs
 | ||||||
| #[proc_macro_derive(Value, attributes(hdl))] | #[proc_macro_attribute] | ||||||
| pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { | pub fn hdl( | ||||||
|     match fayalite_proc_macros_impl::value_derive(item.into()) { |     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(), |         Ok(retval) => retval.into(), | ||||||
|         Err(err) => err.into_compile_error().into(), |         Err(err) => err.into_compile_error().into(), | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ use fayalite_visit_gen::parse_and_generate; | ||||||
| use std::{env, fs, path::Path}; | use std::{env, fs, path::Path}; | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|  |     println!("cargo::rustc-check-cfg=cfg(todo)"); | ||||||
|     let path = "visit_types.json"; |     let path = "visit_types.json"; | ||||||
|     println!("cargo::rerun-if-changed={path}"); |     println!("cargo::rerun-if-changed={path}"); | ||||||
|     println!("cargo::rerun-if-changed=build.rs"); |     println!("cargo::rerun-if-changed=build.rs"); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::Target, |     expr::target::Target, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
| }; | }; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | @ -1,671 +1,234 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     bundle::{BundleType, BundleValue}, |     expr::{ops::ArrayIndex, Expr, ToExpr}, | ||||||
|     expr::{ |     int::{DynSize, KnownSize, Size}, | ||||||
|         ops::{ArrayIndex, ArrayLiteral, ExprIndex}, |     intern::{Intern, Interned, LazyInterned}, | ||||||
|         Expr, ToExpr, |     module::transform::visit::{Fold, Folder, Visit, Visitor}, | ||||||
|     }, |  | ||||||
|     intern::{Intern, Interned, InternedCompare, Memoize}, |  | ||||||
|     module::{ |  | ||||||
|         transform::visit::{Fold, Folder, Visit, Visitor}, |  | ||||||
|         ModuleBuilder, NormalModule, |  | ||||||
|     }, |  | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{ | ||||||
|         CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, |         CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, | ||||||
|         DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType, |  | ||||||
|         StaticValue, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |     }, | ||||||
|     util::{ConstBool, GenericConstBool, MakeMutSlice}, |     util::ConstUsize, | ||||||
| }; |  | ||||||
| use bitvec::{slice::BitSlice, vec::BitVec}; |  | ||||||
| use std::{ |  | ||||||
|     any::Any, |  | ||||||
|     borrow::{Borrow, BorrowMut}, |  | ||||||
|     fmt, |  | ||||||
|     hash::Hash, |  | ||||||
|     marker::PhantomData, |  | ||||||
|     ops::IndexMut, |  | ||||||
|     sync::Arc, |  | ||||||
| }; | }; | ||||||
|  | use std::ops::Index; | ||||||
| 
 | 
 | ||||||
| mod sealed { | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
|     pub trait Sealed {} | pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | ||||||
|  |     element: LazyInterned<T>, | ||||||
|  |     len: Len::SizeType, | ||||||
|  |     type_properties: TypeProperties, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait ValueArrayOrSlice: | impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> { | ||||||
|     sealed::Sealed |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|     + BorrowMut<[<Self as ValueArrayOrSlice>::Element]> |         write!(f, "Array<{:?}, {}>", self.element, self.len()) | ||||||
|     + AsRef<[<Self as ValueArrayOrSlice>::Element]> |  | ||||||
|     + AsMut<[<Self as ValueArrayOrSlice>::Element]> |  | ||||||
|     + Hash |  | ||||||
|     + fmt::Debug |  | ||||||
|     + Eq |  | ||||||
|     + Send |  | ||||||
|     + Sync |  | ||||||
|     + 'static |  | ||||||
|     + IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element> |  | ||||||
|     + ToOwned |  | ||||||
|     + InternedCompare |  | ||||||
| { |  | ||||||
|     type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>; |  | ||||||
|     type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>; |  | ||||||
|     type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync; |  | ||||||
|     type Match: 'static |  | ||||||
|         + Clone |  | ||||||
|         + Eq |  | ||||||
|         + fmt::Debug |  | ||||||
|         + Hash |  | ||||||
|         + Send |  | ||||||
|         + Sync |  | ||||||
|         + BorrowMut<[Expr<Self::Element>]>; |  | ||||||
|     type MaskVA: ValueArrayOrSlice< |  | ||||||
|             Element = <Self::ElementType as Type>::MaskValue, |  | ||||||
|             ElementType = <Self::ElementType as Type>::MaskType, |  | ||||||
|             LenType = Self::LenType, |  | ||||||
|             MaskVA = Self::MaskVA, |  | ||||||
|         > + ?Sized; |  | ||||||
|     type IsStaticLen: GenericConstBool; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType>; |  | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match; |  | ||||||
|     fn len_from_len_type(v: Self::LenType) -> usize; |  | ||||||
|     #[allow(clippy::result_unit_err)] |  | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>; |  | ||||||
|     fn len_type(&self) -> Self::LenType; |  | ||||||
|     fn len(&self) -> usize; |  | ||||||
|     fn is_empty(&self) -> bool; |  | ||||||
|     fn iter(&self) -> std::slice::Iter<Self::Element> { |  | ||||||
|         Borrow::<[_]>::borrow(self).iter() |  | ||||||
|     } |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self>; |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self; |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T> sealed::Sealed for [T] {} |  | ||||||
| 
 |  | ||||||
| impl<V: Value> ValueArrayOrSlice for [V] |  | ||||||
| where |  | ||||||
|     V::Type: Type<Value = V>, |  | ||||||
| { |  | ||||||
|     type Element = V; |  | ||||||
|     type ElementType = V::Type; |  | ||||||
|     type LenType = usize; |  | ||||||
|     type Match = Box<[Expr<V>]>; |  | ||||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue]; |  | ||||||
|     type IsStaticLen = ConstBool<false>; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = None; |  | ||||||
| 
 |  | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match { |  | ||||||
|         (0..array.canonical_type().len()) |  | ||||||
|             .map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()) |  | ||||||
|             .collect() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_from_len_type(v: Self::LenType) -> usize { |  | ||||||
|         v |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { |  | ||||||
|         Ok(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_type(&self) -> Self::LenType { |  | ||||||
|         self.len() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len(&self) -> usize { |  | ||||||
|         <[_]>::len(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn is_empty(&self) -> bool { |  | ||||||
|         <[_]>::is_empty(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self> { |  | ||||||
|         Arc::from(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { |  | ||||||
|         MakeMutSlice::make_mut_slice(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { |  | ||||||
|         self |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T, const N: usize> sealed::Sealed for [T; N] {} | pub type Array< | ||||||
|  |     T = CanonicalType, | ||||||
|  |     const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE }, | ||||||
|  | > = ArrayType<T, ConstUsize<LEN>>; | ||||||
| 
 | 
 | ||||||
| impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N] | #[allow(non_upper_case_globals)] | ||||||
| where | pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics; | ||||||
|     V::Type: Type<Value = V>, | #[allow(non_upper_case_globals)] | ||||||
| { | pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics; | ||||||
|     type Element = V; |  | ||||||
|     type ElementType = V::Type; |  | ||||||
|     type LenType = (); |  | ||||||
|     type Match = [Expr<V>; N]; |  | ||||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue; N]; |  | ||||||
|     type IsStaticLen = ConstBool<true>; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = Some(()); |  | ||||||
| 
 | 
 | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match { | impl<T: Type, Len: Size> ArrayType<T, Len> { | ||||||
|         std::array::from_fn(|index| { |     const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties { | ||||||
|             ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr() |         let TypeProperties { | ||||||
|         }) |             is_passive, | ||||||
|     } |             is_storable, | ||||||
| 
 |             is_castable_from_bits, | ||||||
|     fn len_from_len_type(_v: Self::LenType) -> usize { |             bit_width, | ||||||
|         N |         } = element; | ||||||
|     } |         let Some(bit_width) = bit_width.checked_mul(len) else { | ||||||
| 
 |             panic!("array too big"); | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { |  | ||||||
|         if v == N { |  | ||||||
|             Ok(()) |  | ||||||
|         } else { |  | ||||||
|             Err(()) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_type(&self) -> Self::LenType {} |  | ||||||
| 
 |  | ||||||
|     fn len(&self) -> usize { |  | ||||||
|         N |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn is_empty(&self) -> bool { |  | ||||||
|         N == 0 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self> { |  | ||||||
|         Arc::new(self.clone()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { |  | ||||||
|         Arc::make_mut(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, PartialEq, Eq, Hash)] |  | ||||||
| pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> { |  | ||||||
|     element: VA::ElementType, |  | ||||||
|     len: VA::LenType, |  | ||||||
|     bit_width: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait ArrayTypeTrait: |  | ||||||
|     Type< |  | ||||||
|         CanonicalType = ArrayType<[DynCanonicalValue]>, |  | ||||||
|         Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>, |  | ||||||
|         CanonicalValue = Array<[DynCanonicalValue]>, |  | ||||||
|         MaskType = ArrayType< |  | ||||||
|             <<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA, |  | ||||||
|         >, |  | ||||||
|     > + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + sealed::Sealed |  | ||||||
|     + Connect<Self> |  | ||||||
| { |  | ||||||
|     type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType> |  | ||||||
|         + ?Sized; |  | ||||||
|     type Element: Value<Type = Self::ElementType>; |  | ||||||
|     type ElementType: Type<Value = Self::Element>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> { |  | ||||||
|     type ValueArrayOrSlice = VA; |  | ||||||
|     type Element = VA::Element; |  | ||||||
|     type ElementType = VA::ElementType; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         Self { |  | ||||||
|             element: self.element.clone(), |  | ||||||
|             len: self.len, |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> { |  | ||||||
|     pub fn element(&self) -> &VA::ElementType { |  | ||||||
|         &self.element |  | ||||||
|     } |  | ||||||
|     pub fn len(&self) -> usize { |  | ||||||
|         VA::len_from_len_type(self.len) |  | ||||||
|     } |  | ||||||
|     pub fn is_empty(&self) -> bool { |  | ||||||
|         self.len() == 0 |  | ||||||
|     } |  | ||||||
|     pub fn bit_width(&self) -> usize { |  | ||||||
|         self.bit_width |  | ||||||
|     } |  | ||||||
|     pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { |  | ||||||
|         ArrayType { |  | ||||||
|             len: self.len(), |  | ||||||
|             element: self.element, |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_with_len(element: VA::ElementType, len: usize) -> Self { |  | ||||||
|         Self::new_with_len_type( |  | ||||||
|             element, |  | ||||||
|             VA::try_len_type_from_len(len).expect("length should match"), |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self { |  | ||||||
|         let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else { |  | ||||||
|             panic!("array is too big: bit-width overflowed"); |  | ||||||
|         }; |         }; | ||||||
|         ArrayType { |         TypeProperties { | ||||||
|             element, |             is_passive, | ||||||
|             len, |             is_storable, | ||||||
|  |             is_castable_from_bits, | ||||||
|             bit_width, |             bit_width, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     pub fn new(element: T, len: Len::SizeType) -> Self { | ||||||
|  |         let type_properties = | ||||||
|  |             Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len)); | ||||||
|  |         Self { | ||||||
|  |             element: LazyInterned::Interned(element.intern_sized()), | ||||||
|  |             len, | ||||||
|  |             type_properties, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn element(&self) -> T { | ||||||
|  |         *self.element | ||||||
|  |     } | ||||||
|  |     pub fn len(self) -> usize { | ||||||
|  |         Len::as_usize(self.len) | ||||||
|  |     } | ||||||
|  |     pub fn is_empty(self) -> bool { | ||||||
|  |         self.len() == 0 | ||||||
|  |     } | ||||||
|  |     pub fn type_properties(self) -> TypeProperties { | ||||||
|  |         self.type_properties | ||||||
|  |     } | ||||||
|  |     pub fn as_dyn_array(self) -> Array { | ||||||
|  |         Array::new_dyn(self.element().canonical(), self.len()) | ||||||
|  |     } | ||||||
|  |     pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool { | ||||||
|  |         self.len() == rhs.len() | ||||||
|  |             && self | ||||||
|  |                 .element() | ||||||
|  |                 .canonical() | ||||||
|  |                 .can_connect(rhs.element().canonical()) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA> | impl<T: Type, Len: KnownSize> ArrayType<T, Len> { | ||||||
| where |     pub fn new_static(element: T) -> Self { | ||||||
|     VA::ElementType: Fold<State>, |         Self::new(element, Len::SizeType::default()) | ||||||
| { |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> { | ||||||
|     fn fold(self, state: &mut State) -> Result<Self, State::Error> { |     fn fold(self, state: &mut State) -> Result<Self, State::Error> { | ||||||
|         state.fold_array_type(self) |         state.fold_array_type(self) | ||||||
|     } |     } | ||||||
|     fn default_fold(self, state: &mut State) -> Result<Self, State::Error> { | 
 | ||||||
|         Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) |     fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> { | ||||||
|  |         Ok(ArrayType::new(self.element().fold(state)?, self.len)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> | impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State> | ||||||
| where |     for ArrayType<T, Len> | ||||||
|     VA::ElementType: Visit<State>, |  | ||||||
| { | { | ||||||
|     fn visit(&self, state: &mut State) -> Result<(), State::Error> { |     fn visit(&self, state: &mut State) -> Result<(), State::Error> { | ||||||
|         state.visit_array_type(self) |         state.visit_array_type(self) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { |     fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { | ||||||
|         self.element.visit(state) |         self.element().visit(state) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> { | impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|     pub fn new_array(element: V::Type) -> Self { |     type BaseType = Array; | ||||||
|         ArrayType::new_with_len_type(element, ()) |     type MaskType = ArrayType<T::MaskType, Len>; | ||||||
|     } |     type MatchVariant = Len::ArrayMatch<T>; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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; |  | ||||||
|     type MatchActiveScope = (); |     type MatchActiveScope = (); | ||||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>; |     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; | ||||||
|     type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; |     type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; | ||||||
| 
 | 
 | ||||||
|     fn match_variants<IO: BundleValue>( |     fn match_variants( | ||||||
|         this: Expr<Self::Value>, |         this: Expr<Self>, | ||||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, |  | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Self::MatchVariantsIter |     ) -> Self::MatchVariantsIter { | ||||||
|     where |         let base = Expr::as_dyn_array(this); | ||||||
|         IO::Type: BundleType<Value = IO>, |         let base_ty = Expr::ty(base); | ||||||
|     { |  | ||||||
|         let _ = module_builder; |  | ||||||
|         let _ = source_location; |         let _ = source_location; | ||||||
|         std::iter::once(MatchVariantWithoutScope(VA::make_match(this))) |         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||||
|  |         std::iter::once(MatchVariantWithoutScope( | ||||||
|  |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|  |                 .ok() | ||||||
|  |                 .expect("unreachable"), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         #[derive(Clone, Hash, Eq, PartialEq)] |         ArrayType::new(self.element().mask_type(), self.len) | ||||||
|         struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>); |  | ||||||
|         impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {} |  | ||||||
|         impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> { |  | ||||||
|             type Input = ArrayType<T::ValueArrayOrSlice>; |  | ||||||
|             type InputOwned = ArrayType<T::ValueArrayOrSlice>; |  | ||||||
|             type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType; |  | ||||||
| 
 |  | ||||||
|             fn inner(self, input: &Self::Input) -> Self::Output { |  | ||||||
|                 ArrayType::new_with_len_type(input.element.mask_type(), input.len) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         ArrayMaskTypeMemoize::<Self>(PhantomData).get(self) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |     fn canonical(&self) -> CanonicalType { | ||||||
|         ArrayType { |         CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len())) | ||||||
|             element: self.element.canonical_dyn(), |  | ||||||
|             len: self.len(), |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn source_location(&self) -> SourceLocation { |     #[track_caller] | ||||||
|  |     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|  |         let CanonicalType::Array(array) = canonical_type else { | ||||||
|  |             panic!("expected array"); | ||||||
|  |         }; | ||||||
|  |         Self::new( | ||||||
|  |             T::from_canonical(array.element()), | ||||||
|  |             Len::from_usize(array.len()), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         SourceLocation::builtin() | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     fn type_enum(&self) -> TypeEnum { | impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||||
|         TypeEnum::ArrayType(self.canonical()) |     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { | ||||||
|     } |         let base = Expr::as_dyn_array(*this); | ||||||
| 
 |         let base_ty = Expr::ty(base); | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||||
|         Self { |         Interned::<_>::into_inner(Intern::intern_sized( | ||||||
|             element: VA::ElementType::from_dyn_canonical_type(t.element), |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|             len: VA::try_len_type_from_len(t.len).expect("length should match"), |                 .ok() | ||||||
|             bit_width: t.bit_width, |                 .expect("unreachable"), | ||||||
|         } |         )) | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>( |  | ||||||
|             this, |  | ||||||
|         )?) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||||
|     for ArrayType<Lhs> | pub struct ArrayWithoutGenerics; | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| impl CanonicalType for ArrayType<[DynCanonicalValue]> { | impl<T: Type> Index<T> for ArrayWithoutGenerics { | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; |     type Output = ArrayWithoutLen<T>; | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, PartialEq, Eq, Hash)] |     fn index(&self, element: T) -> &Self::Output { | ||||||
| pub struct Array<VA: ValueArrayOrSlice + ?Sized> { |         Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) | ||||||
|     element_ty: VA::ElementType, |  | ||||||
|     value: Arc<VA>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         Self { |  | ||||||
|             element_ty: self.element_ty.clone(), |  | ||||||
|             value: self.value.clone(), |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> { | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     type Type = ArrayType<VA>; | pub struct ArrayWithoutLen<T: Type> { | ||||||
|  |     element: T, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     fn ty(&self) -> Self::Type { | impl<T: Type> Index<usize> for ArrayWithoutLen<T> { | ||||||
|         ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) |     type Output = Array<T>; | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |     fn index(&self, len: usize) -> &Self::Output { | ||||||
|         Expr::from_value(self) |         Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> { | impl<T: Type, const LEN: usize> Index<ConstUsize<LEN>> for ArrayWithoutLen<T> | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         Array { |  | ||||||
|             element_ty: self.element_ty.canonical_dyn(), |  | ||||||
|             value: AsRef::<[_]>::as_ref(&*self.value) |  | ||||||
|                 .iter() |  | ||||||
|                 .map(|v| v.to_canonical_dyn()) |  | ||||||
|                 .collect(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         #[derive(Hash, Eq, PartialEq)] |  | ||||||
|         struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>); |  | ||||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> { |  | ||||||
|             fn clone(&self) -> Self { |  | ||||||
|                 *self |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {} |  | ||||||
|         impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> { |  | ||||||
|             type Input = Array<VA>; |  | ||||||
|             type InputOwned = Array<VA>; |  | ||||||
|             type Output = Interned<BitSlice>; |  | ||||||
| 
 |  | ||||||
|             fn inner(self, input: &Self::Input) -> Self::Output { |  | ||||||
|                 let mut bits = BitVec::with_capacity(input.ty().bit_width()); |  | ||||||
|                 for element in AsRef::<[_]>::as_ref(&*input.value).iter() { |  | ||||||
|                     bits.extend_from_bitslice(&element.to_bits()); |  | ||||||
|                 } |  | ||||||
|                 Intern::intern_owned(bits) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         ArrayToBitsMemoize::<VA>(PhantomData).get(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for Array<[DynCanonicalValue]> { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::Array(this.clone()) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         Value::to_bits_impl(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> { |  | ||||||
|     pub fn element_ty(&self) -> &VA::ElementType { |  | ||||||
|         &self.element_ty |  | ||||||
|     } |  | ||||||
|     pub fn len(&self) -> usize { |  | ||||||
|         VA::len_from_len_type(self.value.len_type()) |  | ||||||
|     } |  | ||||||
|     pub fn is_empty(&self) -> bool { |  | ||||||
|         self.len() == 0 |  | ||||||
|     } |  | ||||||
|     pub fn value(&self) -> &Arc<VA> { |  | ||||||
|         &self.value |  | ||||||
|     } |  | ||||||
|     pub fn set_element(&mut self, index: usize, element: VA::Element) { |  | ||||||
|         assert_eq!(self.element_ty, element.ty()); |  | ||||||
|         VA::arc_make_mut(&mut self.value)[index] = element; |  | ||||||
|     } |  | ||||||
|     pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self { |  | ||||||
|         for element in value.iter() { |  | ||||||
|             assert_eq!(element_ty, element.ty()); |  | ||||||
|         } |  | ||||||
|         Self { element_ty, value } |  | ||||||
|     } |  | ||||||
|     pub fn into_slice(self) -> Array<[VA::Element]> { |  | ||||||
|         Array { |  | ||||||
|             element_ty: self.element_ty, |  | ||||||
|             value: self.value.arc_to_arc_slice(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA> |  | ||||||
| where | where | ||||||
|     VA::Element: StaticValue, |     ConstUsize<LEN>: KnownSize, | ||||||
| { | { | ||||||
|     fn from(value: T) -> Self { |     type Output = Array<T, LEN>; | ||||||
|         Self::new(StaticType::static_type(), value.into()) | 
 | ||||||
|     } |     fn index(&self, len: ConstUsize<LEN>) -> &Self::Output { | ||||||
| } |         Interned::<_>::into_inner(Intern::intern_sized(Array::new(self.element, len))) | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] { |  | ||||||
|     type Type = ArrayType<[T::Value]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         ArrayType::new_with_len_type(StaticType::static_type(), self.len()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         let elements = Intern::intern_owned(Vec::from_iter( |  | ||||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), |  | ||||||
|         )); |  | ||||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> { |  | ||||||
|     type Type = ArrayType<[T::Value]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         <[E]>::ty(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         <[E]>::to_expr(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] { |  | ||||||
|     type Type = ArrayType<[T::Value; N]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         ArrayType::new_with_len_type(StaticType::static_type(), ()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         let elements = Intern::intern_owned(Vec::from_iter( |  | ||||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), |  | ||||||
|         )); |  | ||||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct ArrayIntoIter<VA: ValueArrayOrSlice> { |  | ||||||
|     array: Arc<VA>, |  | ||||||
|     indexes: std::ops::Range<usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> { |  | ||||||
|     type Item = VA::Element; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(self.array[self.indexes.next()?].clone()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn size_hint(&self) -> (usize, Option<usize>) { |  | ||||||
|         self.indexes.size_hint() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(self.array[self.indexes.next_back()?].clone()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Array<VA> { |  | ||||||
|     pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> { |  | ||||||
|         self.value.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> { |  | ||||||
|     type Item = &'a VA::Element; |  | ||||||
|     type IntoIter = std::slice::Iter<'a, VA::Element>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.value.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> { |  | ||||||
|     type Item = VA::Element; |  | ||||||
|     type IntoIter = ArrayIntoIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         ArrayIntoIter { |  | ||||||
|             indexes: 0..self.len(), |  | ||||||
|             array: self.value, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct ArrayExprIter<VA: ValueArrayOrSlice> { |  | ||||||
|     array: Expr<Array<VA>>, |  | ||||||
|     indexes: std::ops::Range<usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next()?)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn size_hint(&self) -> (usize, Option<usize>) { |  | ||||||
|         self.indexes.size_hint() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
|     type IntoIter = ArrayExprIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
|     type IntoIter = ArrayExprIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Expr<Array<VA>> { |  | ||||||
|     pub fn len(self) -> usize { |  | ||||||
|         self.canonical_type().len() |  | ||||||
|     } |  | ||||||
|     pub fn is_empty(self) -> bool { |  | ||||||
|         self.canonical_type().is_empty() |  | ||||||
|     } |  | ||||||
|     pub fn iter(self) -> ArrayExprIter<VA> { |  | ||||||
|         ArrayExprIter { |  | ||||||
|             indexes: 0..self.len(), |  | ||||||
|             array: self, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -2,111 +2,61 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{Expr, ToExpr}, |     expr::{Expr, ToExpr}, | ||||||
|     int::{UInt, UIntType}, |     hdl, | ||||||
|     intern::Interned, |     int::Bool, | ||||||
|     reset::Reset, |     reset::Reset, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||||
|         impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, |  | ||||||
|         DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |  | ||||||
|     util::interned_bit, |  | ||||||
| }; | }; | ||||||
| use bitvec::slice::BitSlice; |  | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||||
| pub struct ClockType; | pub struct Clock; | ||||||
| 
 | 
 | ||||||
| impl ClockType { | impl Type for Clock { | ||||||
|     pub const fn new() -> Self { |     type BaseType = Clock; | ||||||
|         Self |     type MaskType = Bool; | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| impl Type for ClockType { |     impl_match_variant_as_self!(); | ||||||
|     type Value = Clock; |  | ||||||
|     type CanonicalType = ClockType; |  | ||||||
|     type CanonicalValue = Clock; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
| 
 |  | ||||||
|     impl_match_values_as_self!(); |  | ||||||
| 
 | 
 | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         UIntType::new() |         Bool | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn type_enum(&self) -> TypeEnum { |     fn canonical(&self) -> CanonicalType { | ||||||
|         TypeEnum::Clock(*self) |         CanonicalType::Clock(*self) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |     fn source_location() -> SourceLocation { | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location(&self) -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |         SourceLocation::builtin() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|         Some(this) |         let CanonicalType::Clock(retval) = canonical_type else { | ||||||
|  |             panic!("expected Clock"); | ||||||
|  |         }; | ||||||
|  |         retval | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Connect<Self> for ClockType {} | impl Clock { | ||||||
| 
 |     pub fn type_properties(self) -> TypeProperties { | ||||||
| impl CanonicalType for ClockType { |         Self::TYPE_PROPERTIES | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock; |     } | ||||||
| } |     pub fn can_connect(self, _rhs: Self) -> bool { | ||||||
| 
 |         true | ||||||
| impl StaticType for ClockType { |  | ||||||
|     fn static_type() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | impl StaticType for Clock { | ||||||
| pub struct Clock(pub bool); |     const TYPE: Self = Self; | ||||||
| 
 |     const MASK_TYPE: Self::MaskType = Bool; | ||||||
| impl ToExpr for Clock { |     const TYPE_PROPERTIES: TypeProperties = TypeProperties { | ||||||
|     type Type = ClockType; |         is_passive: true, | ||||||
| 
 |         is_storable: false, | ||||||
|     fn ty(&self) -> Self::Type { |         is_castable_from_bits: true, | ||||||
|         ClockType |         bit_width: 1, | ||||||
|     } |     }; | ||||||
| 
 |     const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; | ||||||
|     fn to_expr(&self) -> Expr<Self> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Value for Clock { |  | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for Clock { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::Clock(*this) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)] |  | ||||||
| #[hdl(static, outline_generated)] |  | ||||||
| pub struct ClockDomain { |  | ||||||
|     pub clk: Clock, |  | ||||||
|     pub rst: Reset, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait ToClock { | pub trait ToClock { | ||||||
|  | @ -137,10 +87,10 @@ impl ToClock for Expr<Clock> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToClock for Clock { | #[hdl] | ||||||
|     fn to_clock(&self) -> Expr<Clock> { | pub struct ClockDomain { | ||||||
|         self.to_expr() |     pub clk: Clock, | ||||||
|     } |     pub rst: Reset, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToClock for bool { | impl ToClock for bool { | ||||||
|  | @ -148,9 +98,3 @@ impl ToClock for bool { | ||||||
|         self.to_expr().to_clock() |         self.to_expr().to_clock() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| impl ToClock for UInt<1> { |  | ||||||
|     fn to_clock(&self) -> Expr<Clock> { |  | ||||||
|         self.to_expr().to_clock() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -1,190 +1,155 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| #![allow(clippy::type_complexity)] | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     bundle::{BundleValue, TypeHintTrait}, |  | ||||||
|     expr::{ops::VariantAccess, Expr, ToExpr}, |     expr::{ops::VariantAccess, Expr, ToExpr}, | ||||||
|     int::{UInt, UIntType}, |     hdl, | ||||||
|     intern::{Intern, Interned, MemoizeGeneric}, |     int::Bool, | ||||||
|  |     intern::{Intern, Interned}, | ||||||
|     module::{ |     module::{ | ||||||
|         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, |         enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl, | ||||||
|         NormalModule, Scope, |         EnumMatchVariantsIterImpl, Scope, | ||||||
|     }, |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties}, | ||||||
|         CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, |  | ||||||
|         DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; |  | ||||||
| use hashbrown::HashMap; | use hashbrown::HashMap; | ||||||
| use std::{ | use std::iter::FusedIterator; | ||||||
|     borrow::Cow, |  | ||||||
|     fmt, |  | ||||||
|     hash::{Hash, Hasher}, |  | ||||||
|     iter::FusedIterator, |  | ||||||
|     marker::PhantomData, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
| pub struct VariantType<T> { | pub struct EnumVariant { | ||||||
|     pub name: Interned<str>, |     pub name: Interned<str>, | ||||||
|     pub ty: Option<T>, |     pub ty: Option<CanonicalType>, | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>); |  | ||||||
| 
 |  | ||||||
| impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         let VariantType { name, ref ty } = *self.0; |  | ||||||
|         if let Some(ty) = ty { |  | ||||||
|             write!(f, "{name}({ty:?})") |  | ||||||
|         } else { |  | ||||||
|             write!(f, "{name}") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         fmt::Debug::fmt(self, f) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T> VariantType<T> { |  | ||||||
|     pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> { |  | ||||||
|         let Self { name, ty } = self; |  | ||||||
|         VariantType { name, ty: f(ty) } |  | ||||||
|     } |  | ||||||
|     pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> { |  | ||||||
|         let Self { name, ty } = self; |  | ||||||
|         VariantType { |  | ||||||
|             name, |  | ||||||
|             ty: ty.map(f), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn as_ref_ty(&self) -> VariantType<&T> { |  | ||||||
|         VariantType { |  | ||||||
|             name: self.name, |  | ||||||
|             ty: self.ty.as_ref(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> { |  | ||||||
|         FmtDebugInEnum(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type> VariantType<T> { |  | ||||||
|     pub fn canonical(&self) -> VariantType<T::CanonicalType> { |  | ||||||
|         self.as_ref_ty().map_ty(T::canonical) |  | ||||||
|     } |  | ||||||
|     pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> { |  | ||||||
|         self.as_ref_ty().map_ty(T::to_dyn) |  | ||||||
|     } |  | ||||||
|     pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> { |  | ||||||
|         self.as_ref_ty().map_ty(T::canonical_dyn) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl VariantType<Interned<dyn DynCanonicalType>> { |  | ||||||
|     pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T { |  | ||||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); |  | ||||||
|         let Some(ty) = self.ty else { |  | ||||||
|             panic!("variant {expected_name} has no value but a value is expected"); |  | ||||||
|         }; |  | ||||||
|         T::from_dyn_canonical_type(ty) |  | ||||||
|     } |  | ||||||
|     pub fn from_canonical_type_helper_no_value(self, expected_name: &str) { |  | ||||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); |  | ||||||
|         assert!( |  | ||||||
|             self.ty.is_none(), |  | ||||||
|             "variant {expected_name} has a value but is expected to have no value" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq)] | #[derive(Clone, Eq)] | ||||||
| struct DynEnumTypeImpl { | struct EnumImpl { | ||||||
|     variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, |     variants: Interned<[EnumVariant]>, | ||||||
|     name_indexes: HashMap<Interned<str>, usize>, |     name_indexes: HashMap<Interned<str>, usize>, | ||||||
|     bit_width: usize, |     type_properties: TypeProperties, | ||||||
|     is_storable: bool, |  | ||||||
|     is_castable_from_bits: bool, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for DynEnumTypeImpl { | impl std::fmt::Debug for EnumImpl { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         write!(f, "DynEnumType ")?; |         f.debug_struct("Enum") | ||||||
|         f.debug_set() |             .field("variants", &self.variants) | ||||||
|             .entries( |             .field("name_indexes", &self.name_indexes) | ||||||
|                 self.variants |             .field("type_properties", &self.type_properties) | ||||||
|                     .iter() |  | ||||||
|                     .map(|variant| variant.fmt_debug_in_enum()), |  | ||||||
|             ) |  | ||||||
|             .finish() |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PartialEq for DynEnumTypeImpl { | impl std::hash::Hash for EnumImpl { | ||||||
|  |     fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||||||
|  |         self.variants.hash(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PartialEq for EnumImpl { | ||||||
|     fn eq(&self, other: &Self) -> bool { |     fn eq(&self, other: &Self) -> bool { | ||||||
|         self.variants == other.variants |         self.variants == other.variants | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Hash for DynEnumTypeImpl { | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | pub struct Enum(Interned<EnumImpl>); | ||||||
|         self.variants.hash(state); | 
 | ||||||
|  | const fn discriminant_bit_width_impl(variant_count: usize) -> usize { | ||||||
|  |     match variant_count.next_power_of_two().checked_ilog2() { | ||||||
|  |         Some(x) => x as usize, | ||||||
|  |         None => 0, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Hash, PartialEq, Eq)] | #[derive(Clone)] | ||||||
| pub struct DynEnumType(Interned<DynEnumTypeImpl>); | pub struct EnumTypePropertiesBuilder { | ||||||
| 
 |     type_properties: TypeProperties, | ||||||
| impl fmt::Debug for DynEnumType { |     variant_count: usize, | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         self.0.fmt(f) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn discriminant_bit_width_impl(variant_count: usize) -> usize { | impl EnumTypePropertiesBuilder { | ||||||
|     variant_count |     #[must_use] | ||||||
|         .next_power_of_two() |     pub const fn new() -> Self { | ||||||
|         .checked_ilog2() |         Self { | ||||||
|         .unwrap_or(0) as usize |             type_properties: TypeProperties { | ||||||
| } |                 is_passive: true, | ||||||
| 
 |                 is_storable: true, | ||||||
| impl DynEnumType { |                 is_castable_from_bits: true, | ||||||
|     #[track_caller] |                 bit_width: 0, | ||||||
|     pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { |             }, | ||||||
|         assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); |             variant_count: 0, | ||||||
|         let mut name_indexes = HashMap::with_capacity(variants.len()); |  | ||||||
|         let mut body_bit_width = 0usize; |  | ||||||
|         let mut is_storable = true; |  | ||||||
|         let mut is_castable_from_bits = true; |  | ||||||
|         for (index, &VariantType { name, ty }) in variants.iter().enumerate() { |  | ||||||
|             if let Some(old_index) = name_indexes.insert(name, index) { |  | ||||||
|                 panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); |  | ||||||
|             } |  | ||||||
|             if let Some(ty) = ty { |  | ||||||
|                 assert!( |  | ||||||
|                     ty.is_passive(), |  | ||||||
|                     "variant type must be a passive type: {ty:?}" |  | ||||||
|                 ); |  | ||||||
|                 body_bit_width = body_bit_width.max(ty.bit_width()); |  | ||||||
|                 is_storable &= ty.is_storable(); |  | ||||||
|                 is_castable_from_bits &= ty.is_castable_from_bits(); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|         let bit_width = body_bit_width |     pub const fn clone(&self) -> Self { | ||||||
|             .checked_add(discriminant_bit_width_impl(variants.len())) |         Self { ..*self } | ||||||
|             .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); |     } | ||||||
|         Self( |     #[must_use] | ||||||
|             DynEnumTypeImpl { |     pub const fn variant(self, field_props: Option<TypeProperties>) -> Self { | ||||||
|                 variants, |         let Self { | ||||||
|                 name_indexes, |             mut type_properties, | ||||||
|                 bit_width, |             variant_count, | ||||||
|  |         } = self; | ||||||
|  |         if let Some(TypeProperties { | ||||||
|  |             is_passive, | ||||||
|             is_storable, |             is_storable, | ||||||
|             is_castable_from_bits, |             is_castable_from_bits, | ||||||
|  |             bit_width, | ||||||
|  |         }) = field_props | ||||||
|  |         { | ||||||
|  |             assert!(is_passive, "variant type must be a passive type"); | ||||||
|  |             type_properties = TypeProperties { | ||||||
|  |                 is_passive: true, | ||||||
|  |                 is_storable: type_properties.is_storable & is_storable, | ||||||
|  |                 is_castable_from_bits: type_properties.is_castable_from_bits | ||||||
|  |                     & is_castable_from_bits, | ||||||
|  |                 bit_width: if type_properties.bit_width < bit_width { | ||||||
|  |                     bit_width | ||||||
|  |                 } else { | ||||||
|  |                     type_properties.bit_width | ||||||
|  |                 }, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         Self { | ||||||
|  |             type_properties, | ||||||
|  |             variant_count: variant_count + 1, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub const fn finish(self) -> TypeProperties { | ||||||
|  |         assert!( | ||||||
|  |             self.variant_count != 0, | ||||||
|  |             "zero-variant enums aren't yet supported: \ | ||||||
|  |             https://github.com/chipsalliance/firrtl-spec/issues/208",
 | ||||||
|  |         ); | ||||||
|  |         let Some(bit_width) = self | ||||||
|  |             .type_properties | ||||||
|  |             .bit_width | ||||||
|  |             .checked_add(discriminant_bit_width_impl(self.variant_count)) | ||||||
|  |         else { | ||||||
|  |             panic!("enum is too big: bit-width overflowed"); | ||||||
|  |         }; | ||||||
|  |         TypeProperties { | ||||||
|  |             bit_width, | ||||||
|  |             ..self.type_properties | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Enum { | ||||||
|  |     #[track_caller] | ||||||
|  |     pub fn new(variants: Interned<[EnumVariant]>) -> Self { | ||||||
|  |         let mut name_indexes = HashMap::with_capacity(variants.len()); | ||||||
|  |         let mut type_props_builder = EnumTypePropertiesBuilder::new(); | ||||||
|  |         for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { | ||||||
|  |             if let Some(old_index) = name_indexes.insert(*name, index) { | ||||||
|  |                 panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); | ||||||
|  |             } | ||||||
|  |             type_props_builder = type_props_builder.variant(ty.map(CanonicalType::type_properties)); | ||||||
|  |         } | ||||||
|  |         Self( | ||||||
|  |             EnumImpl { | ||||||
|  |                 variants, | ||||||
|  |                 name_indexes, | ||||||
|  |                 type_properties: type_props_builder.finish(), | ||||||
|             } |             } | ||||||
|             .intern_sized(), |             .intern_sized(), | ||||||
|         ) |         ) | ||||||
|  | @ -192,233 +157,62 @@ impl DynEnumType { | ||||||
|     pub fn discriminant_bit_width(self) -> usize { |     pub fn discriminant_bit_width(self) -> usize { | ||||||
|         discriminant_bit_width_impl(self.variants().len()) |         discriminant_bit_width_impl(self.variants().len()) | ||||||
|     } |     } | ||||||
|     pub fn is_passive(self) -> bool { |     pub fn type_properties(self) -> TypeProperties { | ||||||
|         true |         self.0.type_properties | ||||||
|     } |  | ||||||
|     pub fn is_storable(self) -> bool { |  | ||||||
|         self.0.is_storable |  | ||||||
|     } |  | ||||||
|     pub fn is_castable_from_bits(self) -> bool { |  | ||||||
|         self.0.is_castable_from_bits |  | ||||||
|     } |  | ||||||
|     pub fn bit_width(self) -> usize { |  | ||||||
|         self.0.bit_width |  | ||||||
|     } |     } | ||||||
|     pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { |     pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { | ||||||
|         &self.0.name_indexes |         &self.0.name_indexes | ||||||
|     } |     } | ||||||
| } |     pub fn can_connect(self, rhs: Self) -> bool { | ||||||
| 
 |         if self.0.variants.len() != rhs.0.variants.len() { | ||||||
| #[derive(Debug, Clone, Hash, PartialEq, Eq)] |             return false; | ||||||
| pub struct DynEnum { |         } | ||||||
|     ty: DynEnumType, |         for ( | ||||||
|     variant_index: usize, |             &EnumVariant { | ||||||
|     variant_value: Option<DynCanonicalValue>, |                 name: lhs_name, | ||||||
| } |                 ty: lhs_ty, | ||||||
| 
 |             }, | ||||||
| impl DynEnum { |             &EnumVariant { | ||||||
|     #[track_caller] |                 name: rhs_name, | ||||||
|     pub fn new_by_index( |                 ty: rhs_ty, | ||||||
|         ty: DynEnumType, |             }, | ||||||
|         variant_index: usize, |         ) in self.0.variants.iter().zip(rhs.0.variants.iter()) | ||||||
|         variant_value: Option<DynCanonicalValue>, |         { | ||||||
|     ) -> Self { |             if lhs_name != rhs_name { | ||||||
|         let variant = ty.variants()[variant_index]; |                 return false; | ||||||
|         assert_eq!( |             } | ||||||
|             variant_value.as_ref().map(|v| v.ty()), |             match (lhs_ty, rhs_ty) { | ||||||
|             variant.ty, |                 (None, None) => {} | ||||||
|             "variant value doesn't match type" |                 (None, Some(_)) | (Some(_), None) => return false, | ||||||
|         ); |                 (Some(lhs_ty), Some(rhs_ty)) => { | ||||||
|         Self { |                     if !lhs_ty.can_connect(rhs_ty) { | ||||||
|             ty, |                         return false; | ||||||
|             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" |  | ||||||
|                 )), |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         true | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait EnumType: | pub trait EnumType: | ||||||
|     Type< |     Type< | ||||||
|         CanonicalType = DynEnumType, |     BaseType = Enum, | ||||||
|         CanonicalValue = DynEnum, |     MaskType = Bool, | ||||||
|         MaskType = UIntType<1>, |  | ||||||
|         MaskValue = UInt<1>, |  | ||||||
|     MatchActiveScope = Scope, |     MatchActiveScope = Scope, | ||||||
|     MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, |     MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, | ||||||
|     MatchVariantsIter = EnumMatchVariantsIter<Self>, |     MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||||
|     > + Connect<Self> | > | ||||||
| where |  | ||||||
|     Self::Value: EnumValue + ToExpr<Type = Self>, |  | ||||||
| { | { | ||||||
|     type Builder; |     fn variants(&self) -> Interned<[EnumVariant]>; | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         v: Self::MatchVariantAndInactiveScope, | ||||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope); |     ) -> (Self::MatchVariant, Self::MatchActiveScope); | ||||||
|     fn builder() -> Self::Builder; |  | ||||||
|     fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>; |  | ||||||
|     fn variants_hint() -> VariantsHint; |  | ||||||
|     #[allow(clippy::result_unit_err)] |  | ||||||
|     fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>( |  | ||||||
|         &self, |  | ||||||
|         variant_index: usize, |  | ||||||
|         variant_value: Option<&VariantValue>, |  | ||||||
|     ) -> Result<Interned<BitSlice>, ()> { |  | ||||||
|         #[derive(Hash, Eq, PartialEq)] |  | ||||||
|         struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>); |  | ||||||
|         impl<E, V> Clone for VariantToBitsMemoize<E, V> { |  | ||||||
|             fn clone(&self) -> Self { |  | ||||||
|                 *self |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         impl<E, V> Copy for VariantToBitsMemoize<E, V> {} |  | ||||||
|         impl< |  | ||||||
|                 E: EnumType<Value: EnumValue<Type = E>>, |  | ||||||
|                 V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone, |  | ||||||
|             > MemoizeGeneric for VariantToBitsMemoize<E, V> |  | ||||||
|         { |  | ||||||
|             type InputRef<'a> = (&'a E, usize, Option<&'a V>); |  | ||||||
|             type InputOwned = (E, usize, Option<V>); |  | ||||||
|             type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>); |  | ||||||
|             type Output = Result<Interned<BitSlice>, ()>; |  | ||||||
| 
 |  | ||||||
|             fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { |  | ||||||
|                 (&input.0, input.1, input.2.as_ref()) |  | ||||||
|             } |  | ||||||
|             fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { |  | ||||||
|                 a == b |  | ||||||
|             } |  | ||||||
|             fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { |  | ||||||
|                 (input.0.into_owned(), input.1, input.2.map(Cow::into_owned)) |  | ||||||
|             } |  | ||||||
|             fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { |  | ||||||
|                 (&input.0, input.1, input.2.as_deref()) |  | ||||||
|             } |  | ||||||
|             fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { |  | ||||||
|                 (Cow::Owned(input.0), input.1, input.2.map(Cow::Owned)) |  | ||||||
|             } |  | ||||||
|             fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { |  | ||||||
|                 (Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed)) |  | ||||||
|             } |  | ||||||
|             fn inner(self, input: Self::InputRef<'_>) -> Self::Output { |  | ||||||
|                 let (ty, variant_index, variant_value) = input; |  | ||||||
|                 let ty = ty.canonical(); |  | ||||||
|                 let mut bits = BitVec::with_capacity(ty.bit_width()); |  | ||||||
|                 bits.extend_from_bitslice( |  | ||||||
|                     &variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()], |  | ||||||
|                 ); |  | ||||||
|                 if let Some(variant_value) = variant_value { |  | ||||||
|                     bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?); |  | ||||||
|                 } |  | ||||||
|                 bits.resize(ty.bit_width(), false); |  | ||||||
|                 Ok(Intern::intern_owned(bits)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get(( |  | ||||||
|             self, |  | ||||||
|             variant_index, |  | ||||||
|             variant_value, |  | ||||||
|         )) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait EnumValue: Value | pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>); | ||||||
| where |  | ||||||
|     <Self as ToExpr>::Type: EnumType<Value = Self>, |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>) | impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>; |  | ||||||
| 
 |  | ||||||
| impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> |  | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     type MatchVariant = T::MatchVariant; |     type MatchVariant = T::MatchVariant; | ||||||
|     type MatchActiveScope = Scope; |     type MatchActiveScope = Scope; | ||||||
| 
 | 
 | ||||||
|  | @ -427,36 +221,22 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> | impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> { | ||||||
| where |     pub fn variant_access(&self) -> VariantAccess { | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> { |  | ||||||
|         self.0.variant_access() |         self.0.variant_access() | ||||||
|     } |     } | ||||||
|     pub fn activate( |     pub fn activate(self) -> (VariantAccess, Scope) { | ||||||
|         self, |  | ||||||
|     ) -> ( |  | ||||||
|         Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>, |  | ||||||
|         Scope, |  | ||||||
|     ) { |  | ||||||
|         self.0.activate() |         self.0.activate() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct EnumMatchVariantsIter<T: EnumType> | pub struct EnumMatchVariantsIter<T: EnumType> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     pub(crate) inner: EnumMatchVariantsIterImpl<T>, |     pub(crate) inner: EnumMatchVariantsIterImpl<T>, | ||||||
|     pub(crate) variant_index: std::ops::Range<usize>, |     pub(crate) variant_index: std::ops::Range<usize>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> | impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     type Item = EnumMatchVariantAndInactiveScope<T>; |     type Item = EnumMatchVariantAndInactiveScope<T>; | ||||||
| 
 | 
 | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|  | @ -470,21 +250,15 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> | impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     fn len(&self) -> usize { |     fn len(&self) -> usize { | ||||||
|         self.variant_index.len() |         self.variant_index.len() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {} | impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {} | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> | impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |     fn next_back(&mut self) -> Option<Self::Item> { | ||||||
|         self.variant_index.next_back().map(|variant_index| { |         self.variant_index.next_back().map(|variant_index| { | ||||||
|             EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) |             EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) | ||||||
|  | @ -492,129 +266,55 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Type for DynEnumType { | impl EnumType for Enum { | ||||||
|     type CanonicalType = DynEnumType; |  | ||||||
|     type Value = DynEnum; |  | ||||||
|     type CanonicalValue = DynEnum; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
|     type MatchVariant = Option<Expr<DynCanonicalValue>>; |  | ||||||
|     type MatchActiveScope = Scope; |  | ||||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; |  | ||||||
|     type MatchVariantsIter = EnumMatchVariantsIter<Self>; |  | ||||||
| 
 |  | ||||||
|     fn match_variants<IO: BundleValue>( |  | ||||||
|         this: Expr<Self::Value>, |  | ||||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, |  | ||||||
|         source_location: SourceLocation, |  | ||||||
|     ) -> Self::MatchVariantsIter |  | ||||||
|     where |  | ||||||
|         IO::Type: crate::bundle::BundleType<Value = IO>, |  | ||||||
|     { |  | ||||||
|         module_builder.enum_match_variants_helper(this, source_location) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn mask_type(&self) -> Self::MaskType { |  | ||||||
|         UIntType::new() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location(&self) -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn type_enum(&self) -> TypeEnum { |  | ||||||
|         TypeEnum::EnumType(*self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |  | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Connect<Self> for DynEnumType {} |  | ||||||
| 
 |  | ||||||
| pub struct NoBuilder; |  | ||||||
| 
 |  | ||||||
| impl EnumType for DynEnumType { |  | ||||||
|     type Builder = NoBuilder; |  | ||||||
| 
 |  | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         v: Self::MatchVariantAndInactiveScope, | ||||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope) { |     ) -> (Self::MatchVariant, Self::MatchActiveScope) { | ||||||
|         let (expr, scope) = v.0.activate(); |         let (expr, scope) = v.0.activate(); | ||||||
|         (expr.variant_type().ty.map(|_| expr.to_expr()), scope) |         (expr.variant_type().map(|_| expr.to_expr()), scope) | ||||||
|     } |     } | ||||||
| 
 |     fn variants(&self) -> Interned<[EnumVariant]> { | ||||||
|     fn builder() -> Self::Builder { |  | ||||||
|         NoBuilder |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> { |  | ||||||
|         self.0.variants |         self.0.variants | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     fn variants_hint() -> VariantsHint { | impl Type for Enum { | ||||||
|         VariantsHint { |     type BaseType = Enum; | ||||||
|             known_variants: [][..].intern(), |     type MaskType = Bool; | ||||||
|             more_variants: true, |     type MatchVariant = Option<Expr<CanonicalType>>; | ||||||
|  |     type MatchActiveScope = Scope; | ||||||
|  |     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||||
|  |     type MatchVariantsIter = EnumMatchVariantsIter<Self>; | ||||||
|  | 
 | ||||||
|  |     fn match_variants( | ||||||
|  |         this: Expr<Self>, | ||||||
|  |         source_location: SourceLocation, | ||||||
|  |     ) -> Self::MatchVariantsIter { | ||||||
|  |         enum_match_variants_helper(this, source_location) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn mask_type(&self) -> Self::MaskType { | ||||||
|  |         Bool | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn canonical(&self) -> CanonicalType { | ||||||
|  |         CanonicalType::Enum(*self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[track_caller] | ||||||
|  |     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|  |         let CanonicalType::Enum(retval) = canonical_type else { | ||||||
|  |             panic!("expected enum"); | ||||||
|  |         }; | ||||||
|  |         retval | ||||||
|  |     } | ||||||
|  |     fn source_location() -> SourceLocation { | ||||||
|  |         SourceLocation::builtin() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl CanonicalType for DynEnumType { | #[hdl] | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType; | pub enum HdlOption<T: Type> { | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToExpr for DynEnum { |  | ||||||
|     type Type = DynEnumType; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         self.ty |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Value for DynEnum { |  | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         self.clone() |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         this.ty |  | ||||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) |  | ||||||
|             .unwrap() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl EnumValue for DynEnum {} |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for DynEnum { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::Enum(this.clone()) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         this.ty |  | ||||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) |  | ||||||
|             .unwrap() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| mod impl_option { |  | ||||||
|     #[allow(dead_code)] |  | ||||||
|     #[derive(crate::ty::Value)] |  | ||||||
|     #[hdl(target(std::option::Option), connect_inexact, outline_generated)] |  | ||||||
|     pub enum Option<T> { |  | ||||||
|     None, |     None, | ||||||
|     Some(T), |     Some(T), | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										432
									
								
								crates/fayalite/src/expr/target.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								crates/fayalite/src/expr/target.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,432 @@ | ||||||
|  | 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), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait GetTarget { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl GetTarget for bool { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>> { | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + GetTarget + Send + Sync + 'static> GetTarget for Interned<T> { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>> { | ||||||
|  |         T::target(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + GetTarget> GetTarget for &'_ T { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>> { | ||||||
|  |         T::target(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + GetTarget> GetTarget for &'_ mut T { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>> { | ||||||
|  |         T::target(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + GetTarget> GetTarget for Box<T> { | ||||||
|  |     fn target(&self) -> Option<Interned<Target>> { | ||||||
|  |         T::target(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -22,6 +22,179 @@ use std::{ | ||||||
| 
 | 
 | ||||||
| pub mod type_map; | pub mod type_map; | ||||||
| 
 | 
 | ||||||
|  | pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any { | ||||||
|  |     fn get(&self) -> Interned<T>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any> | ||||||
|  |     LazyInternedTrait<T> for F | ||||||
|  | { | ||||||
|  |     fn get(&self) -> Interned<T> { | ||||||
|  |         self() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[repr(transparent)] | ||||||
|  | pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>); | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {} | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> { | ||||||
|  |     fn clone(&self) -> Self { | ||||||
|  |         *self | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> { | ||||||
|  |     fn hash<H: Hasher>(&self, state: &mut H) { | ||||||
|  |         self.0.get_ptr_eq_with_type_id().hash(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {} | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> { | ||||||
|  |     fn eq(&self, other: &Self) -> bool { | ||||||
|  |         self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> { | ||||||
|  |     Interned(Interned<T>), | ||||||
|  |     Lazy(LazyInternedFn<T>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> { | ||||||
|  |     pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self { | ||||||
|  |         Self::Lazy(LazyInternedFn(v)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> { | ||||||
|  |     fn clone(&self) -> Self { | ||||||
|  |         *self | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {} | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> { | ||||||
|  |     type Target = T; | ||||||
|  | 
 | ||||||
|  |     fn deref(&self) -> &Self::Target { | ||||||
|  |         Interned::into_inner(self.interned()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {} | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T> | ||||||
|  | where | ||||||
|  |     Interned<T>: PartialEq, | ||||||
|  | { | ||||||
|  |     fn eq(&self, other: &Self) -> bool { | ||||||
|  |         self.interned() == other.interned() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T> | ||||||
|  | where | ||||||
|  |     Interned<T>: Ord, | ||||||
|  | { | ||||||
|  |     fn cmp(&self, other: &Self) -> Ordering { | ||||||
|  |         self.interned().cmp(&other.interned()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T> | ||||||
|  | where | ||||||
|  |     Interned<T>: PartialOrd, | ||||||
|  | { | ||||||
|  |     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||||||
|  |         self.interned().partial_cmp(&other.interned()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T> | ||||||
|  | where | ||||||
|  |     Interned<T>: Hash, | ||||||
|  | { | ||||||
|  |     fn hash<H: Hasher>(&self, state: &mut H) { | ||||||
|  |         self.interned().hash(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> { | ||||||
|  |     pub fn interned(self) -> Interned<T> | ||||||
|  |     where | ||||||
|  |         T: Intern, | ||||||
|  |     { | ||||||
|  |         struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>); | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> { | ||||||
|  |             fn hash<H: Hasher>(&self, _state: &mut H) {} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> { | ||||||
|  |             fn eq(&self, _other: &Self) -> bool { | ||||||
|  |                 true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {} | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> { | ||||||
|  |             fn clone(&self) -> Self { | ||||||
|  |                 *self | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {} | ||||||
|  | 
 | ||||||
|  |         impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> { | ||||||
|  |             type InputRef<'a> = LazyInternedFn<T>; | ||||||
|  | 
 | ||||||
|  |             type InputOwned = LazyInternedFn<T>; | ||||||
|  | 
 | ||||||
|  |             type InputCow<'a> = LazyInternedFn<T>; | ||||||
|  | 
 | ||||||
|  |             type Output = Interned<T>; | ||||||
|  | 
 | ||||||
|  |             fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { | ||||||
|  |                 a == b | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { | ||||||
|  |                 *input | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { | ||||||
|  |                 input | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { | ||||||
|  |                 *input | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { | ||||||
|  |                 input | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { | ||||||
|  |                 input | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             fn inner(self, input: Self::InputRef<'_>) -> Self::Output { | ||||||
|  |                 input.0.get() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         match self { | ||||||
|  |             Self::Interned(retval) => retval, | ||||||
|  |             Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub trait InternedCompare { | pub trait InternedCompare { | ||||||
|     type InternedCompareKey: Ord + Hash; |     type InternedCompareKey: Ord + Hash; | ||||||
|     fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; |     fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; | ||||||
|  | @ -471,6 +644,12 @@ pub struct Interned<T: ?Sized + 'static + Send + Sync, C: InternContext = Global | ||||||
| 
 | 
 | ||||||
| macro_rules! forward_fmt_trait { | macro_rules! forward_fmt_trait { | ||||||
|     ($Tr:ident) => { |     ($Tr:ident) => { | ||||||
|  |         impl<T: ?Sized + Intern + fmt::$Tr> fmt::$Tr for LazyInterned<T> { | ||||||
|  |             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |                 T::fmt(&**self, f) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr |         impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr, C: InternContext> fmt::$Tr | ||||||
|             for Interned<T, C> |             for Interned<T, C> | ||||||
|         { |         { | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ extern crate self as fayalite; | ||||||
| pub use std as __std; | pub use std as __std; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| #[doc(alias = "hdl")] |  | ||||||
| /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | ||||||
| /// a [`Module`][`::fayalite::module::Module`] when called.
 | /// a [`Module`][`::fayalite::module::Module`] when called.
 | ||||||
| /// In the function body it will implicitly create a
 | /// In the function body it will implicitly create a
 | ||||||
|  | @ -21,13 +20,21 @@ pub use std as __std; | ||||||
| /// See [Fayalite Modules][crate::_docs::modules]
 | /// See [Fayalite Modules][crate::_docs::modules]
 | ||||||
| pub use fayalite_proc_macros::hdl_module; | pub use fayalite_proc_macros::hdl_module; | ||||||
| 
 | 
 | ||||||
|  | #[doc(inline)] | ||||||
|  | pub use fayalite_proc_macros::hdl; | ||||||
|  | 
 | ||||||
|  | /// struct used as a placeholder when applying defaults
 | ||||||
|  | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||||
|  | pub struct __; | ||||||
|  | 
 | ||||||
| #[cfg(feature = "unstable-doc")] | #[cfg(feature = "unstable-doc")] | ||||||
| pub mod _docs; | pub mod _docs; | ||||||
| 
 | 
 | ||||||
|  | // FIXME: finish
 | ||||||
| pub mod annotations; | pub mod annotations; | ||||||
| pub mod array; | pub mod array; | ||||||
| pub mod bundle; | pub mod bundle; | ||||||
| pub mod cli; | //pub mod cli;
 | ||||||
| pub mod clock; | pub mod clock; | ||||||
| pub mod enum_; | pub mod enum_; | ||||||
| pub mod expr; | pub mod expr; | ||||||
|  | @ -41,5 +48,6 @@ pub mod reset; | ||||||
| pub mod source_location; | pub mod source_location; | ||||||
| pub mod ty; | pub mod ty; | ||||||
| pub mod util; | pub mod util; | ||||||
| pub mod valueless; | //pub mod valueless;
 | ||||||
|  | pub mod prelude; | ||||||
| pub mod wire; | pub mod wire; | ||||||
|  |  | ||||||
|  | @ -4,15 +4,16 @@ | ||||||
| #![allow(clippy::multiple_bound_locations)] | #![allow(clippy::multiple_bound_locations)] | ||||||
| use crate::{ | use crate::{ | ||||||
|     annotations::{Annotation, IntoAnnotations, TargetedAnnotation}, |     annotations::{Annotation, IntoAnnotations, TargetedAnnotation}, | ||||||
|     array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, |     array::{Array, ArrayType}, | ||||||
|     bundle::{BundleType, BundleValue, DynBundle, DynBundleType}, |     bundle::{Bundle, BundleType}, | ||||||
|     clock::{Clock, ClockType}, |     clock::Clock, | ||||||
|     expr::{Expr, ExprEnum, ExprTrait, Flow, ToExpr}, |     expr::{Expr, Flow, ToExpr, ToLiteralBits}, | ||||||
|     int::{DynUInt, DynUIntType, UInt, UIntType}, |     hdl, | ||||||
|  |     int::{Bool, DynSize, Size, UInt, UIntType}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     module::ScopedNameId, |     module::ScopedNameId, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value}, |     ty::{AsMask, CanonicalType, Type}, | ||||||
|     util::DebugAsDisplay, |     util::DebugAsDisplay, | ||||||
| }; | }; | ||||||
| use bitvec::slice::BitSlice; | use bitvec::slice::BitSlice; | ||||||
|  | @ -20,37 +21,37 @@ use std::{ | ||||||
|     cell::RefCell, |     cell::RefCell, | ||||||
|     fmt, |     fmt, | ||||||
|     hash::{Hash, Hasher}, |     hash::{Hash, Hasher}, | ||||||
|  |     marker::PhantomData, | ||||||
|     num::NonZeroU32, |     num::NonZeroU32, | ||||||
|     rc::Rc, |     rc::Rc, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | #[hdl] | ||||||
| pub struct ReadStruct<Element> { | pub struct ReadStruct<Element, AddrWidth: Size> { | ||||||
|     pub addr: DynUInt, |     pub addr: UIntType<AddrWidth>, | ||||||
|     pub en: UInt<1>, |     pub en: Bool, | ||||||
|     pub clk: Clock, |     pub clk: Clock, | ||||||
|     #[hdl(flip)] |     #[hdl(flip)] | ||||||
|     pub data: Element, |     pub data: Element, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | #[hdl] | ||||||
| pub struct WriteStruct<Element: Value> { | pub struct WriteStruct<Element, AddrWidth: Size> { | ||||||
|     pub addr: DynUInt, |     pub addr: UIntType<AddrWidth>, | ||||||
|     pub en: UInt<1>, |     pub en: Bool, | ||||||
|     pub clk: Clock, |     pub clk: Clock, | ||||||
|     pub data: Element, |     pub data: Element, | ||||||
|     pub mask: AsMask<Element>, |     pub mask: AsMask<Element>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[allow(clippy::multiple_bound_locations)] | #[hdl] | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | pub struct ReadWriteStruct<Element, AddrWidth: Size> { | ||||||
| pub struct ReadWriteStruct<Element: Value> { |     pub addr: UIntType<AddrWidth>, | ||||||
|     pub addr: DynUInt, |     pub en: Bool, | ||||||
|     pub en: UInt<1>, |  | ||||||
|     pub clk: Clock, |     pub clk: Clock, | ||||||
|     #[hdl(flip)] |     #[hdl(flip)] | ||||||
|     pub rdata: Element, |     pub rdata: Element, | ||||||
|     pub wmode: UInt<1>, |     pub wmode: Bool, | ||||||
|     pub wdata: Element, |     pub wdata: Element, | ||||||
|     pub wmask: AsMask<Element>, |     pub wmask: AsMask<Element>, | ||||||
| } | } | ||||||
|  | @ -63,11 +64,10 @@ pub trait PortType: | ||||||
|     sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static |     sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static | ||||||
| { | { | ||||||
|     type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static; |     type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static; | ||||||
|     type PortType: BundleType<Value = Self::PortValue>; |     type Port: BundleType; | ||||||
|     type PortValue: BundleValue<Type = Self::PortType>; |  | ||||||
|     fn port_kind(port_kind: Self::PortKindTy) -> PortKind; |     fn port_kind(port_kind: Self::PortKindTy) -> PortKind; | ||||||
|     fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy; |     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)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|  | @ -79,8 +79,7 @@ impl sealed::Sealed for DynPortType {} | ||||||
| 
 | 
 | ||||||
| impl PortType for DynPortType { | impl PortType for DynPortType { | ||||||
|     type PortKindTy = PortKind; |     type PortKindTy = PortKind; | ||||||
|     type PortType = DynBundleType; |     type Port = Bundle; | ||||||
|     type PortValue = DynBundle; |  | ||||||
| 
 | 
 | ||||||
|     fn port_kind(port_kind: Self::PortKindTy) -> PortKind { |     fn port_kind(port_kind: Self::PortKindTy) -> PortKind { | ||||||
|         port_kind |         port_kind | ||||||
|  | @ -90,41 +89,38 @@ impl PortType for DynPortType { | ||||||
|         port_kind |         port_kind | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn port_ty(port: &MemPort<Self>) -> Self::PortType { |     fn port_ty(port: &MemPort<Self>) -> Self::Port { | ||||||
|         match port.port_kind { |         Bundle::new(match port.port_kind { | ||||||
|             PortKind::ReadOnly => MemPort::<ReadStruct<DynCanonicalValue>>::from_canonical(*port) |             PortKind::ReadOnly => { | ||||||
|  |                 MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||||
|                     .ty() |                     .ty() | ||||||
|                 .canonical(), |                     .fields() | ||||||
|             PortKind::WriteOnly => MemPort::<WriteStruct<DynCanonicalValue>>::from_canonical(*port) |             } | ||||||
|  |             PortKind::WriteOnly => { | ||||||
|  |                 MemPort::<WriteStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||||
|                     .ty() |                     .ty() | ||||||
|                 .canonical(), |                     .fields() | ||||||
|  |             } | ||||||
|             PortKind::ReadWrite => { |             PortKind::ReadWrite => { | ||||||
|                 MemPort::<ReadWriteStruct<DynCanonicalValue>>::from_canonical(*port) |                 MemPort::<ReadWriteStruct<CanonicalType, DynSize>>::from_canonical(*port) | ||||||
|                     .ty() |                     .ty() | ||||||
|                     .canonical() |                     .fields() | ||||||
|             } |  | ||||||
|             } |             } | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait PortStruct: | pub trait PortStruct: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> { | ||||||
|     BundleValue |     type Element: Type; | ||||||
|     + 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>; |  | ||||||
|     const PORT_KIND: PortKind; |     const PORT_KIND: PortKind; | ||||||
| 
 | 
 | ||||||
|     fn addr(this: Expr<Self>) -> Expr<DynUInt>; |     fn addr(this: Expr<Self>) -> Expr<UInt>; | ||||||
|     fn en(this: Expr<Self>) -> Expr<UInt<1>>; |     fn en(this: Expr<Self>) -> Expr<Bool>; | ||||||
|     fn clk(this: Expr<Self>) -> Expr<Clock>; |     fn clk(this: Expr<Self>) -> Expr<Clock>; | ||||||
|     fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; |     fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>; | ||||||
|     fn wdata(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 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 { | macro_rules! impl_port_struct { | ||||||
|  | @ -137,19 +133,11 @@ macro_rules! impl_port_struct { | ||||||
|             $($body:tt)* |             $($body:tt)* | ||||||
|         } |         } | ||||||
|     ) => { |     ) => { | ||||||
|         impl<$Element: Value> sealed::Sealed for $Struct<$Element> |         impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {} | ||||||
|         where |  | ||||||
|             $Element::Type: Type<Value = $Element>, |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         impl<$Element: Value> PortType for $Struct<$Element> |         impl<$Element: Type> PortType for $Struct<$Element, DynSize> { | ||||||
|         where |  | ||||||
|             $Element::Type: Type<Value = $Element>, |  | ||||||
|         { |  | ||||||
|             type PortKindTy = (); |             type PortKindTy = (); | ||||||
|             type PortType = <Self as ToExpr>::Type; |             type Port = Self; | ||||||
|             type PortValue = Self; |  | ||||||
| 
 | 
 | ||||||
|             fn port_kind(_port_kind: Self::PortKindTy) -> PortKind { |             fn port_kind(_port_kind: Self::PortKindTy) -> PortKind { | ||||||
|                 Self::PORT_KIND |                 Self::PORT_KIND | ||||||
|  | @ -164,18 +152,14 @@ macro_rules! impl_port_struct { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<$Element: Value> PortStruct for $Struct<$Element> |         impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> { | ||||||
|         where |  | ||||||
|             $Element::Type: Type<Value = $Element>, |  | ||||||
|         { |  | ||||||
|             type Element = $Element; |             type Element = $Element; | ||||||
|             type ElementType = $Element::Type; |  | ||||||
| 
 | 
 | ||||||
|             fn addr(this: Expr<Self>) -> Expr<DynUInt> { |             fn addr(this: Expr<Self>) -> Expr<UInt> { | ||||||
|                 this.addr |                 this.addr | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             fn en(this: Expr<Self>) -> Expr<UInt<1>> { |             fn en(this: Expr<Self>) -> Expr<Bool> { | ||||||
|                 this.en |                 this.en | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -190,14 +174,9 @@ macro_rules! impl_port_struct { | ||||||
| 
 | 
 | ||||||
| impl_port_struct! { | impl_port_struct! { | ||||||
|     impl<Element> _ for ReadStruct { |     impl<Element> _ for ReadStruct { | ||||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { |         fn port_ty(port: &MemPort<Self>) -> Self { | ||||||
|             type T<V> = <V as ToExpr>::Type; |             let element = Element::from_canonical(port.mem_element_type); | ||||||
|             T::<Self> { |             ReadStruct[element][port.addr_type.width()] | ||||||
|                 addr: port.addr_type, |  | ||||||
|                 en: UIntType::new(), |  | ||||||
|                 clk: ClockType, |  | ||||||
|                 data: Element::Type::from_dyn_canonical_type(port.mem_element_type), |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const PORT_KIND: PortKind = PortKind::ReadOnly; |         const PORT_KIND: PortKind = PortKind::ReadOnly; | ||||||
|  | @ -214,7 +193,7 @@ impl_port_struct! { | ||||||
|             None |             None | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { |         fn wmode(_this: Expr<Self>) -> Expr<Bool> { | ||||||
|             false.to_expr() |             false.to_expr() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -222,16 +201,9 @@ impl_port_struct! { | ||||||
| 
 | 
 | ||||||
| impl_port_struct! { | impl_port_struct! { | ||||||
|     impl<Element> _ for WriteStruct { |     impl<Element> _ for WriteStruct { | ||||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { |         fn port_ty(port: &MemPort<Self>) -> Self { | ||||||
|             type T<V> = <V as ToExpr>::Type; |             let element = Element::from_canonical(port.mem_element_type); | ||||||
|             let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); |             WriteStruct[element][port.addr_type.width()] | ||||||
|             T::<Self> { |  | ||||||
|                 addr: port.addr_type, |  | ||||||
|                 en: UIntType::new(), |  | ||||||
|                 clk: ClockType, |  | ||||||
|                 mask: element_ty.mask_type(), |  | ||||||
|                 data: element_ty, |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const PORT_KIND: PortKind = PortKind::WriteOnly; |         const PORT_KIND: PortKind = PortKind::WriteOnly; | ||||||
|  | @ -248,7 +220,7 @@ impl_port_struct! { | ||||||
|             Some(this.mask) |             Some(this.mask) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> { |         fn wmode(_this: Expr<Self>) -> Expr<Bool> { | ||||||
|             true.to_expr() |             true.to_expr() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -256,18 +228,9 @@ impl_port_struct! { | ||||||
| 
 | 
 | ||||||
| impl_port_struct! { | impl_port_struct! { | ||||||
|     impl<Element> _ for ReadWriteStruct { |     impl<Element> _ for ReadWriteStruct { | ||||||
|         fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type { |         fn port_ty(port: &MemPort<Self>) -> Self { | ||||||
|             type T<V> = <V as ToExpr>::Type; |             let element = Element::from_canonical(port.mem_element_type); | ||||||
|             let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type); |             ReadWriteStruct[element][port.addr_type.width()] | ||||||
|             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, |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const PORT_KIND: PortKind = PortKind::ReadWrite; |         const PORT_KIND: PortKind = PortKind::ReadWrite; | ||||||
|  | @ -284,7 +247,7 @@ impl_port_struct! { | ||||||
|             Some(this.wmask) |             Some(this.wmask) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fn wmode(this: Expr<Self>) -> Expr<UInt<1>> { |         fn wmode(this: Expr<Self>) -> Expr<Bool> { | ||||||
|             this.wmode |             this.wmode | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -382,8 +345,8 @@ pub struct MemPort<T: PortType> { | ||||||
|     source_location: SourceLocation, |     source_location: SourceLocation, | ||||||
|     port_kind: T::PortKindTy, |     port_kind: T::PortKindTy, | ||||||
|     port_index: usize, |     port_index: usize, | ||||||
|     addr_type: DynUIntType, |     addr_type: UInt, | ||||||
|     mem_element_type: Interned<dyn DynCanonicalType>, |     mem_element_type: CanonicalType, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: PortType> fmt::Debug for MemPort<T> { | 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> { | impl<T: PortType> MemPort<T> { | ||||||
|     type Type = T::PortType; |     pub fn ty(&self) -> T::Port { | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         T::port_ty(self) |         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 { |     pub fn source_location(&self) -> SourceLocation { | ||||||
|         self.source_location |         self.source_location | ||||||
|     } |     } | ||||||
|  | @ -436,10 +390,10 @@ impl<T: PortType> MemPort<T> { | ||||||
|             index: self.port_index, |             index: self.port_index, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn mem_element_type(&self) -> Interned<dyn DynCanonicalType> { |     pub fn mem_element_type(&self) -> CanonicalType { | ||||||
|         self.mem_element_type |         self.mem_element_type | ||||||
|     } |     } | ||||||
|     pub fn addr_type(&self) -> DynUIntType { |     pub fn addr_type(&self) -> UInt { | ||||||
|         self.addr_type |         self.addr_type | ||||||
|     } |     } | ||||||
|     pub fn canonical(&self) -> MemPort<DynPortType> { |     pub fn canonical(&self) -> MemPort<DynPortType> { | ||||||
|  | @ -463,7 +417,6 @@ impl<T: PortType> MemPort<T> { | ||||||
|     pub fn from_canonical(port: MemPort<DynPortType>) -> Self |     pub fn from_canonical(port: MemPort<DynPortType>) -> Self | ||||||
|     where |     where | ||||||
|         T: PortStruct, |         T: PortStruct, | ||||||
|         T::Type: BundleType<Value = T>, |  | ||||||
|     { |     { | ||||||
|         let MemPort { |         let MemPort { | ||||||
|             mem_name, |             mem_name, | ||||||
|  | @ -488,8 +441,8 @@ impl<T: PortType> MemPort<T> { | ||||||
|         mem_name: ScopedNameId, |         mem_name: ScopedNameId, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         port_name: PortName, |         port_name: PortName, | ||||||
|         addr_type: DynUIntType, |         addr_type: UInt, | ||||||
|         mem_element_type: Interned<dyn DynCanonicalType>, |         mem_element_type: CanonicalType, | ||||||
|     ) -> Self { |     ) -> Self { | ||||||
|         assert!( |         assert!( | ||||||
|             mem_element_type.is_storable(), |             mem_element_type.is_storable(), | ||||||
|  | @ -520,10 +473,10 @@ pub enum ReadUnderWrite { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| struct MemImpl<T: ArrayTypeTrait, P> { | struct MemImpl<Element: Type, Len: Size, P> { | ||||||
|     scoped_name: ScopedNameId, |     scoped_name: ScopedNameId, | ||||||
|     source_location: SourceLocation, |     source_location: SourceLocation, | ||||||
|     array_type: T, |     array_type: ArrayType<Element, Len>, | ||||||
|     initial_value: Option<Interned<BitSlice>>, |     initial_value: Option<Interned<BitSlice>>, | ||||||
|     ports: P, |     ports: P, | ||||||
|     read_latency: usize, |     read_latency: usize, | ||||||
|  | @ -533,11 +486,11 @@ struct MemImpl<T: ArrayTypeTrait, P> { | ||||||
|     mem_annotations: Interned<[Annotation]>, |     mem_annotations: Interned<[Annotation]>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct Mem<VA: ValueArrayOrSlice + ?Sized>( | pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>( | ||||||
|     Interned<MemImpl<ArrayType<VA>, Interned<[Interned<MemPort<DynPortType>>]>>>, |     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<'_> { | impl fmt::Debug for PortsDebug<'_> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     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 { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         let MemImpl { |         let MemImpl { | ||||||
|             scoped_name, |             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) { |     fn hash<H: Hasher>(&self, state: &mut H) { | ||||||
|         self.0.hash(state); |         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 { |     fn eq(&self, other: &Self) -> bool { | ||||||
|         self.0 == other.0 |         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 { |     fn clone(&self) -> Self { | ||||||
|         *self |         *self | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | impl<Element: Type, Len: Size> Mem<Element, Len> { | ||||||
|     #[allow(clippy::too_many_arguments)] |     #[allow(clippy::too_many_arguments)] | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new_unchecked( |     pub fn new_unchecked( | ||||||
|         scoped_name: ScopedNameId, |         scoped_name: ScopedNameId, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         array_type: ArrayType<VA>, |         array_type: ArrayType<Element, Len>, | ||||||
|         initial_value: Option<Interned<BitSlice>>, |         initial_value: Option<Interned<BitSlice>>, | ||||||
|         ports: Interned<[Interned<MemPort<DynPortType>>]>, |         ports: Interned<[MemPort<DynPortType>]>, | ||||||
|         read_latency: usize, |         read_latency: usize, | ||||||
|         write_latency: NonZeroU32, |         write_latency: NonZeroU32, | ||||||
|         read_under_write: ReadUnderWrite, |         read_under_write: ReadUnderWrite, | ||||||
|  | @ -617,14 +570,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||||
|         mem_annotations: Interned<[Annotation]>, |         mem_annotations: Interned<[Annotation]>, | ||||||
|     ) -> Self { |     ) -> Self { | ||||||
|         if let Some(initial_value) = initial_value { |         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(), |                 array_type.element(), | ||||||
|                 Some(array_type.len()), |                 Some(array_type.len()), | ||||||
|                 initial_value, |                 initial_value, | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|         let addr_width = memory_addr_width(array_type.len()); |         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!( |         assert!( | ||||||
|             expected_mem_element_type.is_storable(), |             expected_mem_element_type.is_storable(), | ||||||
|             "memory element type must be a storable type" |             "memory element type must be a storable type" | ||||||
|  | @ -637,7 +590,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||||
|                 port_index, |                 port_index, | ||||||
|                 addr_type, |                 addr_type, | ||||||
|                 mem_element_type, |                 mem_element_type, | ||||||
|             } = **port; |             } = *port; | ||||||
|             assert_eq!(mem_name, scoped_name, "memory name must match with ports"); |             assert_eq!(mem_name, scoped_name, "memory name must match with ports"); | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 port_index, index, |                 port_index, index, | ||||||
|  | @ -659,7 +612,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||||
|             }; |             }; | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 Some(port), |                 Some(port), | ||||||
|                 ports.get(port.port_index).map(|v| &**v), |                 ports.get(port.port_index), | ||||||
|                 "port on memory must match annotation's target base" |                 "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 { |     pub fn source_location(self) -> SourceLocation { | ||||||
|         self.0.source_location |         self.0.source_location | ||||||
|     } |     } | ||||||
|     pub fn array_type(self) -> ArrayType<VA> { |     pub fn array_type(self) -> ArrayType<Element, Len> { | ||||||
|         self.0.array_type.clone() |         self.0.array_type.clone() | ||||||
|     } |     } | ||||||
|     pub fn initial_value(self) -> Option<Interned<BitSlice>> { |     pub fn initial_value(self) -> Option<Interned<BitSlice>> { | ||||||
|         self.0.initial_value |         self.0.initial_value | ||||||
|     } |     } | ||||||
|     pub fn ports(self) -> Interned<[Interned<MemPort<DynPortType>>]> { |     pub fn ports(self) -> Interned<[MemPort<DynPortType>]> { | ||||||
|         self.0.ports |         self.0.ports | ||||||
|     } |     } | ||||||
|     pub fn read_latency(self) -> usize { |     pub fn read_latency(self) -> usize { | ||||||
|  | @ -706,7 +659,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||||
|     pub fn mem_annotations(self) -> Interned<[Annotation]> { |     pub fn mem_annotations(self) -> Interned<[Annotation]> { | ||||||
|         self.0.mem_annotations |         self.0.mem_annotations | ||||||
|     } |     } | ||||||
|     pub fn canonical(self) -> Mem<[DynCanonicalValue]> { |     pub fn canonical(self) -> Mem { | ||||||
|         let MemImpl { |         let MemImpl { | ||||||
|             scoped_name, |             scoped_name, | ||||||
|             source_location, |             source_location, | ||||||
|  | @ -719,7 +672,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> { | ||||||
|             port_annotations, |             port_annotations, | ||||||
|             mem_annotations, |             mem_annotations, | ||||||
|         } = *self.0; |         } = *self.0; | ||||||
|         let array_type = array_type.canonical(); |         let array_type = array_type.as_dyn_array(); | ||||||
|         Mem(Intern::intern_sized(MemImpl { |         Mem(Intern::intern_sized(MemImpl { | ||||||
|             scoped_name, |             scoped_name, | ||||||
|             source_location, |             source_location, | ||||||
|  | @ -751,10 +704,10 @@ impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> { | ||||||
| pub(crate) struct MemBuilderTarget { | pub(crate) struct MemBuilderTarget { | ||||||
|     pub(crate) scoped_name: ScopedNameId, |     pub(crate) scoped_name: ScopedNameId, | ||||||
|     pub(crate) source_location: SourceLocation, |     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) depth: Option<usize>, | ||||||
|     pub(crate) initial_value: Option<Interned<BitSlice>>, |     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) read_latency: usize, | ||||||
|     pub(crate) write_latency: NonZeroU32, |     pub(crate) write_latency: NonZeroU32, | ||||||
|     pub(crate) read_under_write: ReadUnderWrite, |     pub(crate) read_under_write: ReadUnderWrite, | ||||||
|  | @ -763,11 +716,11 @@ pub(crate) struct MemBuilderTarget { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MemBuilderTarget { | impl MemBuilderTarget { | ||||||
|     pub(crate) fn make_memory(&self) -> Option<Mem<[DynCanonicalValue]>> { |     pub(crate) fn make_memory(&self) -> Option<Mem> { | ||||||
|         Some(Mem::new_unchecked( |         Some(Mem::new_unchecked( | ||||||
|             self.scoped_name, |             self.scoped_name, | ||||||
|             self.source_location, |             self.source_location, | ||||||
|             ArrayType::new_slice(self.mem_element_type, self.depth?), |             ArrayType::new_dyn(self.mem_element_type, self.depth?), | ||||||
|             self.initial_value, |             self.initial_value, | ||||||
|             Intern::intern(&self.ports), |             Intern::intern(&self.ports), | ||||||
|             self.read_latency, |             self.read_latency, | ||||||
|  | @ -817,16 +770,18 @@ impl fmt::Debug for MemBuilderTarget { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct MemBuilder<VA: ValueArrayOrSlice + ?Sized> { | pub struct MemBuilder<Element: Type, Len: Size> { | ||||||
|     mem_element_type: VA::ElementType, |     mem_element_type: Element, | ||||||
|     target: Rc<RefCell<MemBuilderTarget>>, |     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 { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         let Self { |         let Self { | ||||||
|             mem_element_type, |             mem_element_type, | ||||||
|             target, |             target, | ||||||
|  |             _phantom: _, | ||||||
|         } = &self; |         } = &self; | ||||||
|         target.borrow().debug_fmt("MemBuilder", mem_element_type, f) |         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 |         .map_or(usize::BITS, usize::ilog2) as usize | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | impl<Element: Type, Len: Size> MemBuilder<Element, Len> { | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn check_initial_value_bit_slice( |     fn check_initial_value_bit_slice( | ||||||
|         mem_element_type: &VA::ElementType, |         mem_element_type: Element, | ||||||
|         depth: Option<usize>, |         depth: Option<usize>, | ||||||
|         initial_value: Interned<BitSlice>, |         initial_value: Interned<BitSlice>, | ||||||
|     ) -> Interned<BitSlice> { |     ) -> Interned<BitSlice> { | ||||||
|  |         let element_bit_width = mem_element_type.canonical().bit_width(); | ||||||
|         if let Some(depth) = depth { |         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", |                 "memory must be small enough that its initializer bit length fits in usize", | ||||||
|             ); |             ); | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|  | @ -858,7 +814,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|         assert!( |         assert!( | ||||||
|             initial_value |             initial_value | ||||||
|                 .len() |                 .len() | ||||||
|                 .checked_rem(mem_element_type.bit_width()) |                 .checked_rem(element_bit_width) | ||||||
|                 .unwrap_or(initial_value.len()) |                 .unwrap_or(initial_value.len()) | ||||||
|                 == 0, |                 == 0, | ||||||
|             "Mem's initializer bit length must be a multiple of the element type's bit width", |             "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] |     #[track_caller] | ||||||
|     fn check_initial_value_expr( |     fn check_initial_value_expr( | ||||||
|         mem_element_type: &VA::ElementType, |         mem_element_type: &Element, | ||||||
|         depth: Option<usize>, |         depth: Option<usize>, | ||||||
|         initial_value: Expr<Array<[DynCanonicalValue]>>, |         initial_value: Expr<Array>, | ||||||
|     ) -> Interned<BitSlice> { |     ) -> Interned<BitSlice> { | ||||||
|         let initial_value_ty = initial_value.canonical_type(); |         let initial_value_ty = Expr::ty(initial_value); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             *mem_element_type, |             *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", |             "Mem's element type must match initializer's element type", | ||||||
|         ); |         ); | ||||||
|         if let Some(depth) = depth { |         if let Some(depth) = depth { | ||||||
|  | @ -889,7 +845,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|         }; |         }; | ||||||
|         debug_assert_eq!( |         debug_assert_eq!( | ||||||
|             retval.len(), |             retval.len(), | ||||||
|             initial_value_ty.bit_width(), |             initial_value_ty.type_properties().bit_width, | ||||||
|             "initial value produced wrong literal bits length" |             "initial value produced wrong literal bits length" | ||||||
|         ); |         ); | ||||||
|         retval |         retval | ||||||
|  | @ -898,9 +854,9 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|     pub(crate) fn new( |     pub(crate) fn new( | ||||||
|         scoped_name: ScopedNameId, |         scoped_name: ScopedNameId, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         mem_element_type: VA::ElementType, |         mem_element_type: Element, | ||||||
|     ) -> (Self, Rc<RefCell<MemBuilderTarget>>) { |     ) -> (Self, Rc<RefCell<MemBuilderTarget>>) { | ||||||
|         let canonical_mem_element_type = mem_element_type.canonical_dyn(); |         let canonical_mem_element_type = mem_element_type.canonical(); | ||||||
|         assert!( |         assert!( | ||||||
|             canonical_mem_element_type.is_storable(), |             canonical_mem_element_type.is_storable(), | ||||||
|             "memory element type must be a storable type" |             "memory element type must be a storable type" | ||||||
|  | @ -909,7 +865,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|             scoped_name, |             scoped_name, | ||||||
|             source_location, |             source_location, | ||||||
|             mem_element_type: canonical_mem_element_type, |             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, |             initial_value: None, | ||||||
|             ports: vec![], |             ports: vec![], | ||||||
|             read_latency: 0, |             read_latency: 0, | ||||||
|  | @ -922,6 +878,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|             Self { |             Self { | ||||||
|                 mem_element_type, |                 mem_element_type, | ||||||
|                 target: Rc::clone(&target), |                 target: Rc::clone(&target), | ||||||
|  |                 _phantom: PhantomData, | ||||||
|             }, |             }, | ||||||
|             target, |             target, | ||||||
|         ) |         ) | ||||||
|  | @ -931,19 +888,19 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|         &mut self, |         &mut self, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         port_kind: PortKind, |         port_kind: PortKind, | ||||||
|     ) -> Interned<MemPort<DynPortType>> { |     ) -> MemPort<DynPortType> { | ||||||
|         let mut target = self.target.borrow_mut(); |         let mut target = self.target.borrow_mut(); | ||||||
|         let Some(depth) = target.depth else { |         let Some(depth) = target.depth else { | ||||||
|             panic!("MemBuilder::depth must be called before adding ports"); |             panic!("MemBuilder::depth must be called before adding ports"); | ||||||
|         }; |         }; | ||||||
|         let port = Intern::intern_sized(MemPort { |         let port = MemPort { | ||||||
|             mem_name: target.scoped_name, |             mem_name: target.scoped_name, | ||||||
|             source_location, |             source_location, | ||||||
|             port_kind, |             port_kind, | ||||||
|             port_index: target.ports.len(), |             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, |             mem_element_type: target.mem_element_type, | ||||||
|         }); |         }; | ||||||
|         target.ports.push(port); |         target.ports.push(port); | ||||||
|         port |         port | ||||||
|     } |     } | ||||||
|  | @ -952,50 +909,53 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|         &mut self, |         &mut self, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         kind: PortKind, |         kind: PortKind, | ||||||
|     ) -> Expr<DynBundle> { |     ) -> Expr<Bundle> { | ||||||
|         Expr::new_unchecked(ExprEnum::MemPort(self.new_port_impl(source_location, kind))) |         self.new_port_impl(source_location, kind).to_expr() | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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) |         self.new_port_with_loc(SourceLocation::caller(), kind) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new_read_port_with_loc( |     pub fn new_read_port_with_loc( | ||||||
|         &mut self, |         &mut self, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Expr<ReadStruct<VA::Element>> { |     ) -> Expr<ReadStruct<Element, DynSize>> { | ||||||
|         Expr::new_unchecked(ExprEnum::MemPort( |         Expr::from_bundle( | ||||||
|             self.new_port_impl(source_location, PortKind::ReadOnly), |             self.new_port_impl(source_location, PortKind::ReadOnly) | ||||||
|         )) |                 .to_expr(), | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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()) |         self.new_read_port_with_loc(SourceLocation::caller()) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new_write_port_with_loc( |     pub fn new_write_port_with_loc( | ||||||
|         &mut self, |         &mut self, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Expr<WriteStruct<VA::Element>> { |     ) -> Expr<WriteStruct<Element, DynSize>> { | ||||||
|         Expr::new_unchecked(ExprEnum::MemPort( |         Expr::from_bundle( | ||||||
|             self.new_port_impl(source_location, PortKind::WriteOnly), |             self.new_port_impl(source_location, PortKind::WriteOnly) | ||||||
|         )) |                 .to_expr(), | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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()) |         self.new_write_port_with_loc(SourceLocation::caller()) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new_rw_port_with_loc( |     pub fn new_rw_port_with_loc( | ||||||
|         &mut self, |         &mut self, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Expr<ReadWriteStruct<VA::Element>> { |     ) -> Expr<ReadWriteStruct<Element, DynSize>> { | ||||||
|         Expr::new_unchecked(ExprEnum::MemPort( |         Expr::from_bundle( | ||||||
|             self.new_port_impl(source_location, PortKind::ReadWrite), |             self.new_port_impl(source_location, PortKind::ReadWrite) | ||||||
|         )) |                 .to_expr(), | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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()) |         self.new_rw_port_with_loc(SourceLocation::caller()) | ||||||
|     } |     } | ||||||
|     pub fn scoped_name(&self) -> ScopedNameId { |     pub fn scoped_name(&self) -> ScopedNameId { | ||||||
|  | @ -1004,7 +964,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|     pub fn source_location(&self) -> SourceLocation { |     pub fn source_location(&self) -> SourceLocation { | ||||||
|         self.target.borrow().source_location |         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 |         &self.mem_element_type | ||||||
|     } |     } | ||||||
|     #[allow(clippy::result_unit_err)] |     #[allow(clippy::result_unit_err)] | ||||||
|  | @ -1027,28 +987,28 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> { | ||||||
|         target.depth = Some(depth); |         target.depth = Some(depth); | ||||||
|     } |     } | ||||||
|     #[allow(clippy::result_unit_err)] |     #[allow(clippy::result_unit_err)] | ||||||
|     pub fn get_array_type(&self) -> Result<ArrayType<VA>, ()> { |     pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> { | ||||||
|         Ok(ArrayType::new_with_len( |         Ok(ArrayType::new( | ||||||
|             self.mem_element_type.clone(), |             self.mem_element_type.clone(), | ||||||
|             self.get_depth()?, |             Len::from_usize(self.get_depth()?), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
|     pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> { |     pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> { | ||||||
|         self.target.borrow().initial_value |         self.target.borrow().initial_value | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[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(); |         let mut target = self.target.borrow_mut(); | ||||||
|         if target.initial_value.is_some() { |         if target.initial_value.is_some() { | ||||||
|             panic!("can't set Mem's initial value more than once"); |             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( |         target.initial_value = Some(Self::check_initial_value_expr( | ||||||
|             &self.mem_element_type, |             &self.mem_element_type, | ||||||
|             target.depth, |             target.depth, | ||||||
|             initial_value, |             initial_value, | ||||||
|         )); |         )); | ||||||
|         target.depth = Some(initial_value.ty().len()); |         target.depth = Some(Expr::ty(initial_value).len()); | ||||||
|     } |     } | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) { |     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"); |             panic!("can't set Mem's initial value more than once"); | ||||||
|         } |         } | ||||||
|         target.initial_value = Some(Self::check_initial_value_bit_slice( |         target.initial_value = Some(Self::check_initial_value_bit_slice( | ||||||
|             &self.mem_element_type, |             self.mem_element_type, | ||||||
|             target.depth, |             target.depth, | ||||||
|             initial_value, |             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 { |         if element_bit_width != 0 { | ||||||
|             target.depth = Some(initial_value.len() / element_bit_width); |             target.depth = Some(initial_value.len() / element_bit_width); | ||||||
|         } |         } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,5 +1,15 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
|  | // TODO:
 | ||||||
| pub mod simplify_enums; | pub mod simplify_enums; | ||||||
| pub mod simplify_memories; | //pub mod simplify_memories;
 | ||||||
| pub mod visit; | pub mod visit; | ||||||
|  | 
 | ||||||
|  | pub mod simplify_memories { | ||||||
|  |     use crate::{bundle::Bundle, intern::Interned, module::Module}; | ||||||
|  | 
 | ||||||
|  |     pub fn simplify_memories(module: Interned<Module<Bundle>>) -> Interned<Module<Bundle>> { | ||||||
|  |         // placeholder
 | ||||||
|  |         module | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,10 +2,14 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     array::{Array, ArrayType}, |     array::{Array, ArrayType}, | ||||||
|     bundle::{BundleType, BundleValue, DynBundle, DynBundleType}, |     bundle::{Bundle, BundleType}, | ||||||
|     enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType}, |     enum_::{Enum, EnumType, EnumVariant}, | ||||||
|     expr::{ops, Expr, ExprEnum, ToExpr}, |     expr::{ | ||||||
|     int::{DynUInt, DynUIntType, IntCmp}, |         ops::{self, EnumLiteral}, | ||||||
|  |         CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, | ||||||
|  |     }, | ||||||
|  |     hdl, | ||||||
|  |     int::{DynSize, IntCmp, Size, UInt, UIntType}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     memory::{DynPortType, Mem, MemPort}, |     memory::{DynPortType, Mem, MemPort}, | ||||||
|     module::{ |     module::{ | ||||||
|  | @ -13,7 +17,7 @@ use crate::{ | ||||||
|         Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, |         Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, | ||||||
|     }, |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum}, |     ty::{CanonicalType, Type}, | ||||||
|     wire::Wire, |     wire::Wire, | ||||||
| }; | }; | ||||||
| use core::fmt; | use core::fmt; | ||||||
|  | @ -21,7 +25,7 @@ use hashbrown::HashMap; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub enum SimplifyEnumsError { | pub enum SimplifyEnumsError { | ||||||
|     EnumIsNotCastableFromBits { enum_type: DynEnumType }, |     EnumIsNotCastableFromBits { enum_type: Enum }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Display for SimplifyEnumsError { | impl fmt::Display for SimplifyEnumsError { | ||||||
|  | @ -37,26 +41,23 @@ impl fmt::Display for SimplifyEnumsError { | ||||||
| 
 | 
 | ||||||
| impl std::error::Error for SimplifyEnumsError {} | impl std::error::Error for SimplifyEnumsError {} | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Eq, PartialEq, Hash, Debug)] | #[hdl] | ||||||
| struct TagAndBody<T> { | struct TagAndBody<T, BodyWidth: Size> { | ||||||
|     tag: T, |     tag: T, | ||||||
|     body: DynUInt, |     body: UIntType<BodyWidth>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type TagAndBodyType<T> = <TagAndBody<T> as ToExpr>::Type; |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| enum EnumTypeState { | enum EnumTypeState { | ||||||
|     TagEnumAndBody(TagAndBodyType<DynEnum>), |     TagEnumAndBody(TagAndBody<Enum, DynSize>), | ||||||
|     TagUIntAndBody(TagAndBodyType<DynUInt>), |     TagUIntAndBody(TagAndBody<UInt, DynSize>), | ||||||
|     UInt(DynUIntType), |     UInt(UInt), | ||||||
|     Unchanged, |     Unchanged, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct State { | struct State { | ||||||
|     enum_types: HashMap<DynEnumType, EnumTypeState>, |     enum_types: HashMap<Enum, EnumTypeState>, | ||||||
|     replacement_mem_ports: |     replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>, | ||||||
|         HashMap<Interned<MemPort<DynPortType>>, Interned<Wire<Interned<dyn DynCanonicalType>>>>, |  | ||||||
|     kind: SimplifyEnumsKind, |     kind: SimplifyEnumsKind, | ||||||
|     name_id_gen: NameIdGen, |     name_id_gen: NameIdGen, | ||||||
| } | } | ||||||
|  | @ -64,12 +65,12 @@ struct State { | ||||||
| impl State { | impl State { | ||||||
|     fn get_or_make_enum_type_state( |     fn get_or_make_enum_type_state( | ||||||
|         &mut self, |         &mut self, | ||||||
|         enum_type: DynEnumType, |         enum_type: Enum, | ||||||
|     ) -> Result<EnumTypeState, SimplifyEnumsError> { |     ) -> Result<EnumTypeState, SimplifyEnumsError> { | ||||||
|         if let Some(retval) = self.enum_types.get(&enum_type) { |         if let Some(retval) = self.enum_types.get(&enum_type) { | ||||||
|             return Ok(retval.clone()); |             return Ok(retval.clone()); | ||||||
|         } |         } | ||||||
|         if !enum_type.is_castable_from_bits() { |         if !enum_type.type_properties().is_castable_from_bits { | ||||||
|             return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type }); |             return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type }); | ||||||
|         } |         } | ||||||
|         let has_body = enum_type |         let has_body = enum_type | ||||||
|  | @ -78,29 +79,29 @@ impl State { | ||||||
|             .any(|variant| variant.ty.is_some()); |             .any(|variant| variant.ty.is_some()); | ||||||
|         let retval = match (self.kind, has_body) { |         let retval = match (self.kind, has_body) { | ||||||
|             (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => { |             (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => { | ||||||
|                 EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { |                 EnumTypeState::TagEnumAndBody(TagAndBody { | ||||||
|                     tag: DynEnumType::new(Interned::from_iter(enum_type.variants().iter().map( |                     tag: Enum::new(Interned::from_iter(enum_type.variants().iter().map(|v| { | ||||||
|                         |v| VariantType { |                         EnumVariant { | ||||||
|                             name: v.name, |                             name: v.name, | ||||||
|                             ty: None, |                             ty: None, | ||||||
|                         }, |                         } | ||||||
|                     ))), |                     }))), | ||||||
|                     body: DynUIntType::new( |                     body: UInt::new_dyn( | ||||||
|                         enum_type.bit_width() - enum_type.discriminant_bit_width(), |                         enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(), | ||||||
|                     ), |                     ), | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
|             (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged, |             (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged, | ||||||
|             (SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => { |             (SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => { | ||||||
|                 EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { |                 EnumTypeState::TagUIntAndBody(TagAndBody { | ||||||
|                     tag: DynUIntType::new(enum_type.discriminant_bit_width()), |                     tag: UInt::new_dyn(enum_type.discriminant_bit_width()), | ||||||
|                     body: DynUIntType::new( |                     body: UInt::new_dyn( | ||||||
|                         enum_type.bit_width() - enum_type.discriminant_bit_width(), |                         enum_type.type_properties().bit_width - enum_type.discriminant_bit_width(), | ||||||
|                     ), |                     ), | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
|             (SimplifyEnumsKind::ReplaceWithUInt, _) => { |             (SimplifyEnumsKind::ReplaceWithUInt, _) => { | ||||||
|                 EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width())) |                 EnumTypeState::UInt(UInt::new_dyn(enum_type.type_properties().bit_width)) | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         self.enum_types.insert(enum_type, retval.clone()); |         self.enum_types.insert(enum_type, retval.clone()); | ||||||
|  | @ -108,21 +109,14 @@ impl State { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt { |  | ||||||
|     let Some(value) = value else { |  | ||||||
|         return DynUInt::with_type(target_ty, 0u8); |  | ||||||
|     }; |  | ||||||
|     DynUInt::from_bit_slice(&value.to_bits()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn connect_port( | fn connect_port( | ||||||
|     stmts: &mut Vec<Stmt>, |     stmts: &mut Vec<Stmt>, | ||||||
|     lhs: Expr<DynCanonicalValue>, |     lhs: Expr<CanonicalType>, | ||||||
|     rhs: Expr<DynCanonicalValue>, |     rhs: Expr<CanonicalType>, | ||||||
|     source_location: SourceLocation, |     source_location: SourceLocation, | ||||||
| ) { | ) { | ||||||
|     println!("connect_port: lhs={lhs:?} rhs={rhs:?}"); |     println!("connect_port: lhs={lhs:?} rhs={rhs:?}"); | ||||||
|     if lhs.canonical_type() == rhs.canonical_type() { |     if Expr::ty(lhs) == Expr::ty(rhs) { | ||||||
|         stmts.push( |         stmts.push( | ||||||
|             dbg!(StmtConnect { |             dbg!(StmtConnect { | ||||||
|                 lhs, |                 lhs, | ||||||
|  | @ -133,55 +127,53 @@ fn connect_port( | ||||||
|         ); |         ); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     match ( |     match (Expr::ty(lhs), Expr::ty(rhs)) { | ||||||
|         lhs.canonical_type().type_enum(), |         (CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_)) => { | ||||||
|         rhs.canonical_type().type_enum(), |             let lhs = Expr::<Bundle>::from_canonical(lhs); | ||||||
|     ) { |  | ||||||
|         (TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => { |  | ||||||
|             let lhs = lhs.with_type::<DynBundle>(); |  | ||||||
|             for field in lhs_type.fields() { |             for field in lhs_type.fields() { | ||||||
|                 assert!(!field.flipped); |                 assert!(!field.flipped); | ||||||
|                 connect_port(stmts, lhs.field(&field.name), rhs, source_location); |                 connect_port(stmts, Expr::field(lhs, &field.name), rhs, source_location); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         (TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => { |         (CanonicalType::UInt(_), CanonicalType::Bundle(rhs_type)) => { | ||||||
|             let rhs = rhs.with_type::<DynBundle>(); |             let rhs = Expr::<Bundle>::from_canonical(rhs); | ||||||
|             for field in rhs_type.fields() { |             for field in rhs_type.fields() { | ||||||
|                 assert!(!field.flipped); |                 assert!(!field.flipped); | ||||||
|                 connect_port(stmts, lhs, rhs.field(&field.name), source_location); |                 connect_port(stmts, lhs, Expr::field(rhs, &field.name), source_location); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         (TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => { |         (CanonicalType::Bundle(lhs_type), CanonicalType::Bundle(_)) => { | ||||||
|             let lhs = lhs.with_type::<DynBundle>(); |             let lhs = Expr::<Bundle>::from_canonical(lhs); | ||||||
|             let rhs = rhs.with_type::<DynBundle>(); |             let rhs = Expr::<Bundle>::from_canonical(rhs); | ||||||
|             for field in lhs_type.fields() { |             for field in lhs_type.fields() { | ||||||
|                 let (lhs_field, rhs_field) = if field.flipped { |                 let (lhs_field, rhs_field) = if field.flipped { | ||||||
|                     (rhs.field(&field.name), lhs.field(&field.name)) |                     (Expr::field(rhs, &field.name), Expr::field(lhs, &field.name)) | ||||||
|                 } else { |                 } else { | ||||||
|                     (lhs.field(&field.name), rhs.field(&field.name)) |                     (Expr::field(lhs, &field.name), Expr::field(rhs, &field.name)) | ||||||
|                 }; |                 }; | ||||||
|                 connect_port(stmts, lhs_field, rhs_field, source_location); |                 connect_port(stmts, lhs_field, rhs_field, source_location); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         (TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => { |         (CanonicalType::Array(lhs_type), CanonicalType::Array(_)) => { | ||||||
|             let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>(); |             let lhs = Expr::<Array>::from_canonical(lhs); | ||||||
|             let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>(); |             let rhs = Expr::<Array>::from_canonical(rhs); | ||||||
|             for index in 0..lhs_type.len() { |             for index in 0..lhs_type.len() { | ||||||
|                 connect_port(stmts, lhs[index], rhs[index], source_location); |                 connect_port(stmts, lhs[index], rhs[index], source_location); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         (TypeEnum::BundleType(_), _) |         (CanonicalType::Bundle(_), _) | ||||||
|         | (TypeEnum::EnumType(_), _) |         | (CanonicalType::Enum(_), _) | ||||||
|         | (TypeEnum::ArrayType(_), _) |         | (CanonicalType::Array(_), _) | ||||||
|         | (TypeEnum::UInt(_), _) |         | (CanonicalType::UInt(_), _) | ||||||
|         | (TypeEnum::SInt(_), _) |         | (CanonicalType::SInt(_), _) | ||||||
|         | (TypeEnum::Clock(_), _) |         | (CanonicalType::Bool(_), _) | ||||||
|         | (TypeEnum::AsyncReset(_), _) |         | (CanonicalType::Clock(_), _) | ||||||
|         | (TypeEnum::SyncReset(_), _) |         | (CanonicalType::AsyncReset(_), _) | ||||||
|         | (TypeEnum::Reset(_), _) => unreachable!( |         | (CanonicalType::SyncReset(_), _) | ||||||
|  |         | (CanonicalType::Reset(_), _) => unreachable!( | ||||||
|             "trying to connect memory ports:\n{:?}\n{:?}", |             "trying to connect memory ports:\n{:?}\n{:?}", | ||||||
|             lhs.canonical_type().type_enum(), |             Expr::ty(lhs), | ||||||
|             rhs.canonical_type().type_enum(), |             Expr::ty(rhs), | ||||||
|         ), |         ), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -189,18 +181,11 @@ fn connect_port( | ||||||
| impl Folder for State { | impl Folder for State { | ||||||
|     type Error = SimplifyEnumsError; |     type Error = SimplifyEnumsError; | ||||||
| 
 | 
 | ||||||
|     fn fold_dyn_enum(&mut self, _v: DynEnum) -> Result<DynEnum, Self::Error> { |     fn fold_enum(&mut self, _v: Enum) -> Result<Enum, Self::Error> { | ||||||
|         unreachable!() |         unreachable!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_dyn_enum_type(&mut self, _v: DynEnumType) -> Result<DynEnumType, Self::Error> { |     fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> { | ||||||
|         unreachable!() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> |  | ||||||
|     where |  | ||||||
|         T::Type: BundleType<Value = T>, |  | ||||||
|     { |  | ||||||
|         let old_name_id_gen = |         let old_name_id_gen = | ||||||
|             std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical())); |             std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical())); | ||||||
|         let retval = Fold::default_fold(v, self); |         let retval = Fold::default_fold(v, self); | ||||||
|  | @ -211,98 +196,86 @@ impl Folder for State { | ||||||
|     fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> { |     fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> { | ||||||
|         match op { |         match op { | ||||||
|             ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? { |             ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? { | ||||||
|                 EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { tag, body }) => { |                 EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => *Expr::expr_enum( | ||||||
|                     TagAndBodyType::<DynEnum>::builder() |                     <TagAndBody<Enum, DynSize> as BundleType>::Builder::default() | ||||||
|                         .field_tag(DynEnum::new_by_index(tag, op.variant_index(), None)) |                         .field_tag(EnumLiteral::new_by_index(tag, op.variant_index(), None)) | ||||||
|                         .field_body(match op.variant_value() { |                         .field_body(match op.variant_value() { | ||||||
|                             Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), |                             Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), | ||||||
|                             None => DynUInt::with_type(body, 0u8).to_expr(), |                             None => body.zero().to_expr(), | ||||||
|                         }) |                         }) | ||||||
|                         .build() |                         .to_expr(), | ||||||
|                         .expr_enum() |  | ||||||
|                 } |  | ||||||
|                 EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { tag, body }) => { |  | ||||||
|                     TagAndBodyType::<DynUInt>::builder() |  | ||||||
|                         .field_tag(DynUInt::with_type(tag, op.variant_index())) |  | ||||||
|                         .field_body(match op.variant_value() { |  | ||||||
|                             Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), |  | ||||||
|                             None => DynUInt::with_type(body, 0u8).to_expr(), |  | ||||||
|                         }) |  | ||||||
|                         .build() |  | ||||||
|                         .expr_enum() |  | ||||||
|                 } |  | ||||||
|                 EnumTypeState::UInt(_) => TagAndBodyType::<DynUInt>::builder() |  | ||||||
|                     .field_tag(DynUInt::with_type( |  | ||||||
|                         DynUIntType::new(op.ty().discriminant_bit_width()), |  | ||||||
|                         op.variant_index(), |  | ||||||
|                     )) |  | ||||||
|                     .field_body(match op.variant_value() { |  | ||||||
|                         Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), |  | ||||||
|                         None => DynUInt::with_type( |  | ||||||
|                             DynUIntType::new( |  | ||||||
|                                 op.ty().bit_width() - op.ty().discriminant_bit_width(), |  | ||||||
|                 ), |                 ), | ||||||
|                             0u8, |                 EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => *Expr::expr_enum( | ||||||
|  |                     <TagAndBody<UInt, DynSize> as BundleType>::Builder::default() | ||||||
|  |                         .field_tag(tag.from_int_wrapping(op.variant_index())) | ||||||
|  |                         .field_body(match op.variant_value() { | ||||||
|  |                             Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), | ||||||
|  |                             None => body.zero().to_expr(), | ||||||
|  |                         }) | ||||||
|  |                         .to_expr(), | ||||||
|  |                 ), | ||||||
|  |                 EnumTypeState::UInt(_) => *Expr::expr_enum( | ||||||
|  |                     <TagAndBody<UInt, DynSize> as BundleType>::Builder::default() | ||||||
|  |                         .field_tag( | ||||||
|  |                             UIntType::new(op.ty().discriminant_bit_width()) | ||||||
|  |                                 .from_int_wrapping(op.variant_index()), | ||||||
|                         ) |                         ) | ||||||
|  |                         .field_body(match op.variant_value() { | ||||||
|  |                             Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), | ||||||
|  |                             None => UIntType::new( | ||||||
|  |                                 op.ty().type_properties().bit_width | ||||||
|  |                                     - op.ty().discriminant_bit_width(), | ||||||
|  |                             ) | ||||||
|  |                             .zero() | ||||||
|                             .to_expr(), |                             .to_expr(), | ||||||
|                         }) |                         }) | ||||||
|                     .build() |                         .cast_to_bits(), | ||||||
|                     .cast_to_bits() |  | ||||||
|                     .expr_enum(), |  | ||||||
|                 EnumTypeState::Unchanged => ExprEnum::EnumLiteral( |  | ||||||
|                     ops::EnumLiteral::new_unchecked( |  | ||||||
|                         op.variant_value().map(|v| v.fold(self)).transpose()?, |  | ||||||
|                         op.variant_index(), |  | ||||||
|                         op.ty(), |  | ||||||
|                     ) |  | ||||||
|                     .intern_sized(), |  | ||||||
|                 ), |                 ), | ||||||
|  |                 EnumTypeState::Unchanged => ExprEnum::EnumLiteral(ops::EnumLiteral::new_by_index( | ||||||
|  |                     op.ty(), | ||||||
|  |                     op.variant_index(), | ||||||
|  |                     op.variant_value().map(|v| v.fold(self)).transpose()?, | ||||||
|  |                 )), | ||||||
|             }), |             }), | ||||||
|             ExprEnum::VariantAccess(op) => { |             ExprEnum::VariantAccess(op) => Ok( | ||||||
|                 Ok(match self.get_or_make_enum_type_state(op.base().ty())? { |                 match self.get_or_make_enum_type_state(Expr::ty(op.base()))? { | ||||||
|                     EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => { |                     EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => { | ||||||
|                         match op.variant_type().ty { |                         match op.variant_type() { | ||||||
|                             Some(_) => op |                             Some(variant_type) => *Expr::expr_enum( | ||||||
|                                 .base() |                                 Expr::<TagAndBody<CanonicalType, DynSize>>::from_canonical( | ||||||
|                                 .expr_enum() |                                     (*Expr::expr_enum(op.base())).fold(self)?.to_expr(), | ||||||
|                                 .fold(self)? |                                 ) | ||||||
|                                 .to_expr() |                                 .body[..variant_type.bit_width()] | ||||||
|                                 .with_type::<TagAndBody<DynCanonicalValue>>() |                                     .cast_bits_to(variant_type), | ||||||
|                                 .body[..op.ty().bit_width()] |                             ), | ||||||
|                                 .cast_bits_to::<DynCanonicalValue>(op.ty()) |                             None => *Expr::expr_enum(().to_expr()), | ||||||
|                                 .expr_enum(), |  | ||||||
|                             None => ().to_expr().expr_enum(), |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     EnumTypeState::UInt(_) => match op.variant_type().ty { |                     EnumTypeState::UInt(_) => match op.variant_type() { | ||||||
|                         Some(_) => { |                         Some(variant_type) => { | ||||||
|                             let base_int = op |                             let base_int = Expr::<UInt>::from_canonical( | ||||||
|                                 .base() |                                 (*Expr::expr_enum(op.base())).fold(self)?.to_expr(), | ||||||
|                                 .expr_enum() |                             ); | ||||||
|                                 .fold(self)? |  | ||||||
|                                 .to_expr() |  | ||||||
|                                 .with_type::<DynUInt>(); |  | ||||||
|                             dbg!(base_int); |                             dbg!(base_int); | ||||||
|                             let base_ty = op.base().ty(); |                             let base_ty = Expr::ty(op.base()); | ||||||
|                             let ty_bit_width = op.ty().bit_width(); |                             let variant_type_bit_width = variant_type.bit_width(); | ||||||
|                             base_int[base_ty.discriminant_bit_width()..][..ty_bit_width] |                             *Expr::expr_enum( | ||||||
|                                 .cast_bits_to::<DynCanonicalValue>(op.ty()) |                                 base_int[base_ty.discriminant_bit_width()..] | ||||||
|                                 .expr_enum() |                                     [..variant_type_bit_width] | ||||||
|  |                                     .cast_bits_to(variant_type), | ||||||
|  |                             ) | ||||||
|                         } |                         } | ||||||
|                         None => ().to_expr().expr_enum(), |                         None => *Expr::expr_enum(().to_expr()), | ||||||
|                     }, |                     }, | ||||||
|                     EnumTypeState::Unchanged => match op.variant_type().ty { |                     EnumTypeState::Unchanged => match op.variant_type() { | ||||||
|                         Some(_) => ExprEnum::VariantAccess( |                         Some(_) => ExprEnum::VariantAccess(ops::VariantAccess::new_by_index( | ||||||
|                             ops::VariantAccess::new_unchecked( |  | ||||||
|                             op.base().fold(self)?, |                             op.base().fold(self)?, | ||||||
|                             op.variant_index(), |                             op.variant_index(), | ||||||
|                             ) |                         )), | ||||||
|                             .intern_sized(), |                         None => *Expr::expr_enum(().to_expr()), | ||||||
|                         ), |  | ||||||
|                         None => ().to_expr().expr_enum(), |  | ||||||
|                     }, |                     }, | ||||||
|                 }) |                 }, | ||||||
|             } |             ), | ||||||
|             ExprEnum::MemPort(mem_port) => Ok( |             ExprEnum::MemPort(mem_port) => Ok( | ||||||
|                 if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { |                 if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { | ||||||
|                     ExprEnum::Wire(wire) |                     ExprEnum::Wire(wire) | ||||||
|  | @ -310,24 +283,34 @@ impl Folder for State { | ||||||
|                     ExprEnum::MemPort(mem_port.fold(self)?) |                     ExprEnum::MemPort(mem_port.fold(self)?) | ||||||
|                 }, |                 }, | ||||||
|             ), |             ), | ||||||
|             ExprEnum::Literal(_) |             ExprEnum::UIntLiteral(_) | ||||||
|             | ExprEnum::ArrayLiteral(_) |             | ExprEnum::SIntLiteral(_) | ||||||
|  |             | ExprEnum::BoolLiteral(_) | ||||||
|             | ExprEnum::BundleLiteral(_) |             | ExprEnum::BundleLiteral(_) | ||||||
|  |             | ExprEnum::ArrayLiteral(_) | ||||||
|             | ExprEnum::NotU(_) |             | ExprEnum::NotU(_) | ||||||
|             | ExprEnum::NotS(_) |             | ExprEnum::NotS(_) | ||||||
|  |             | ExprEnum::NotB(_) | ||||||
|             | ExprEnum::Neg(_) |             | ExprEnum::Neg(_) | ||||||
|             | ExprEnum::BitAndU(_) |             | ExprEnum::BitAndU(_) | ||||||
|             | ExprEnum::BitAndS(_) |             | ExprEnum::BitAndS(_) | ||||||
|  |             | ExprEnum::BitAndB(_) | ||||||
|             | ExprEnum::BitOrU(_) |             | ExprEnum::BitOrU(_) | ||||||
|             | ExprEnum::BitOrS(_) |             | ExprEnum::BitOrS(_) | ||||||
|  |             | ExprEnum::BitOrB(_) | ||||||
|             | ExprEnum::BitXorU(_) |             | ExprEnum::BitXorU(_) | ||||||
|             | ExprEnum::BitXorS(_) |             | ExprEnum::BitXorS(_) | ||||||
|  |             | ExprEnum::BitXorB(_) | ||||||
|             | ExprEnum::AddU(_) |             | ExprEnum::AddU(_) | ||||||
|             | ExprEnum::AddS(_) |             | ExprEnum::AddS(_) | ||||||
|             | ExprEnum::SubU(_) |             | ExprEnum::SubU(_) | ||||||
|             | ExprEnum::SubS(_) |             | ExprEnum::SubS(_) | ||||||
|             | ExprEnum::MulU(_) |             | ExprEnum::MulU(_) | ||||||
|             | ExprEnum::MulS(_) |             | ExprEnum::MulS(_) | ||||||
|  |             | ExprEnum::DivU(_) | ||||||
|  |             | ExprEnum::DivS(_) | ||||||
|  |             | ExprEnum::RemU(_) | ||||||
|  |             | ExprEnum::RemS(_) | ||||||
|             | ExprEnum::DynShlU(_) |             | ExprEnum::DynShlU(_) | ||||||
|             | ExprEnum::DynShlS(_) |             | ExprEnum::DynShlS(_) | ||||||
|             | ExprEnum::DynShrU(_) |             | ExprEnum::DynShrU(_) | ||||||
|  | @ -336,41 +319,68 @@ impl Folder for State { | ||||||
|             | ExprEnum::FixedShlS(_) |             | ExprEnum::FixedShlS(_) | ||||||
|             | ExprEnum::FixedShrU(_) |             | ExprEnum::FixedShrU(_) | ||||||
|             | ExprEnum::FixedShrS(_) |             | ExprEnum::FixedShrS(_) | ||||||
|  |             | ExprEnum::CmpLtB(_) | ||||||
|  |             | ExprEnum::CmpLeB(_) | ||||||
|  |             | ExprEnum::CmpGtB(_) | ||||||
|  |             | ExprEnum::CmpGeB(_) | ||||||
|  |             | ExprEnum::CmpEqB(_) | ||||||
|  |             | ExprEnum::CmpNeB(_) | ||||||
|             | ExprEnum::CmpLtU(_) |             | ExprEnum::CmpLtU(_) | ||||||
|             | ExprEnum::CmpLtS(_) |  | ||||||
|             | ExprEnum::CmpLeU(_) |             | ExprEnum::CmpLeU(_) | ||||||
|             | ExprEnum::CmpLeS(_) |  | ||||||
|             | ExprEnum::CmpGtU(_) |             | ExprEnum::CmpGtU(_) | ||||||
|             | ExprEnum::CmpGtS(_) |  | ||||||
|             | ExprEnum::CmpGeU(_) |             | ExprEnum::CmpGeU(_) | ||||||
|             | ExprEnum::CmpGeS(_) |  | ||||||
|             | ExprEnum::CmpEqU(_) |             | ExprEnum::CmpEqU(_) | ||||||
|             | ExprEnum::CmpEqS(_) |  | ||||||
|             | ExprEnum::CmpNeU(_) |             | ExprEnum::CmpNeU(_) | ||||||
|  |             | ExprEnum::CmpLtS(_) | ||||||
|  |             | ExprEnum::CmpLeS(_) | ||||||
|  |             | ExprEnum::CmpGtS(_) | ||||||
|  |             | ExprEnum::CmpGeS(_) | ||||||
|  |             | ExprEnum::CmpEqS(_) | ||||||
|             | ExprEnum::CmpNeS(_) |             | ExprEnum::CmpNeS(_) | ||||||
|             | ExprEnum::CastUIntToUInt(_) |             | ExprEnum::CastUIntToUInt(_) | ||||||
|             | ExprEnum::CastUIntToSInt(_) |             | ExprEnum::CastUIntToSInt(_) | ||||||
|             | ExprEnum::CastSIntToUInt(_) |             | ExprEnum::CastSIntToUInt(_) | ||||||
|             | ExprEnum::CastSIntToSInt(_) |             | ExprEnum::CastSIntToSInt(_) | ||||||
|             | ExprEnum::SliceUInt(_) |             | ExprEnum::CastBoolToUInt(_) | ||||||
|             | ExprEnum::SliceSInt(_) |             | ExprEnum::CastBoolToSInt(_) | ||||||
|             | ExprEnum::ReduceBitAnd(_) |             | ExprEnum::CastUIntToBool(_) | ||||||
|             | ExprEnum::ReduceBitOr(_) |             | ExprEnum::CastSIntToBool(_) | ||||||
|             | ExprEnum::ReduceBitXor(_) |             | ExprEnum::CastBoolToSyncReset(_) | ||||||
|  |             | ExprEnum::CastUIntToSyncReset(_) | ||||||
|  |             | ExprEnum::CastSIntToSyncReset(_) | ||||||
|  |             | ExprEnum::CastBoolToAsyncReset(_) | ||||||
|  |             | ExprEnum::CastUIntToAsyncReset(_) | ||||||
|  |             | ExprEnum::CastSIntToAsyncReset(_) | ||||||
|  |             | ExprEnum::CastSyncResetToBool(_) | ||||||
|  |             | ExprEnum::CastSyncResetToUInt(_) | ||||||
|  |             | ExprEnum::CastSyncResetToSInt(_) | ||||||
|  |             | ExprEnum::CastSyncResetToReset(_) | ||||||
|  |             | ExprEnum::CastAsyncResetToBool(_) | ||||||
|  |             | ExprEnum::CastAsyncResetToUInt(_) | ||||||
|  |             | ExprEnum::CastAsyncResetToSInt(_) | ||||||
|  |             | ExprEnum::CastAsyncResetToReset(_) | ||||||
|  |             | ExprEnum::CastResetToBool(_) | ||||||
|  |             | ExprEnum::CastResetToUInt(_) | ||||||
|  |             | ExprEnum::CastResetToSInt(_) | ||||||
|  |             | ExprEnum::CastBoolToClock(_) | ||||||
|  |             | ExprEnum::CastUIntToClock(_) | ||||||
|  |             | ExprEnum::CastSIntToClock(_) | ||||||
|  |             | ExprEnum::CastClockToBool(_) | ||||||
|  |             | ExprEnum::CastClockToUInt(_) | ||||||
|  |             | ExprEnum::CastClockToSInt(_) | ||||||
|             | ExprEnum::FieldAccess(_) |             | ExprEnum::FieldAccess(_) | ||||||
|             | ExprEnum::ArrayIndex(_) |             | ExprEnum::ArrayIndex(_) | ||||||
|             | ExprEnum::DynArrayIndex(_) |             | ExprEnum::DynArrayIndex(_) | ||||||
|  |             | ExprEnum::ReduceBitAndU(_) | ||||||
|  |             | ExprEnum::ReduceBitAndS(_) | ||||||
|  |             | ExprEnum::ReduceBitOrU(_) | ||||||
|  |             | ExprEnum::ReduceBitOrS(_) | ||||||
|  |             | ExprEnum::ReduceBitXorU(_) | ||||||
|  |             | ExprEnum::ReduceBitXorS(_) | ||||||
|  |             | ExprEnum::SliceUInt(_) | ||||||
|  |             | ExprEnum::SliceSInt(_) | ||||||
|             | ExprEnum::CastToBits(_) |             | ExprEnum::CastToBits(_) | ||||||
|             | ExprEnum::CastBitsTo(_) |             | ExprEnum::CastBitsTo(_) | ||||||
|             | ExprEnum::CastBitToClock(_) |  | ||||||
|             | ExprEnum::CastBitToSyncReset(_) |  | ||||||
|             | ExprEnum::CastBitToAsyncReset(_) |  | ||||||
|             | ExprEnum::CastSyncResetToReset(_) |  | ||||||
|             | ExprEnum::CastAsyncResetToReset(_) |  | ||||||
|             | ExprEnum::CastClockToBit(_) |  | ||||||
|             | ExprEnum::CastSyncResetToBit(_) |  | ||||||
|             | ExprEnum::CastAsyncResetToBit(_) |  | ||||||
|             | ExprEnum::CastResetToBit(_) |  | ||||||
|             | ExprEnum::ModuleIO(_) |             | ExprEnum::ModuleIO(_) | ||||||
|             | ExprEnum::Instance(_) |             | ExprEnum::Instance(_) | ||||||
|             | ExprEnum::Wire(_) |             | ExprEnum::Wire(_) | ||||||
|  | @ -382,7 +392,7 @@ impl Folder for State { | ||||||
|         let mut memories = vec![]; |         let mut memories = vec![]; | ||||||
|         let mut stmts = vec![]; |         let mut stmts = vec![]; | ||||||
|         for memory in block.memories { |         for memory in block.memories { | ||||||
|             let old_element_ty = *memory.array_type().element(); |             let old_element_ty = memory.array_type().element(); | ||||||
|             let new_element_ty = memory.array_type().element().fold(self)?; |             let new_element_ty = memory.array_type().element().fold(self)?; | ||||||
|             if new_element_ty != old_element_ty { |             if new_element_ty != old_element_ty { | ||||||
|                 let mut new_ports = vec![]; |                 let mut new_ports = vec![]; | ||||||
|  | @ -394,7 +404,7 @@ impl Folder for State { | ||||||
|                         port.addr_type(), |                         port.addr_type(), | ||||||
|                         new_element_ty, |                         new_element_ty, | ||||||
|                     ); |                     ); | ||||||
|                     new_ports.push(new_port.intern()); |                     new_ports.push(new_port); | ||||||
|                     let new_port_ty = new_port.ty(); |                     let new_port_ty = new_port.ty(); | ||||||
|                     let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields()); |                     let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields()); | ||||||
|                     if let Some(wmask_name) = new_port.port_kind().wmask_name() { |                     if let Some(wmask_name) = new_port.port_kind().wmask_name() { | ||||||
|  | @ -404,7 +414,7 @@ impl Folder for State { | ||||||
|                             .unwrap(); |                             .unwrap(); | ||||||
|                         wire_ty_fields[index].ty = port.ty().fields()[index].ty; |                         wire_ty_fields[index].ty = port.ty().fields()[index].ty; | ||||||
|                     } |                     } | ||||||
|                     let wire_ty = DynBundleType::new(Intern::intern_owned(wire_ty_fields)); |                     let wire_ty = Bundle::new(Intern::intern_owned(wire_ty_fields)); | ||||||
|                     if wire_ty == new_port_ty { |                     if wire_ty == new_port_ty { | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|  | @ -419,23 +429,22 @@ impl Folder for State { | ||||||
|                     stmts.push( |                     stmts.push( | ||||||
|                         StmtWire { |                         StmtWire { | ||||||
|                             annotations: Default::default(), |                             annotations: Default::default(), | ||||||
|                             wire: wire.to_dyn_canonical_wire(), |                             wire: wire.canonical(), | ||||||
|                         } |                         } | ||||||
|                         .into(), |                         .into(), | ||||||
|                     ); |                     ); | ||||||
|                     connect_port( |                     connect_port( | ||||||
|                         &mut stmts, |                         &mut stmts, | ||||||
|                         new_port.to_expr().to_canonical_dyn(), |                         Expr::canonical(new_port.to_expr()), | ||||||
|                         wire.to_expr().to_canonical_dyn(), |                         Expr::canonical(wire.to_expr()), | ||||||
|                         port.source_location(), |                         port.source_location(), | ||||||
|                     ); |                     ); | ||||||
|                     self.replacement_mem_ports |                     self.replacement_mem_ports.insert(port, wire.canonical()); | ||||||
|                         .insert(port, wire.to_dyn_canonical_wire().intern_sized()); |  | ||||||
|                 } |                 } | ||||||
|                 memories.push(Mem::new_unchecked( |                 memories.push(Mem::new_unchecked( | ||||||
|                     memory.scoped_name(), |                     memory.scoped_name(), | ||||||
|                     memory.source_location(), |                     memory.source_location(), | ||||||
|                     ArrayType::new_slice(new_element_ty, memory.array_type().len()), |                     ArrayType::new_dyn(new_element_ty, memory.array_type().len()), | ||||||
|                     memory.initial_value(), |                     memory.initial_value(), | ||||||
|                     Intern::intern_owned(new_ports), |                     Intern::intern_owned(new_ports), | ||||||
|                     memory.read_latency(), |                     memory.read_latency(), | ||||||
|  | @ -458,7 +467,7 @@ impl Folder for State { | ||||||
|     fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> { |     fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> { | ||||||
|         fn match_int_tag( |         fn match_int_tag( | ||||||
|             state: &mut State, |             state: &mut State, | ||||||
|             int_tag_expr: Expr<DynUInt>, |             int_tag_expr: Expr<UInt>, | ||||||
|             source_location: SourceLocation, |             source_location: SourceLocation, | ||||||
|             blocks: Interned<[Block]>, |             blocks: Interned<[Block]>, | ||||||
|         ) -> Result<StmtIf, SimplifyEnumsError> { |         ) -> Result<StmtIf, SimplifyEnumsError> { | ||||||
|  | @ -473,16 +482,15 @@ impl Folder for State { | ||||||
|                 }); |                 }); | ||||||
|             }; |             }; | ||||||
|             let mut retval = StmtIf { |             let mut retval = StmtIf { | ||||||
|                 cond: int_tag_expr.cmp_eq(DynUInt::with_type( |                 cond: int_tag_expr | ||||||
|                     int_tag_expr.ty(), |                     .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)), | ||||||
|                     next_to_last_variant_index, |  | ||||||
|                 )), |  | ||||||
|                 source_location, |                 source_location, | ||||||
|                 blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?], |                 blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?], | ||||||
|             }; |             }; | ||||||
|             for (variant_index, block) in blocks_iter.rev() { |             for (variant_index, block) in blocks_iter.rev() { | ||||||
|                 retval = StmtIf { |                 retval = StmtIf { | ||||||
|                     cond: int_tag_expr.cmp_eq(DynUInt::with_type(int_tag_expr.ty(), variant_index)), |                     cond: int_tag_expr | ||||||
|  |                         .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)), | ||||||
|                     source_location, |                     source_location, | ||||||
|                     blocks: [ |                     blocks: [ | ||||||
|                         block.fold(state)?, |                         block.fold(state)?, | ||||||
|  | @ -500,33 +508,27 @@ impl Folder for State { | ||||||
|                 expr, |                 expr, | ||||||
|                 source_location, |                 source_location, | ||||||
|                 blocks, |                 blocks, | ||||||
|             }) => match self.get_or_make_enum_type_state(expr.ty())? { |             }) => match self.get_or_make_enum_type_state(Expr::ty(expr))? { | ||||||
|                 EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch { |                 EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch { | ||||||
|                     expr: expr |                     expr: Expr::<TagAndBody<Enum, DynSize>>::from_canonical( | ||||||
|                         .expr_enum() |                         Expr::canonical(expr).fold(self)?, | ||||||
|                         .fold(self)? |                     ) | ||||||
|                         .to_expr() |  | ||||||
|                         .with_type::<TagAndBody<DynEnum>>() |  | ||||||
|                     .tag, |                     .tag, | ||||||
|                     source_location, |                     source_location, | ||||||
|                     blocks: blocks.fold(self)?, |                     blocks: blocks.fold(self)?, | ||||||
|                 } |                 } | ||||||
|                 .into()), |                 .into()), | ||||||
|                 EnumTypeState::TagUIntAndBody(_) => { |                 EnumTypeState::TagUIntAndBody(_) => { | ||||||
|                     let int_tag_expr = expr |                     let int_tag_expr = Expr::<TagAndBody<UInt, DynSize>>::from_canonical( | ||||||
|                         .expr_enum() |                         Expr::canonical(expr).fold(self)?, | ||||||
|                         .fold(self)? |                     ) | ||||||
|                         .to_expr() |  | ||||||
|                         .with_type::<TagAndBody<DynUInt>>() |  | ||||||
|                     .tag; |                     .tag; | ||||||
|                     Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) |                     Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) | ||||||
|                 } |                 } | ||||||
|                 EnumTypeState::UInt(_) => { |                 EnumTypeState::UInt(_) => { | ||||||
|                     let int_tag_expr = expr |                     let int_tag_expr = | ||||||
|                         .expr_enum() |                         Expr::<UInt>::from_canonical(Expr::canonical(expr).fold(self)?) | ||||||
|                         .fold(self)? |                             [..Expr::ty(expr).discriminant_bit_width()]; | ||||||
|                         .to_expr() |  | ||||||
|                         .with_type::<DynUInt>()[..expr.ty().discriminant_bit_width()]; |  | ||||||
|                     Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) |                     Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) | ||||||
|                 } |                 } | ||||||
|                 EnumTypeState::Unchanged => Ok(StmtMatch { |                 EnumTypeState::Unchanged => Ok(StmtMatch { | ||||||
|  | @ -544,92 +546,49 @@ impl Folder for State { | ||||||
|         unreachable!() |         unreachable!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_type_enum(&mut self, type_enum: TypeEnum) -> Result<TypeEnum, Self::Error> { |     fn fold_canonical_type( | ||||||
|         match type_enum { |         &mut self, | ||||||
|             TypeEnum::EnumType(enum_type) => { |         canonical_type: CanonicalType, | ||||||
|  |     ) -> Result<CanonicalType, Self::Error> { | ||||||
|  |         match canonical_type { | ||||||
|  |             CanonicalType::Enum(enum_type) => { | ||||||
|                 Ok(match self.get_or_make_enum_type_state(enum_type)? { |                 Ok(match self.get_or_make_enum_type_state(enum_type)? { | ||||||
|                     EnumTypeState::TagEnumAndBody(ty) => TypeEnum::BundleType(ty.canonical()), |                     EnumTypeState::TagEnumAndBody(ty) => ty.canonical(), | ||||||
|                     EnumTypeState::TagUIntAndBody(ty) => TypeEnum::BundleType(ty.canonical()), |                     EnumTypeState::TagUIntAndBody(ty) => ty.canonical(), | ||||||
|                     EnumTypeState::UInt(ty) => TypeEnum::UInt(ty), |                     EnumTypeState::UInt(ty) => ty.canonical(), | ||||||
|                     EnumTypeState::Unchanged => TypeEnum::EnumType(enum_type), |                     EnumTypeState::Unchanged => enum_type.canonical(), | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
|             TypeEnum::BundleType(_) |             CanonicalType::Bundle(_) | ||||||
|             | TypeEnum::ArrayType(_) |             | CanonicalType::Array(_) | ||||||
|             | TypeEnum::UInt(_) |             | CanonicalType::UInt(_) | ||||||
|             | TypeEnum::SInt(_) |             | CanonicalType::SInt(_) | ||||||
|             | TypeEnum::Clock(_) |             | CanonicalType::Bool(_) | ||||||
|             | TypeEnum::AsyncReset(_) |             | CanonicalType::Clock(_) | ||||||
|             | TypeEnum::SyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | TypeEnum::Reset(_) => type_enum.default_fold(self), |             | CanonicalType::SyncReset(_) | ||||||
|  |             | CanonicalType::Reset(_) => canonical_type.default_fold(self), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_value_enum(&mut self, value_enum: ValueEnum) -> Result<ValueEnum, Self::Error> { |     fn fold_enum_variant(&mut self, _v: EnumVariant) -> Result<EnumVariant, Self::Error> { | ||||||
|         match value_enum { |  | ||||||
|             ValueEnum::Enum(enum_value) => { |  | ||||||
|                 Ok(match self.get_or_make_enum_type_state(enum_value.ty())? { |  | ||||||
|                     EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { |  | ||||||
|                         tag: tag_ty, |  | ||||||
|                         body: body_ty, |  | ||||||
|                     }) => ValueEnum::Bundle( |  | ||||||
|                         TagAndBody { |  | ||||||
|                             tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None), |  | ||||||
|                             body: value_to_uint(enum_value.variant_value().as_ref(), body_ty), |  | ||||||
|                         } |  | ||||||
|                         .to_canonical(), |  | ||||||
|                     ), |  | ||||||
|                     EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { |  | ||||||
|                         tag: tag_ty, |  | ||||||
|                         body: body_ty, |  | ||||||
|                     }) => ValueEnum::Bundle( |  | ||||||
|                         TagAndBody { |  | ||||||
|                             tag: DynUInt::with_type(tag_ty, enum_value.variant_index()), |  | ||||||
|                             body: value_to_uint(enum_value.variant_value().as_ref(), body_ty), |  | ||||||
|                         } |  | ||||||
|                         .to_canonical(), |  | ||||||
|                     ), |  | ||||||
|                     EnumTypeState::UInt(target_ty) => { |  | ||||||
|                         ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty)) |  | ||||||
|                     } |  | ||||||
|                     EnumTypeState::Unchanged => ValueEnum::Enum(enum_value), |  | ||||||
|                 }) |  | ||||||
|             } |  | ||||||
|             ValueEnum::Bundle(_) |  | ||||||
|             | ValueEnum::Array(_) |  | ||||||
|             | ValueEnum::UInt(_) |  | ||||||
|             | ValueEnum::SInt(_) |  | ||||||
|             | ValueEnum::Clock(_) |  | ||||||
|             | ValueEnum::AsyncReset(_) |  | ||||||
|             | ValueEnum::SyncReset(_) |  | ||||||
|             | ValueEnum::Reset(_) => value_enum.default_fold(self), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn fold_variant_type<T>(&mut self, _v: VariantType<T>) -> Result<VariantType<T>, Self::Error> { |  | ||||||
|         unreachable!() |         unreachable!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_enum_literal<EnumTy>( |     fn fold_enum_literal<T: EnumType>( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _v: ops::EnumLiteral<EnumTy>, |         _v: ops::EnumLiteral<T>, | ||||||
|     ) -> Result<ops::EnumLiteral<EnumTy>, Self::Error> |     ) -> Result<ops::EnumLiteral<T>, Self::Error> | ||||||
|     where |     where | ||||||
|         EnumTy: EnumType, |         T: Fold<Self>, | ||||||
|         EnumTy::Value: EnumValue<Type = EnumTy>, |  | ||||||
|     { |     { | ||||||
|         unreachable!() |         unreachable!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_variant_access<T, VariantTy>( |     fn fold_variant_access<VariantType: Type>( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _v: ops::VariantAccess<T, VariantTy>, |         _v: ops::VariantAccess<VariantType>, | ||||||
|     ) -> Result<ops::VariantAccess<T, VariantTy>, Self::Error> |     ) -> Result<ops::VariantAccess<VariantType>, Self::Error> { | ||||||
|     where |  | ||||||
|         T: EnumType, |  | ||||||
|         T::Value: EnumValue, |  | ||||||
|         VariantTy: Type, |  | ||||||
|     { |  | ||||||
|         unreachable!() |         unreachable!() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -642,9 +601,9 @@ pub enum SimplifyEnumsKind { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn simplify_enums( | pub fn simplify_enums( | ||||||
|     module: Interned<Module<DynBundle>>, |     module: Interned<Module<Bundle>>, | ||||||
|     kind: SimplifyEnumsKind, |     kind: SimplifyEnumsKind, | ||||||
| ) -> Result<Interned<Module<DynBundle>>, SimplifyEnumsError> { | ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> { | ||||||
|     module.fold(&mut State { |     module.fold(&mut State { | ||||||
|         enum_types: HashMap::new(), |         enum_types: HashMap::new(), | ||||||
|         replacement_mem_ports: HashMap::new(), |         replacement_mem_ports: HashMap::new(), | ||||||
|  |  | ||||||
|  | @ -3,18 +3,19 @@ | ||||||
| #![allow(clippy::multiple_bound_locations)] | #![allow(clippy::multiple_bound_locations)] | ||||||
| use crate::{ | use crate::{ | ||||||
|     annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation}, |     annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation}, | ||||||
|     array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice}, |     array::ArrayType, | ||||||
|     bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType}, |     bundle::{Bundle, BundleField, BundleType}, | ||||||
|     clock::{Clock, ClockType}, |     clock::Clock, | ||||||
|     enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType}, |     enum_::{Enum, EnumType, EnumVariant}, | ||||||
|     expr::{ |     expr::{ | ||||||
|         ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement, |         ops, | ||||||
|         TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr, |         target::{ | ||||||
|  |             Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, | ||||||
|  |             TargetPathDynArrayElement, TargetPathElement, | ||||||
|         }, |         }, | ||||||
|     int::{ |         Expr, ExprEnum, | ||||||
|         DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, IntType, |  | ||||||
|         IntTypeTrait, |  | ||||||
|     }, |     }, | ||||||
|  |     int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, |     memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, | ||||||
|     module::{ |     module::{ | ||||||
|  | @ -24,10 +25,9 @@ use crate::{ | ||||||
|         StmtMatch, StmtReg, StmtWire, |         StmtMatch, StmtReg, StmtWire, | ||||||
|     }, |     }, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType}, |     reset::{AsyncReset, Reset, SyncReset}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum}, |     ty::{CanonicalType, Type}, | ||||||
|     util::{ConstBool, GenericConstBool}, |  | ||||||
|     wire::Wire, |     wire::Wire, | ||||||
| }; | }; | ||||||
| use num_bigint::{BigInt, BigUint}; | use num_bigint::{BigInt, BigUint}; | ||||||
|  | @ -473,7 +473,4 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type InternedDynType = Interned<dyn DynType>; |  | ||||||
| type InternedDynCanonicalType = Interned<dyn DynCanonicalType>; |  | ||||||
| 
 |  | ||||||
| include!(concat!(env!("OUT_DIR"), "/visit.rs")); | include!(concat!(env!("OUT_DIR"), "/visit.rs")); | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								crates/fayalite/src/prelude.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								crates/fayalite/src/prelude.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | pub use crate::{ | ||||||
|  |     annotations::Annotation, | ||||||
|  |     array::{Array, ArrayType}, | ||||||
|  |     clock::{Clock, ClockDomain, ToClock}, | ||||||
|  |     enum_::HdlOption, | ||||||
|  |     expr::{CastBitsTo, CastToBits, Expr, ReduceBits, ToExpr}, | ||||||
|  |     hdl, hdl_module, | ||||||
|  |     int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, | ||||||
|  |     memory::{Mem, ReadUnderWrite}, | ||||||
|  |     module::{ | ||||||
|  |         annotate, connect, connect_any, instance, memory, memory_array, memory_with_init, | ||||||
|  |         reg_builder, wire, Instance, Module, ModuleBuilder, | ||||||
|  |     }, | ||||||
|  |     reg::Reg, | ||||||
|  |     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, | ||||||
|  |     source_location::SourceLocation, | ||||||
|  |     ty::{AsMask, CanonicalType, Type}, | ||||||
|  |     util::{ConstUsize, GenericConstUsize}, | ||||||
|  |     wire::Wire, | ||||||
|  | }; | ||||||
|  | @ -2,21 +2,21 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     clock::ClockDomain, |     clock::ClockDomain, | ||||||
|     expr::{Expr, ExprTrait, Flow, ToExpr}, |     expr::{Expr, Flow}, | ||||||
|     intern::Interned, |     intern::Interned, | ||||||
|     module::{NameId, ScopedNameId}, |     module::{NameId, ScopedNameId}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{DynCanonicalType, DynType, Type}, |     ty::{CanonicalType, Type}, | ||||||
| }; | }; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq, PartialEq, Hash)] | #[derive(Copy, Clone, Eq, PartialEq, Hash)] | ||||||
| pub struct Reg<T: Type> { | pub struct Reg<T: Type> { | ||||||
|     name: ScopedNameId, |     name: ScopedNameId, | ||||||
|     source_location: SourceLocation, |     source_location: SourceLocation, | ||||||
|     ty: T, |     ty: T, | ||||||
|     clock_domain: Expr<ClockDomain>, |     clock_domain: Expr<ClockDomain>, | ||||||
|     init: Option<Expr<T::Value>>, |     init: Option<Expr<T>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Type + fmt::Debug> fmt::Debug for Reg<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> { | impl<T: Type> Reg<T> { | ||||||
|     pub fn canonical(&self) -> Reg<T::CanonicalType> { |     pub fn canonical(&self) -> Reg<CanonicalType> { | ||||||
|         let Self { |         let Self { | ||||||
|             name, |             name, | ||||||
|             source_location, |             source_location, | ||||||
|  | @ -66,49 +54,20 @@ impl<T: Type> Reg<T> { | ||||||
|             init: init.map(Expr::canonical), |             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] |     #[track_caller] | ||||||
|     pub fn new_unchecked( |     pub fn new_unchecked( | ||||||
|         scoped_name: ScopedNameId, |         scoped_name: ScopedNameId, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|         ty: T, |         ty: T, | ||||||
|         clock_domain: Expr<ClockDomain>, |         clock_domain: Expr<ClockDomain>, | ||||||
|         init: Option<Expr<T::Value>>, |         init: Option<Expr<T>>, | ||||||
|     ) -> Self { |     ) -> 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 { |         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 { |         Self { | ||||||
|             name: scoped_name, |             name: scoped_name, | ||||||
|  | @ -118,6 +77,9 @@ impl<T: Type> Reg<T> { | ||||||
|             init, |             init, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     pub fn ty(&self) -> T { | ||||||
|  |         self.ty | ||||||
|  |     } | ||||||
|     pub fn source_location(&self) -> SourceLocation { |     pub fn source_location(&self) -> SourceLocation { | ||||||
|         self.source_location |         self.source_location | ||||||
|     } |     } | ||||||
|  | @ -139,7 +101,7 @@ impl<T: Type> Reg<T> { | ||||||
|     pub fn clock_domain(&self) -> Expr<ClockDomain> { |     pub fn clock_domain(&self) -> Expr<ClockDomain> { | ||||||
|         self.clock_domain |         self.clock_domain | ||||||
|     } |     } | ||||||
|     pub fn init(&self) -> Option<Expr<T::Value>> { |     pub fn init(&self) -> Option<Expr<T>> { | ||||||
|         self.init |         self.init | ||||||
|     } |     } | ||||||
|     pub fn flow(&self) -> Flow { |     pub fn flow(&self) -> Flow { | ||||||
|  |  | ||||||
|  | @ -2,358 +2,119 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{Expr, ToExpr}, |     expr::{Expr, ToExpr}, | ||||||
|     int::{UInt, UIntType}, |     int::Bool, | ||||||
|     intern::Interned, |  | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||||
|         impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, |  | ||||||
|         DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |  | ||||||
|     util::interned_bit, |  | ||||||
| }; | }; | ||||||
| use bitvec::slice::BitSlice; |  | ||||||
| 
 | 
 | ||||||
| pub trait ResetTypeTrait: CanonicalType + StaticType<MaskType = UIntType<1>> {} | mod sealed { | ||||||
| 
 |     pub trait ResetTypeSealed {} | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] |  | ||||||
| pub struct AsyncResetType; |  | ||||||
| 
 |  | ||||||
| impl AsyncResetType { |  | ||||||
|     pub const fn new() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Type for AsyncResetType { | pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {} | ||||||
|     type Value = AsyncReset; |  | ||||||
|     type CanonicalType = AsyncResetType; |  | ||||||
|     type CanonicalValue = AsyncReset; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
| 
 | 
 | ||||||
|     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; | ||||||
|  | 
 | ||||||
|  |         impl Type for $name { | ||||||
|  |             type BaseType = $name; | ||||||
|  |             type MaskType = Bool; | ||||||
|  | 
 | ||||||
|  |             impl_match_variant_as_self!(); | ||||||
| 
 | 
 | ||||||
|             fn mask_type(&self) -> Self::MaskType { |             fn mask_type(&self) -> Self::MaskType { | ||||||
|         UIntType::new() |                 Bool | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |             fn canonical(&self) -> CanonicalType { | ||||||
|         *self |                 CanonicalType::$name(*self) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|     fn source_location(&self) -> SourceLocation { |             fn source_location() -> SourceLocation { | ||||||
|                 SourceLocation::builtin() |                 SourceLocation::builtin() | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|     fn type_enum(&self) -> TypeEnum { |             fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|         TypeEnum::AsyncReset(*self) |                 let CanonicalType::$name(retval) = canonical_type else { | ||||||
|  |                     panic!("expected {}", stringify!($name)); | ||||||
|  |                 }; | ||||||
|  |                 retval | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |         impl $name { | ||||||
|         t |             pub fn type_properties(self) -> TypeProperties { | ||||||
|  |                 Self::TYPE_PROPERTIES | ||||||
|  |             } | ||||||
|  |             pub fn can_connect(self, _rhs: Self) -> bool { | ||||||
|  |                 true | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |         impl StaticType for $name { | ||||||
|         Some(this) |             const TYPE: Self = Self; | ||||||
|     } |             const MASK_TYPE: Self::MaskType = Bool; | ||||||
| } |             const TYPE_PROPERTIES: TypeProperties = TypeProperties { | ||||||
| 
 |                 is_passive: true, | ||||||
| impl Connect<Self> for AsyncResetType {} |                 is_storable: false, | ||||||
| 
 |                 is_castable_from_bits: $is_castable_from_bits, | ||||||
| impl CanonicalType for AsyncResetType { |                 bit_width: 1, | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset; |             }; | ||||||
| } |             const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; | ||||||
| 
 |  | ||||||
| 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> { |         impl sealed::ResetTypeSealed for $name {} | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| impl Value for AsyncReset { |         impl ResetType for $name {} | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| impl CanonicalValue for AsyncReset { |         pub trait $Trait { | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |             fn $trait_fn(&self) -> Expr<$name>; | ||||||
|         ValueEnum::AsyncReset(*this) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] |  | ||||||
| pub struct SyncResetType; |  | ||||||
| 
 |  | ||||||
| impl SyncResetType { |  | ||||||
|     pub const fn new() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Type for SyncResetType { |  | ||||||
|     type CanonicalType = SyncResetType; |  | ||||||
|     type Value = SyncReset; |  | ||||||
|     type CanonicalValue = SyncReset; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
| 
 |  | ||||||
|     impl_match_values_as_self!(); |  | ||||||
| 
 |  | ||||||
|     fn mask_type(&self) -> Self::MaskType { |  | ||||||
|         UIntType::new() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location(&self) -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn type_enum(&self) -> TypeEnum { |  | ||||||
|         TypeEnum::SyncReset(*self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |  | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Connect<Self> for SyncResetType {} |  | ||||||
| 
 |  | ||||||
| impl CanonicalType for SyncResetType { |  | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl StaticType for SyncResetType { |  | ||||||
|     fn static_type() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ResetTypeTrait for SyncResetType {} |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] |  | ||||||
| pub struct SyncReset(pub bool); |  | ||||||
| 
 |  | ||||||
| impl ToExpr for SyncReset { |  | ||||||
|     type Type = SyncResetType; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         SyncResetType |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<Self> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Value for SyncReset { |  | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for SyncReset { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::SyncReset(*this) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         interned_bit(this.0) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)] |  | ||||||
| pub struct ResetType; |  | ||||||
| 
 |  | ||||||
| impl ResetType { |  | ||||||
|     pub const fn new() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Type for ResetType { |  | ||||||
|     type Value = Reset; |  | ||||||
|     type CanonicalType = ResetType; |  | ||||||
|     type CanonicalValue = Reset; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
| 
 |  | ||||||
|     impl_match_values_as_self!(); |  | ||||||
| 
 |  | ||||||
|     fn mask_type(&self) -> Self::MaskType { |  | ||||||
|         UIntType::new() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location(&self) -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn type_enum(&self) -> TypeEnum { |  | ||||||
|         TypeEnum::Reset(*self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |  | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Connect<Self> for ResetType {} |  | ||||||
| 
 |  | ||||||
| impl CanonicalType for ResetType { |  | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl StaticType for ResetType { |  | ||||||
|     fn static_type() -> Self { |  | ||||||
|         Self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ResetTypeTrait for ResetType {} |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] |  | ||||||
| pub enum Reset {} |  | ||||||
| 
 |  | ||||||
| impl ToExpr for Reset { |  | ||||||
|     type Type = ResetType; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         match *self {} |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<Self> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Value for Reset { |  | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         match *this {} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for Reset { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::Reset(*this) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         match *this {} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| macro_rules! make_to_reset { |  | ||||||
|     ( |  | ||||||
|         $(#[from_value($from_value_ty:ty)])* |  | ||||||
|         $vis:vis trait $Trait:ident { |  | ||||||
|             fn $fn:ident(&self) -> Expr<$T:ty>; |  | ||||||
|         } |  | ||||||
|     ) => { |  | ||||||
|         $vis trait $Trait { |  | ||||||
|             fn $fn(&self) -> Expr<$T>; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<T: ?Sized + $Trait> $Trait for &'_ T { |         impl<T: ?Sized + $Trait> $Trait for &'_ T { | ||||||
|             fn $fn(&self) -> Expr<$T> { |             fn $trait_fn(&self) -> Expr<$name> { | ||||||
|                 (**self).$fn() |                 (**self).$trait_fn() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<T: ?Sized + $Trait> $Trait for &'_ mut T { |         impl<T: ?Sized + $Trait> $Trait for &'_ mut T { | ||||||
|             fn $fn(&self) -> Expr<$T> { |             fn $trait_fn(&self) -> Expr<$name> { | ||||||
|                 (**self).$fn() |                 (**self).$trait_fn() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<T: ?Sized + $Trait> $Trait for Box<T> { |         impl<T: ?Sized + $Trait> $Trait for Box<T> { | ||||||
|             fn $fn(&self) -> Expr<$T> { |             fn $trait_fn(&self) -> Expr<$name> { | ||||||
|                 (**self).$fn() |                 (**self).$trait_fn() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl $Trait for Expr<$T> { |         impl $Trait for Expr<$name> { | ||||||
|             fn $fn(&self) -> Expr<$T> { |             fn $trait_fn(&self) -> Expr<$name> { | ||||||
|                 *self |                 *self | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         impl $Trait for $T { |  | ||||||
|             fn $fn(&self) -> Expr<$T> { |  | ||||||
|                 self.to_expr() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $(impl $Trait for $from_value_ty { |  | ||||||
|             fn $fn(&self) -> Expr<$T> { |  | ||||||
|                 self.to_expr().$fn() |  | ||||||
|             } |  | ||||||
|         })* |  | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| make_to_reset! { | reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true); | ||||||
|     #[from_value(SyncReset)] | reset_type!(SyncReset, ToSyncReset::to_sync_reset, true); | ||||||
|     #[from_value(AsyncReset)] | reset_type!( | ||||||
|     pub trait ToReset { |     Reset, | ||||||
|         fn to_reset(&self) -> Expr<Reset>; |     ToReset::to_reset, | ||||||
|  |     false // Reset is not castable from bits because we don't know if it's async or sync
 | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | impl ToSyncReset for bool { | ||||||
|  |     fn to_sync_reset(&self) -> Expr<SyncReset> { | ||||||
|  |         self.to_expr().to_sync_reset() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| make_to_reset! { | impl ToAsyncReset for bool { | ||||||
|     #[from_value(bool)] |     fn to_async_reset(&self) -> Expr<AsyncReset> { | ||||||
|     #[from_value(UInt<1>)] |         self.to_expr().to_async_reset() | ||||||
|     pub trait ToAsyncReset { |  | ||||||
|         fn to_async_reset(&self) -> Expr<AsyncReset>; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| make_to_reset! { |  | ||||||
|     #[from_value(bool)] |  | ||||||
|     #[from_value(UInt<1>)] |  | ||||||
|     pub trait ToSyncReset { |  | ||||||
|         fn to_sync_reset(&self) -> Expr<SyncReset>; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,10 +3,14 @@ | ||||||
| 
 | 
 | ||||||
| mod const_bool; | mod const_bool; | ||||||
| mod const_cmp; | mod const_cmp; | ||||||
|  | mod const_usize; | ||||||
| mod misc; | mod misc; | ||||||
|  | mod scoped_ref; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||||
|  | #[doc(inline)] | ||||||
|  | pub use const_usize::{ConstUsize, GenericConstUsize}; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use const_cmp::{ | pub use const_cmp::{ | ||||||
|  | @ -14,6 +18,9 @@ pub use const_cmp::{ | ||||||
|     const_usize_cmp, |     const_usize_cmp, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #[doc(inline)] | ||||||
|  | pub use scoped_ref::ScopedRef; | ||||||
|  | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use misc::{ | pub use misc::{ | ||||||
|     interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, |     interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, | ||||||
|  |  | ||||||
							
								
								
									
										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; | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								crates/fayalite/src/util/scoped_ref.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								crates/fayalite/src/util/scoped_ref.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | ||||||
|  | mod safety_boundary { | ||||||
|  |     use std::{cell::Cell, ptr::NonNull}; | ||||||
|  | 
 | ||||||
|  |     pub(super) struct Impl<T: ?Sized>(Cell<Option<NonNull<T>>>); | ||||||
|  | 
 | ||||||
|  |     impl<T: ?Sized> Impl<T> { | ||||||
|  |         #[inline] | ||||||
|  |         pub(super) const fn new() -> Self { | ||||||
|  |             Self(Cell::new(None)) | ||||||
|  |         } | ||||||
|  |         #[inline] | ||||||
|  |         pub(super) const fn opt_static_ref(v: Option<&'static T>) -> Self { | ||||||
|  |             Self(Cell::new(if let Some(v) = v { | ||||||
|  |                 // SAFETY: v is a valid reference for lifetime 'static
 | ||||||
|  |                 unsafe { Some(NonNull::new_unchecked(v as *const T as *mut T)) } | ||||||
|  |             } else { | ||||||
|  |                 None | ||||||
|  |             })) | ||||||
|  |         } | ||||||
|  |         /// set `self` to `value` for the duration of the `f()` call.
 | ||||||
|  |         #[inline] | ||||||
|  |         pub(super) fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R { | ||||||
|  |             struct ResetOnDrop<'a, T: ?Sized> { | ||||||
|  |                 this: &'a Impl<T>, | ||||||
|  |                 old: Option<NonNull<T>>, | ||||||
|  |             } | ||||||
|  |             impl<T: ?Sized> Drop for ResetOnDrop<'_, T> { | ||||||
|  |                 fn drop(&mut self) { | ||||||
|  |                     self.this.0.set(self.old); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // reset to old value before exiting this function ensuring `self`
 | ||||||
|  |             // is not set to `value` when its lifetime is expired
 | ||||||
|  |             let _reset_on_drop = ResetOnDrop { | ||||||
|  |                 this: self, | ||||||
|  |                 old: self.0.replace(value.map(NonNull::from)), | ||||||
|  |             }; | ||||||
|  |             f() | ||||||
|  |         } | ||||||
|  |         #[inline] | ||||||
|  |         pub(super) fn get_ptr(&self) -> Option<NonNull<T>> { | ||||||
|  |             self.0.get() | ||||||
|  |         } | ||||||
|  |         /// get the reference in `self` for the duration of the `f(...)` call.
 | ||||||
|  |         #[inline] | ||||||
|  |         pub(super) fn with_opt<F: for<'a> FnOnce(Option<&'a T>) -> R, R>(&self, f: F) -> R { | ||||||
|  |             // SAFETY:
 | ||||||
|  |             // `self.0` is only `Some` when inside some `set_opt` call or when set
 | ||||||
|  |             // to some `&'static T`, which ensures that the pointer is live and valid.
 | ||||||
|  |             //
 | ||||||
|  |             // the reference we give away has its lifetime scoped to this
 | ||||||
|  |             // function call which ensures that it won't escape
 | ||||||
|  |             unsafe { f(self.0.get().map(|v| &*v.as_ptr())) } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// holds a `Cell<Option<&'scoped T>>` where `'scoped` is erased. This is useful for holding references in TLS.
 | ||||||
|  | pub struct ScopedRef<T: ?Sized>(safety_boundary::Impl<T>); | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized> ScopedRef<T> { | ||||||
|  |     /// create a new empty [`ScopedRef`]
 | ||||||
|  |     #[inline] | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         Self(safety_boundary::Impl::new()) | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     pub const fn opt_static_ref(v: Option<&'static T>) -> Self { | ||||||
|  |         Self(safety_boundary::Impl::opt_static_ref(v)) | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     pub const fn static_ref(v: &'static T) -> Self { | ||||||
|  |         Self::opt_static_ref(Some(v)) | ||||||
|  |     } | ||||||
|  |     /// set `self` to `value` for the duration of the `f()` call.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R { | ||||||
|  |         self.0.set_opt(value, f) | ||||||
|  |     } | ||||||
|  |     /// set `self` to `value` for the duration of the `f()` call.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn set<F: FnOnce() -> R, R>(&self, value: &T, f: F) -> R { | ||||||
|  |         self.0.set_opt(Some(value), f) | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     pub fn is_some(&self) -> bool { | ||||||
|  |         self.0.get_ptr().is_some() | ||||||
|  |     } | ||||||
|  |     #[inline] | ||||||
|  |     pub fn is_none(&self) -> bool { | ||||||
|  |         self.0.get_ptr().is_none() | ||||||
|  |     } | ||||||
|  |     /// get the reference in `self` for the duration of the `f(...)` call. panics if no reference is set.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> R { | ||||||
|  |         self.0.with_opt( | ||||||
|  |             #[inline] | ||||||
|  |             |v| f(v.expect("called ScopedRef::with on an empty ScopedRef")), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     /// get the reference in `self` for the duration of the `f(...)` call.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn with_opt<F: FnOnce(Option<&T>) -> R, R>(&self, f: F) -> R { | ||||||
|  |         self.0.with_opt(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,15 +1,15 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{Expr, ExprTrait, Flow, ToExpr}, |     expr::Flow, | ||||||
|     intern::Interned, |     intern::Interned, | ||||||
|     module::{NameId, ScopedNameId}, |     module::{NameId, ScopedNameId}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{DynCanonicalType, DynType, Type}, |     ty::{CanonicalType, Type}, | ||||||
| }; | }; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq, PartialEq, Hash)] | #[derive(Copy, Clone, Eq, PartialEq, Hash)] | ||||||
| pub struct Wire<T: Type> { | pub struct Wire<T: Type> { | ||||||
|     name: ScopedNameId, |     name: ScopedNameId, | ||||||
|     source_location: SourceLocation, |     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> { | impl<T: Type> Wire<T> { | ||||||
|     pub fn canonical(&self) -> Wire<T::CanonicalType> { |     pub fn canonical(&self) -> Wire<CanonicalType> { | ||||||
|         let Self { |         let Self { | ||||||
|             name, |             name, | ||||||
|             source_location, |             source_location, | ||||||
|  | @ -50,29 +38,8 @@ impl<T: Type> Wire<T> { | ||||||
|             ty: ty.canonical(), |             ty: ty.canonical(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> { |     pub fn ty(&self) -> T { | ||||||
|         let Self { |         self.ty | ||||||
|             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 new_unchecked( |     pub fn new_unchecked( | ||||||
|         scoped_name: ScopedNameId, |         scoped_name: ScopedNameId, | ||||||
|  |  | ||||||
|  | @ -1,27 +1,18 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     annotations::{Annotation, CustomFirrtlAnnotation}, |  | ||||||
|     array::Array, |  | ||||||
|     assert_export_firrtl, |     assert_export_firrtl, | ||||||
|     clock::{Clock, ClockDomain}, |  | ||||||
|     expr::ToExpr, |  | ||||||
|     hdl_module, |  | ||||||
|     int::{DynUInt, DynUIntType, IntCmp, SInt, UInt}, |  | ||||||
|     intern::Intern, |     intern::Intern, | ||||||
|     memory::MemBuilder, |  | ||||||
|     module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind}, |     module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind}, | ||||||
|     reset::{SyncReset, ToReset}, |     prelude::*, | ||||||
|     source_location::SourceLocation, |  | ||||||
|     ty::{StaticValue, Value}, |  | ||||||
| }; | }; | ||||||
| use serde_json::json; | use serde_json::json; | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Hash, Eq, PartialEq, Debug, Clone)] | #[hdl(outline_generated)] | ||||||
| pub enum TestEnum { | pub enum TestEnum { | ||||||
|     A, |     A, | ||||||
|     B(UInt<8>), |     B(UInt<8>), | ||||||
|     C(Array<[UInt<1>; 3]>), |     C(Array<UInt<1>, 3>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
|  | @ -31,46 +22,47 @@ pub fn my_module(width: usize) { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let rst: SyncReset = m.input(); |     let rst: SyncReset = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let clock_domain: ClockDomain = m.wire(); |     let clock_domain: ClockDomain = wire(); | ||||||
|     m.connect(clock_domain.clk, clk); |     connect(clock_domain.clk, clk); | ||||||
|     m.connect(clock_domain.rst, rst.to_reset()); |     connect(clock_domain.rst, rst.to_reset()); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let i: UInt<8> = m.input(); |     let i: UInt<8> = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o: Array<[UInt<8>; 3]> = m.output(); |     let o: Array<UInt<8>, 3> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let i2: DynUInt = m.input(DynUIntType::new(width)); |     let i2: UInt = m.input(UInt[width]); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o2: DynUInt = m.output(DynUIntType::new(width)); |     let o2: UInt = m.output(UInt[width]); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o3: (UInt<32>, SInt<5>) = m.output(); |     let o3: (UInt<32>, SInt<5>) = m.output(); | ||||||
|     m.connect( |     connect( | ||||||
|         o3, |         o3, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         (5_hdl_u32, -3_hdl_i5), |         (5_hdl_u32, -3_hdl_i5), | ||||||
|     ); |     ); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let m2 = m.instance(module2()); |     let m2 = instance(module2()); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let r: UInt<8> = m.reg_builder().clock_domain(clock_domain).reset(8_hdl_u8); |     let r: UInt<8> = reg_builder().clock_domain(clock_domain).reset(8_hdl_u8); | ||||||
|     m.connect(m2.i, i); |     connect(m2.i, i); | ||||||
|     m.connect(r, m2.o); |     connect(r, m2.o); | ||||||
|     m.connect( |     connect( | ||||||
|         o, |         o, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         [r, r, b'\r'_hdl], |         [r, r, b'\r'_hdl], | ||||||
|     ); |     ); | ||||||
|     m.connect(o[1], 30_hdl_u8); |     connect(o[1], 30_hdl_u8); | ||||||
|     m.connect(o2, i2); |     connect(o2, i2); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o4: TestEnum = m.output(); |     let o4: TestEnum = m.output(); | ||||||
|     m.connect( |     connect( | ||||||
|         o4, |         o4, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         TestEnum::C(Array::from([false.into(), true.into(), false.into()])), |         TestEnum::C(Array::from([false.into(), true.into(), false.into()])), | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn module2() { | pub fn module2() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -78,11 +70,12 @@ pub fn module2() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o: UInt<8> = m.output(); |     let o: UInt<8> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let w: UInt<8> = m.wire(); |     let w: UInt<8> = wire(); | ||||||
|     m.connect(w, i); |     connect(w, i); | ||||||
|     m.connect(o, w); |     connect(o, w); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_mymodule() { | fn test_mymodule() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -139,19 +132,21 @@ circuit my_module: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_array_repeat<const N: usize>() { | pub fn check_array_repeat<const N: usize>() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let i: UInt<8> = m.input(); |     let i: UInt<8> = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o: Array<[UInt<8>; N]> = m.output(); |     let o: Array<[UInt<8>; N]> = m.output(); | ||||||
|     m.connect( |     connect( | ||||||
|         o, |         o, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         [i; N], |         [i; N], | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_array_repeat() { | fn test_array_repeat() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -192,6 +187,7 @@ circuit check_array_repeat_1: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | ||||||
| where | where | ||||||
|  | @ -206,14 +202,15 @@ where | ||||||
|     let bytes = v.to_string().as_bytes().to_expr(); |     let bytes = v.to_string().as_bytes().to_expr(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o2: Array<[UInt<8>]> = m.output(bytes.ty()); |     let o2: Array<[UInt<8>]> = m.output(bytes.ty()); | ||||||
|     m.connect( |     connect( | ||||||
|         o, |         o, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         [i; N], |         [i; N], | ||||||
|     ); |     ); | ||||||
|     m.connect(o2, bytes); |     connect(o2, bytes); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_skipped_generics() { | fn test_skipped_generics() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -252,21 +249,24 @@ circuit check_skipped_generics: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_partially_written() { | pub fn check_partially_written() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let i: (UInt<8>, UInt<8>) = m.input(); |     let i: (UInt<8>, UInt<8>) = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o: (UInt<8>, UInt<8>) = m.output(); |     let o: (UInt<8>, UInt<8>) = m.output(); | ||||||
|     m.connect(o.0, i.0); |     connect(o.0, i.0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| #[should_panic(expected = "check_partially_written::o.1 is not connected to")] | #[should_panic(expected = "check_partially_written::o.1 is not connected to")] | ||||||
| fn test_partially_written() { | fn test_partially_written() { | ||||||
|     check_partially_written(); |     check_partially_written(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_conditionally_written() { | pub fn check_conditionally_written() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -277,16 +277,18 @@ pub fn check_conditionally_written() { | ||||||
|     let o: UInt<8> = m.output(); |     let o: UInt<8> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if cond { |     if cond { | ||||||
|         m.connect(o, i); |         connect(o, i); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| #[should_panic(expected = "check_conditionally_written::o is not always connected to")] | #[should_panic(expected = "check_conditionally_written::o is not always connected to")] | ||||||
| fn test_conditionally_written() { | fn test_conditionally_written() { | ||||||
|     check_conditionally_written(); |     check_conditionally_written(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_written_inside_condition() { | pub fn check_written_inside_condition() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -296,11 +298,12 @@ pub fn check_written_inside_condition() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if cond { |     if cond { | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         let w: UInt<8> = m.wire(); |         let w: UInt<8> = wire(); | ||||||
|         m.connect(w, i); |         connect(w, i); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_written_inside_condition() { | fn test_written_inside_condition() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -321,6 +324,7 @@ circuit check_written_inside_condition: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_written_with_dynamic_index() { | pub fn check_written_with_dynamic_index() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -328,16 +332,18 @@ pub fn check_written_with_dynamic_index() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let idx: UInt<8> = m.input(); |     let idx: UInt<8> = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let w: Array<[UInt<8>; 2]> = m.wire(); |     let w: Array<[UInt<8>; 2]> = wire(); | ||||||
|     m.connect(w[idx], i); |     connect(w[idx], i); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| #[should_panic = "is not always connected to"] | #[should_panic = "is not always connected to"] | ||||||
| fn test_written_with_dynamic_index() { | fn test_written_with_dynamic_index() { | ||||||
|     check_written_with_dynamic_index(); |     check_written_with_dynamic_index(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_written_inside_both_if_else() { | pub fn check_written_inside_both_if_else() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -348,12 +354,13 @@ pub fn check_written_inside_both_if_else() { | ||||||
|     let o: UInt<8> = m.output(); |     let o: UInt<8> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if cond { |     if cond { | ||||||
|         m.connect(o, i); |         connect(o, i); | ||||||
|     } else { |     } else { | ||||||
|         m.connect(o, 3_hdl_u8); |         connect(o, 3_hdl_u8); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_written_inside_both_if_else() { | fn test_written_inside_both_if_else() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -376,20 +383,19 @@ circuit check_written_inside_both_if_else: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | #[hdl(outline_generated)] | ||||||
| #[hdl(static, outline_generated)] |  | ||||||
| pub struct TestStruct<T> { | pub struct TestStruct<T> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|     pub b: UInt<8>, |     pub b: UInt<8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | #[hdl(outline_generated)] | ||||||
| #[hdl(static, outline_generated)] | pub struct TestStruct2 { | ||||||
| pub struct TestStruct2(pub UInt<8>); |     pub v: UInt<8>, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)] | #[hdl(outline_generated)] | ||||||
| #[hdl(static, outline_generated)] | pub struct TestStruct3 {} | ||||||
| pub struct TestStruct3; |  | ||||||
| 
 | 
 | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_struct_literals() { | pub fn check_struct_literals() { | ||||||
|  | @ -401,17 +407,17 @@ pub fn check_struct_literals() { | ||||||
|     let o2: TestStruct2 = m.output(); |     let o2: TestStruct2 = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o3: TestStruct3 = m.output(); |     let o3: TestStruct3 = m.output(); | ||||||
|     m.connect( |     connect( | ||||||
|         o, |         o, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         TestStruct::<_> { a: 1234u16, b: i }, |         TestStruct { a: 1234u16, b: i }, | ||||||
|     ); |     ); | ||||||
|     m.connect( |     connect( | ||||||
|         o2, |         o2, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         TestStruct2(i), |         TestStruct2 { v: i }, | ||||||
|     ); |     ); | ||||||
|     m.connect( |     connect( | ||||||
|         o3, |         o3, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         TestStruct3 {}, |         TestStruct3 {}, | ||||||
|  | @ -429,7 +435,7 @@ fn test_struct_literals() { | ||||||
|         "/test/check_struct_literals.fir": r"FIRRTL version 3.2.0
 |         "/test/check_struct_literals.fir": r"FIRRTL version 3.2.0
 | ||||||
| circuit check_struct_literals: | circuit check_struct_literals: | ||||||
|     type Ty0 = {a: UInt<16>, b: UInt<8>} |     type Ty0 = {a: UInt<16>, b: UInt<8>} | ||||||
|     type Ty1 = {`0`: UInt<8>} |     type Ty1 = {v: UInt<8>} | ||||||
|     type Ty2 = {} |     type Ty2 = {} | ||||||
|     module check_struct_literals: @[module-XXXXXXXXXX.rs 1:1] |     module check_struct_literals: @[module-XXXXXXXXXX.rs 1:1] | ||||||
|         input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] |         input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] | ||||||
|  | @ -441,7 +447,7 @@ circuit check_struct_literals: | ||||||
|         connect _bundle_literal_expr.b, i |         connect _bundle_literal_expr.b, i | ||||||
|         connect o, _bundle_literal_expr @[module-XXXXXXXXXX.rs 6:1] |         connect o, _bundle_literal_expr @[module-XXXXXXXXXX.rs 6:1] | ||||||
|         wire _bundle_literal_expr_1: Ty1 |         wire _bundle_literal_expr_1: Ty1 | ||||||
|         connect _bundle_literal_expr_1.`0`, i |         connect _bundle_literal_expr_1.v, i | ||||||
|         connect o2, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 7:1] |         connect o2, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 7:1] | ||||||
|         wire _bundle_literal_expr_2: Ty2 |         wire _bundle_literal_expr_2: Ty2 | ||||||
|         invalidate _bundle_literal_expr_2 |         invalidate _bundle_literal_expr_2 | ||||||
|  | @ -450,6 +456,7 @@ circuit check_struct_literals: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_enum_literals() { | pub fn check_enum_literals() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -458,26 +465,26 @@ pub fn check_enum_literals() { | ||||||
|     let o: Option<UInt<8>> = m.output(); |     let o: Option<UInt<8>> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let o2: TestEnum = m.output(); |     let o2: TestEnum = m.output(); | ||||||
|     m.connect( |     connect( | ||||||
|         o, |         o, | ||||||
|         #[hdl] |         #[hdl] | ||||||
|         Some(i), |         Some(i), | ||||||
|     ); |     ); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if i.cmp_eq(0_hdl_u8) { |     if i.cmp_eq(0_hdl_u8) { | ||||||
|         m.connect( |         connect( | ||||||
|             o2, |             o2, | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             TestEnum::A, |             TestEnum::A, | ||||||
|         ); |         ); | ||||||
|     } else if i.cmp_gt(8_hdl_u8) { |     } else if i.cmp_gt(8_hdl_u8) { | ||||||
|         m.connect( |         connect( | ||||||
|             o2, |             o2, | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             TestEnum::B(i), |             TestEnum::B(i), | ||||||
|         ); |         ); | ||||||
|     } else { |     } else { | ||||||
|         m.connect( |         connect( | ||||||
|             o2, |             o2, | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             TestEnum::C( |             TestEnum::C( | ||||||
|  | @ -488,6 +495,7 @@ pub fn check_enum_literals() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_enum_literals() { | fn test_enum_literals() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -669,6 +677,7 @@ circuit check_enum_literals: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_struct_enum_match() { | pub fn check_struct_enum_match() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -679,37 +688,38 @@ pub fn check_struct_enum_match() { | ||||||
|     let o: Array<[UInt<8>; 5]> = m.output(); |     let o: Array<[UInt<8>; 5]> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     if let Some(v) = i1 { |     if let Some(v) = i1 { | ||||||
|         m.connect(o[0], v); |         connect(o[0], v); | ||||||
|     } else if let TestEnum::B(v) = i2 { |     } else if let TestEnum::B(v) = i2 { | ||||||
|         m.connect_any(o[0], v + 2_hdl_u8); |         connect_any(o[0], v + 2_hdl_u8); | ||||||
|     } else { |     } else { | ||||||
|         m.connect(o[0], 23_hdl_u8); |         connect(o[0], 23_hdl_u8); | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     match i1 { |     match i1 { | ||||||
|         Some(_) => m.connect(o[1], 1_hdl_u8), |         Some(_) => connect(o[1], 1_hdl_u8), | ||||||
|         None => m.connect(o[1], 0_hdl_u8), |         None => connect(o[1], 0_hdl_u8), | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     match i2 { |     match i2 { | ||||||
|         TestEnum::A => m.connect(o[2], 0_hdl_u8), |         TestEnum::A => connect(o[2], 0_hdl_u8), | ||||||
|         TestEnum::B(v) => m.connect(o[2], v), |         TestEnum::B(v) => connect(o[2], v), | ||||||
|         TestEnum::C(v) => m.connect_any(o[2], v[1]), |         TestEnum::C(v) => connect_any(o[2], v[1]), | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     match i2 { |     match i2 { | ||||||
|         TestEnum::A => m.connect(o[3], 0_hdl_u8), |         TestEnum::A => connect(o[3], 0_hdl_u8), | ||||||
|         TestEnum::B { 0: _ } => m.connect(o[3], 1_hdl_u8), |         TestEnum::B { 0: _ } => connect(o[3], 1_hdl_u8), | ||||||
|         TestEnum::C(..) => m.connect(o[3], 2_hdl_u8), |         TestEnum::C(..) => connect(o[3], 2_hdl_u8), | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     match i2 { |     match i2 { | ||||||
|         TestEnum::B { 0: _, .. } => m.connect(o[4], 1_hdl_u8), |         TestEnum::B { 0: _, .. } => connect(o[4], 1_hdl_u8), | ||||||
|         TestEnum::C(v, ..) => m.connect_any(o[4], v[2]), |         TestEnum::C(v, ..) => connect_any(o[4], v[2]), | ||||||
|         _ => m.connect(o[4], 0_hdl_u8), |         _ => connect(o[4], 0_hdl_u8), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_struct_enum_match() { | fn test_struct_enum_match() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -960,6 +970,7 @@ circuit check_struct_enum_match: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_extern_module() { | pub fn check_extern_module() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -982,12 +993,13 @@ pub fn check_extern_module() { | ||||||
|         m.parameter_raw_verilog("raw_param", "12'hxzx"); |         m.parameter_raw_verilog("raw_param", "12'hxzx"); | ||||||
|     } |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let submodule = m.instance(extern_module()); |     let submodule = instance(extern_module()); | ||||||
|     m.connect(submodule.i1, i1); |     connect(submodule.i1, i1); | ||||||
|     m.connect(submodule.i2, i2); |     connect(submodule.i2, i2); | ||||||
|     m.connect(o, submodule.o); |     connect(o, submodule.o); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_extern_module() { | fn test_extern_module() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1019,6 +1031,7 @@ circuit check_extern_module: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory() { | pub fn check_memory() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1032,20 +1045,21 @@ pub fn check_memory() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let clk: Clock = m.input(); |     let clk: Clock = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem: MemBuilder<[UInt<8>; 0x100]> = m.memory_array(); |     let mut mem: MemBuilder<[UInt<8>; 0x100]> = memory_array(); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, 1_hdl_u1); |     connect(write_port.mask, 1_hdl_u1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory() { | fn test_memory() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1085,6 +1099,7 @@ circuit check_memory: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_init() { | pub fn check_memory_init() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1099,18 +1114,18 @@ pub fn check_memory_init() { | ||||||
|     let wdata1: UInt<8> = m.input(); |     let wdata1: UInt<8> = m.input(); | ||||||
|     let mem_init1 = Vec::from_iter((0..0x100).map(|i| (i * i) as u8)); |     let mem_init1 = Vec::from_iter((0..0x100).map(|i| (i * i) as u8)); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem1 = m.memory_with_init(mem_init1); |     let mut mem1 = memory_with_init(mem_init1); | ||||||
|     let read_port1 = mem1.new_read_port(); |     let read_port1 = mem1.new_read_port(); | ||||||
|     m.connect_any(read_port1.addr, raddr1); |     connect_any(read_port1.addr, raddr1); | ||||||
|     m.connect(read_port1.en, 1_hdl_u1); |     connect(read_port1.en, 1_hdl_u1); | ||||||
|     m.connect(read_port1.clk, clk); |     connect(read_port1.clk, clk); | ||||||
|     m.connect(rdata1, read_port1.data); |     connect(rdata1, read_port1.data); | ||||||
|     let write_port1 = mem1.new_write_port(); |     let write_port1 = mem1.new_write_port(); | ||||||
|     m.connect_any(write_port1.addr, waddr1); |     connect_any(write_port1.addr, waddr1); | ||||||
|     m.connect(write_port1.en, 1_hdl_u1); |     connect(write_port1.en, 1_hdl_u1); | ||||||
|     m.connect(write_port1.clk, clk); |     connect(write_port1.clk, clk); | ||||||
|     m.connect(write_port1.data, wdata1); |     connect(write_port1.data, wdata1); | ||||||
|     m.connect(write_port1.mask, 1_hdl_u1); |     connect(write_port1.mask, 1_hdl_u1); | ||||||
| 
 | 
 | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let raddr2: UInt<4> = m.input(); |     let raddr2: UInt<4> = m.input(); | ||||||
|  | @ -1122,20 +1137,21 @@ pub fn check_memory_init() { | ||||||
|     let wdata2: UInt<31> = m.input(); |     let wdata2: UInt<31> = m.input(); | ||||||
|     let mem_init2 = Vec::from_iter((0..0x10).map(|i| UInt::new(i * i * i))); |     let mem_init2 = Vec::from_iter((0..0x10).map(|i| UInt::new(i * i * i))); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem2 = m.memory_with_init(mem_init2); |     let mut mem2 = memory_with_init(mem_init2); | ||||||
|     let read_port2 = mem2.new_read_port(); |     let read_port2 = mem2.new_read_port(); | ||||||
|     m.connect_any(read_port2.addr, raddr2); |     connect_any(read_port2.addr, raddr2); | ||||||
|     m.connect(read_port2.en, 1_hdl_u1); |     connect(read_port2.en, 1_hdl_u1); | ||||||
|     m.connect(read_port2.clk, clk); |     connect(read_port2.clk, clk); | ||||||
|     m.connect(rdata2, read_port2.data); |     connect(rdata2, read_port2.data); | ||||||
|     let write_port2 = mem2.new_write_port(); |     let write_port2 = mem2.new_write_port(); | ||||||
|     m.connect_any(write_port2.addr, waddr2); |     connect_any(write_port2.addr, waddr2); | ||||||
|     m.connect(write_port2.en, 1_hdl_u1); |     connect(write_port2.en, 1_hdl_u1); | ||||||
|     m.connect(write_port2.clk, clk); |     connect(write_port2.clk, clk); | ||||||
|     m.connect(write_port2.data, wdata2); |     connect(write_port2.data, wdata2); | ||||||
|     m.connect(write_port2.mask, 1_hdl_u1); |     connect(write_port2.mask, 1_hdl_u1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_init() { | fn test_memory_init() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1485,6 +1501,7 @@ a9 | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_array() { | pub fn check_memory_of_array() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1504,20 +1521,21 @@ pub fn check_memory_of_array() { | ||||||
|         [UInt::new(i), UInt::new(i * i), UInt::new(i * i * i)] |         [UInt::new(i), UInt::new(i * i), UInt::new(i * i * i)] | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_array() { | fn test_memory_of_array() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1582,6 +1600,7 @@ b8c40e | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_arrays() { | pub fn check_memory_of_arrays() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1608,20 +1627,21 @@ pub fn check_memory_of_arrays() { | ||||||
|         ] |         ] | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_arrays() { | fn test_memory_of_arrays() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1714,6 +1734,7 @@ circuit check_memory_of_arrays: %[[ | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_bundle_of_arrays() { | pub fn check_memory_of_bundle_of_arrays() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1744,20 +1765,21 @@ pub fn check_memory_of_bundle_of_arrays() { | ||||||
|         ) |         ) | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_bundle_of_arrays() { | fn test_memory_of_bundle_of_arrays() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -1894,6 +1916,7 @@ circuit check_memory_of_bundle_of_arrays: %[[ | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_array_of_bundle() { | pub fn check_memory_of_array_of_bundle() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -1920,20 +1943,21 @@ pub fn check_memory_of_array_of_bundle() { | ||||||
|         ] |         ] | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_array_of_bundle() { | fn test_memory_of_array_of_bundle() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -2215,6 +2239,7 @@ b8 | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_bundle() { | pub fn check_memory_of_bundle() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -2234,20 +2259,21 @@ pub fn check_memory_of_bundle() { | ||||||
|         (UInt::new(i ^ 3), SInt::new(i ^ i / 2)) |         (UInt::new(i ^ 3), SInt::new(i ^ i / 2)) | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_bundle() { | fn test_memory_of_bundle() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -2369,6 +2395,7 @@ circuit check_memory_of_bundle: %[[ | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_enum() { | pub fn check_memory_of_enum() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -2404,20 +2431,21 @@ pub fn check_memory_of_enum() { | ||||||
|         } |         } | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = m.memory_with_init(mem_init); |     let mut mem = memory_with_init(mem_init); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_enum() { | fn test_memory_of_enum() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -2851,6 +2879,7 @@ circuit check_memory_of_enum: %[[ | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_memory_of_array_of_enum() { | pub fn check_memory_of_array_of_enum() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|  | @ -2866,20 +2895,21 @@ pub fn check_memory_of_array_of_enum() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let clk: Clock = m.input(); |     let clk: Clock = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem: MemBuilder<[Array<[TestEnum; 2]>; 0x100]> = m.memory_array(); |     let mut mem: MemBuilder<[Array<[TestEnum; 2]>; 0x100]> = memory_array(); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_memory_of_array_of_enum() { | fn test_memory_of_array_of_enum() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  | @ -2999,6 +3029,7 @@ circuit check_memory_of_array_of_enum: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_annotations() { | pub fn check_annotations() { | ||||||
|     m.annotate_module(Annotation::CustomFirrtl(CustomFirrtlAnnotation { |     m.annotate_module(Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|  | @ -3011,10 +3042,10 @@ pub fn check_annotations() { | ||||||
|     })); |     })); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let raddr: UInt<8> = m.input(); |     let raddr: UInt<8> = m.input(); | ||||||
|     m.annotate(raddr, Annotation::DontTouch); |     annotate(raddr, Annotation::DontTouch); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let rdata: Array<[UInt<4>; 2]> = m.output(); |     let rdata: Array<[UInt<4>; 2]> = m.output(); | ||||||
|     m.annotate( |     annotate( | ||||||
|         rdata, |         rdata, | ||||||
|         Annotation::CustomFirrtl(CustomFirrtlAnnotation { |         Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|             class: "the.annotation.ExampleClass".intern(), |             class: "the.annotation.ExampleClass".intern(), | ||||||
|  | @ -3032,7 +3063,7 @@ pub fn check_annotations() { | ||||||
|     let wdata: Array<[UInt<4>; 2]> = m.input(); |     let wdata: Array<[UInt<4>; 2]> = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let wmask: Array<[UInt<1>; 2]> = m.input(); |     let wmask: Array<[UInt<1>; 2]> = m.input(); | ||||||
|     m.annotate( |     annotate( | ||||||
|         wmask[1], |         wmask[1], | ||||||
|         Annotation::CustomFirrtl(CustomFirrtlAnnotation { |         Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|             class: "some.annotation.Class".intern(), |             class: "some.annotation.Class".intern(), | ||||||
|  | @ -3046,7 +3077,7 @@ pub fn check_annotations() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let clk: Clock = m.input(); |     let clk: Clock = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem: MemBuilder<[Array<[UInt<4>; 2]>; 0x100]> = m.memory_array(); |     let mut mem: MemBuilder<[Array<[UInt<4>; 2]>; 0x100]> = memory_array(); | ||||||
|     mem.annotate(Annotation::CustomFirrtl(CustomFirrtlAnnotation { |     mem.annotate(Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|         class: "the.annotation.ExampleClass2".intern(), |         class: "the.annotation.ExampleClass2".intern(), | ||||||
|         additional_fields: json!({ |         additional_fields: json!({ | ||||||
|  | @ -3057,7 +3088,7 @@ pub fn check_annotations() { | ||||||
|         .unwrap(), |         .unwrap(), | ||||||
|     })); |     })); | ||||||
|     let read_port = mem.new_read_port(); |     let read_port = mem.new_read_port(); | ||||||
|     m.annotate( |     annotate( | ||||||
|         read_port, |         read_port, | ||||||
|         Annotation::CustomFirrtl(CustomFirrtlAnnotation { |         Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|             class: "the.annotation.ExampleClass3".intern(), |             class: "the.annotation.ExampleClass3".intern(), | ||||||
|  | @ -3068,12 +3099,12 @@ pub fn check_annotations() { | ||||||
|             .unwrap(), |             .unwrap(), | ||||||
|         }), |         }), | ||||||
|     ); |     ); | ||||||
|     m.connect_any(read_port.addr, raddr); |     connect_any(read_port.addr, raddr); | ||||||
|     m.connect(read_port.en, 1_hdl_u1); |     connect(read_port.en, 1_hdl_u1); | ||||||
|     m.connect(read_port.clk, clk); |     connect(read_port.clk, clk); | ||||||
|     m.connect(rdata, read_port.data); |     connect(rdata, read_port.data); | ||||||
|     let write_port = mem.new_write_port(); |     let write_port = mem.new_write_port(); | ||||||
|     m.annotate( |     annotate( | ||||||
|         write_port.data[0], |         write_port.data[0], | ||||||
|         Annotation::CustomFirrtl(CustomFirrtlAnnotation { |         Annotation::CustomFirrtl(CustomFirrtlAnnotation { | ||||||
|             class: "some.annotation.Class".intern(), |             class: "some.annotation.Class".intern(), | ||||||
|  | @ -3084,13 +3115,14 @@ pub fn check_annotations() { | ||||||
|             .unwrap(), |             .unwrap(), | ||||||
|         }), |         }), | ||||||
|     ); |     ); | ||||||
|     m.connect_any(write_port.addr, waddr); |     connect_any(write_port.addr, waddr); | ||||||
|     m.connect(write_port.en, 1_hdl_u1); |     connect(write_port.en, 1_hdl_u1); | ||||||
|     m.connect(write_port.clk, clk); |     connect(write_port.clk, clk); | ||||||
|     m.connect(write_port.data, wdata); |     connect(write_port.data, wdata); | ||||||
|     m.connect(write_port.mask, wmask); |     connect(write_port.mask, wmask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[cfg(todo)] | ||||||
| #[test] | #[test] | ||||||
| fn test_annotations() { | fn test_annotations() { | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |     let _n = SourceLocation::normalize_files_for_tests(); | ||||||
|  |  | ||||||
|  | @ -1,32 +1,26 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     int::UInt, |     array::ArrayType, | ||||||
|     ty::{StaticValue, Value}, |     hdl, | ||||||
|  |     int::{IntType, Size, UInt}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] |  | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated)] | ||||||
| pub struct S<T> { | pub struct S<T: IntType, Len: Size> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|     b: UInt<3>, |     b: UInt<3>, | ||||||
|  |     pub(crate) c: ArrayType<UInt<1>, Len>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] |  | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated)] | ||||||
| pub enum E<T> { | pub enum E<T> { | ||||||
|     A, |     A, | ||||||
|     B {}, |     B(UInt<3>), | ||||||
|     C(), |     C(T), | ||||||
|     D(UInt<3>), |  | ||||||
|     E { a: UInt<3> }, |  | ||||||
|     F(UInt<3>, UInt<3>), |  | ||||||
|     G(T), |  | ||||||
|     H(T, UInt<1>), |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] | #[hdl(outline_generated)] | ||||||
| #[hdl(outline_generated, static, where(T: StaticValue))] | pub struct S2<T = ()> { | ||||||
| pub struct S2<T> { |  | ||||||
|     pub v: E<T>, |     pub v: E<T>, | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue