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
						43da831d31
					
				
					 18 changed files with 3977 additions and 6907 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 | ||||||
|  |  | ||||||
							
								
								
									
										713
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										713
									
								
								crates/fayalite-proc-macros-impl/src/hdl_bundle.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,713 @@ | ||||||
|  | use crate::{ | ||||||
|  |     hdl_type_common::{common_derives, get_target, TypeOptions, 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<TypeOptions>, | ||||||
|  |     pub(crate) vis: Visibility, | ||||||
|  |     pub(crate) struct_token: Token![struct], | ||||||
|  |     pub(crate) ident: Ident, | ||||||
|  |     pub(crate) generics: Generics, | ||||||
|  |     pub(crate) fields: 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, | ||||||
|  |             generics, | ||||||
|  |             fields, | ||||||
|  |             semi_token, | ||||||
|  |         } = item; | ||||||
|  |         let mut errors = Errors::new(); | ||||||
|  |         let options = errors | ||||||
|  |             .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) | ||||||
|  |             .unwrap_or_default(); | ||||||
|  |         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)); | ||||||
|  |         } | ||||||
|  |         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 (field_index, field) in self.fields.named.iter().enumerate() { | ||||||
|  |             match get_field_state(field_index) { | ||||||
|  |                 BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue, | ||||||
|  |                 BuilderFieldState::Generic => {} | ||||||
|  |             } | ||||||
|  |             if !retval.params.empty_or_trailing() { | ||||||
|  |                 retval.params.push_punct(Token)); | ||||||
|  |             } | ||||||
|  |             retval.params.push_value(GenericParam::Type( | ||||||
|  |                 type_var_for_field_name(field.ident.as_ref().unwrap()).into(), | ||||||
|  |             )); | ||||||
|  |         } | ||||||
|  |         retval | ||||||
|  |     } | ||||||
|  |     fn builder_struct_ty( | ||||||
|  |         &self, | ||||||
|  |         mut get_field_state: impl FnMut(usize) -> BuilderFieldState, | ||||||
|  |     ) -> Type { | ||||||
|  |         let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() { | ||||||
|  |             parse_quote_spanned! {self.ident.span()=> | ||||||
|  |                 <> | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             let builder_type_generics = self.generics.split_for_impl().1; | ||||||
|  |             parse_quote! { #builder_type_generics } | ||||||
|  |         }; | ||||||
|  |         for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() { | ||||||
|  |             let ident = ident.as_ref().unwrap(); | ||||||
|  |             if !ty_arguments.args.empty_or_trailing() { | ||||||
|  |                 ty_arguments.args.push_punct(Token)); | ||||||
|  |             } | ||||||
|  |             ty_arguments | ||||||
|  |                 .args | ||||||
|  |                 .push_value(match get_field_state(field_index) { | ||||||
|  |                     BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=> | ||||||
|  |                         ::fayalite::bundle::Unfilled<#ty> | ||||||
|  |                     }, | ||||||
|  |                     BuilderFieldState::Generic => { | ||||||
|  |                         let type_var = type_var_for_field_name(ident); | ||||||
|  |                         parse_quote_spanned! {self.ident.span()=> | ||||||
|  |                             #type_var | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=> | ||||||
|  |                         ::fayalite::expr::Expr<#ty> | ||||||
|  |                     }, | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |         let ident = &self.ident; | ||||||
|  |         parse_quote_spanned! {ident.span()=> | ||||||
|  |             #ident #ty_arguments | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn type_var_for_field_name(ident: &Ident) -> Ident { | ||||||
|  |     format_ident!("__T_{}", ident) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn field_fn_for_field_name(ident: &Ident) -> Ident { | ||||||
|  |     format_ident!("field_{}", ident) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ToTokens for Builder { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         let Self { | ||||||
|  |             vis, | ||||||
|  |             struct_token, | ||||||
|  |             ident, | ||||||
|  |             target, | ||||||
|  |             generics, | ||||||
|  |             fields, | ||||||
|  |         } = self; | ||||||
|  |         let phantom_field_name = self.phantom_field_name(); | ||||||
|  |         let builder_struct = ItemStruct { | ||||||
|  |             attrs: vec![parse_quote_spanned! {ident.span()=> | ||||||
|  |                 #[allow(non_camel_case_types, dead_code)] | ||||||
|  |             }], | ||||||
|  |             vis: vis.clone(), | ||||||
|  |             struct_token: *struct_token, | ||||||
|  |             ident: ident.clone(), | ||||||
|  |             generics: self.builder_struct_generics(|_| BuilderFieldState::Generic), | ||||||
|  |             fields: Fields::Named(FieldsNamed { | ||||||
|  |                 brace_token: fields.brace_token, | ||||||
|  |                 named: Punctuated::from_iter( | ||||||
|  |                     [Pair::Punctuated( | ||||||
|  |                         self.phantom_field(), | ||||||
|  |                         Token), | ||||||
|  |                     )] | ||||||
|  |                     .into_iter() | ||||||
|  |                     .chain(fields.named.pairs().map_pair_value_ref(|field| { | ||||||
|  |                         let ident = field.ident.as_ref().unwrap(); | ||||||
|  |                         let type_var = type_var_for_field_name(ident); | ||||||
|  |                         Field { | ||||||
|  |                             vis: Visibility::Inherited, | ||||||
|  |                             ty: parse_quote_spanned! {ident.span()=> | ||||||
|  |                                 #type_var | ||||||
|  |                             }, | ||||||
|  |                             ..field.clone() | ||||||
|  |                         } | ||||||
|  |                     })), | ||||||
|  |                 ), | ||||||
|  |             }), | ||||||
|  |             semi_token: None, | ||||||
|  |         }; | ||||||
|  |         builder_struct.to_tokens(tokens); | ||||||
|  |         let field_idents = Vec::from_iter( | ||||||
|  |             self.fields | ||||||
|  |                 .named | ||||||
|  |                 .iter() | ||||||
|  |                 .map(|field| field.ident.as_ref().unwrap()), | ||||||
|  |         ); | ||||||
|  |         for ( | ||||||
|  |             field_index, | ||||||
|  |             Field { | ||||||
|  |                 vis, | ||||||
|  |                 ident: field_ident, | ||||||
|  |                 ty, | ||||||
|  |                 .. | ||||||
|  |             }, | ||||||
|  |         ) in self.fields.named.iter().enumerate() | ||||||
|  |         { | ||||||
|  |             let field_ident = field_ident.as_ref().unwrap(); | ||||||
|  |             let fn_ident = field_fn_for_field_name(field_ident); | ||||||
|  |             let fn_generics = self.builder_struct_generics(|i| { | ||||||
|  |                 if i == field_index { | ||||||
|  |                     BuilderFieldState::Unfilled | ||||||
|  |                 } else { | ||||||
|  |                     BuilderFieldState::Generic | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             let (impl_generics, _, where_clause) = fn_generics.split_for_impl(); | ||||||
|  |             let unfilled_ty = self.builder_struct_ty(|i| { | ||||||
|  |                 if i == field_index { | ||||||
|  |                     BuilderFieldState::Unfilled | ||||||
|  |                 } else { | ||||||
|  |                     BuilderFieldState::Generic | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             let filled_ty = self.builder_struct_ty(|i| { | ||||||
|  |                 if i == field_index { | ||||||
|  |                     BuilderFieldState::Filled | ||||||
|  |                 } else { | ||||||
|  |                     BuilderFieldState::Generic | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             let pat_fields = | ||||||
|  |                 Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| { | ||||||
|  |                     let field_ident = field.ident.as_ref().unwrap(); | ||||||
|  |                     if field_index == i { | ||||||
|  |                         quote_spanned! {self.ident.span()=> | ||||||
|  |                             #field_ident: _, | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         quote_spanned! {self.ident.span()=> | ||||||
|  |                             #field_ident, | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 })); | ||||||
|  |             quote_spanned! {self.ident.span()=> | ||||||
|  |                 #[automatically_derived] | ||||||
|  |                 #[allow(non_camel_case_types, dead_code)] | ||||||
|  |                 impl #impl_generics #unfilled_ty | ||||||
|  |                 #where_clause | ||||||
|  |                 { | ||||||
|  |                     #vis fn #fn_ident( | ||||||
|  |                         self, | ||||||
|  |                         #field_ident: ::fayalite::expr::Expr<#ty>, | ||||||
|  |                     ) -> #filled_ty { | ||||||
|  |                         let Self { | ||||||
|  |                             #phantom_field_name: _, | ||||||
|  |                             #(#pat_fields)* | ||||||
|  |                         } = self; | ||||||
|  |                         #ident { | ||||||
|  |                             #phantom_field_name: ::fayalite::__std::marker::PhantomData, | ||||||
|  |                             #(#field_idents,)* | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .to_tokens(tokens); | ||||||
|  |         } | ||||||
|  |         let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled); | ||||||
|  |         let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled); | ||||||
|  |         let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl(); | ||||||
|  |         quote_spanned! {self.ident.span()=> | ||||||
|  |             #[automatically_derived] | ||||||
|  |             #[allow(non_camel_case_types, dead_code)] | ||||||
|  |             impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty | ||||||
|  |             #unfilled_where_clause | ||||||
|  |             { | ||||||
|  |                 fn default() -> Self { | ||||||
|  |                     #ident { | ||||||
|  |                         #phantom_field_name: ::fayalite::__std::marker::PhantomData, | ||||||
|  |                         #(#field_idents: ::fayalite::__std::default::Default::default(),)* | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |         let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled); | ||||||
|  |         let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled); | ||||||
|  |         let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl(); | ||||||
|  |         let type_generics = self.generics.split_for_impl().1; | ||||||
|  |         quote_spanned! {self.ident.span()=> | ||||||
|  |             #[automatically_derived] | ||||||
|  |             #[allow(non_camel_case_types, dead_code)] | ||||||
|  |             impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty | ||||||
|  |             #filled_where_clause | ||||||
|  |             { | ||||||
|  |                 type Type = #target #type_generics; | ||||||
|  |                 fn to_expr( | ||||||
|  |                     &self, | ||||||
|  |                 ) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> { | ||||||
|  |                     let __ty = #target { | ||||||
|  |                         #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* | ||||||
|  |                     }; | ||||||
|  |                     let __field_values = [ | ||||||
|  |                         #(::fayalite::expr::Expr::canonical(self.#field_idents),)* | ||||||
|  |                     ]; | ||||||
|  |                     ::fayalite::expr::ToExpr::to_expr( | ||||||
|  |                         &::fayalite::expr::ops::BundleLiteral::new(__ty, &__field_values), | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedBundle { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         let Self { | ||||||
|  |             attrs, | ||||||
|  |             options, | ||||||
|  |             vis, | ||||||
|  |             struct_token, | ||||||
|  |             ident, | ||||||
|  |             generics, | ||||||
|  |             fields, | ||||||
|  |             field_flips, | ||||||
|  |             mask_type_ident, | ||||||
|  |             mask_type_match_variant_ident, | ||||||
|  |             match_variant_ident, | ||||||
|  |             builder_ident, | ||||||
|  |             mask_type_builder_ident, | ||||||
|  |         } = self; | ||||||
|  |         let TypeOptions { | ||||||
|  |             outline_generated: _, | ||||||
|  |             connect_inexact, | ||||||
|  |             target, | ||||||
|  |         } = &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.clone(), | ||||||
|  |             fields: Fields::Named(fields.clone()), | ||||||
|  |             semi_token: None, | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |         let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||||||
|  |         let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||||
|  |         let tokens = wrapped_in_const.inner(); | ||||||
|  |         let builder = Builder { | ||||||
|  |             vis: vis.clone(), | ||||||
|  |             struct_token: *struct_token, | ||||||
|  |             ident: builder_ident.clone(), | ||||||
|  |             target: target.clone(), | ||||||
|  |             generics: generics.clone(), | ||||||
|  |             fields: fields.clone(), | ||||||
|  |         }; | ||||||
|  |         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 = 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.clone(), | ||||||
|  |             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.clone(), | ||||||
|  |             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.clone(), | ||||||
|  |             fields: Fields::Named(mask_type_match_variant_fields), | ||||||
|  |             semi_token: None, | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |         let mut match_variant_fields = 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.clone(), | ||||||
|  |             fields: Fields::Named(match_variant_fields), | ||||||
|  |             semi_token: None, | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |         let match_variant_body_fields = | ||||||
|  |             Vec::from_iter(fields.named.iter().map(|Field { ident, .. }| { | ||||||
|  |                 let ident: &Ident = 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.iter().map(|Field { ident, .. }| { | ||||||
|  |                 let ident: &Ident = 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.iter().enumerate().zip(field_flips).map( | ||||||
|  |                 |((index, Field { ident, .. }), flip)| { | ||||||
|  |                     let ident: &Ident = 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.iter().zip(field_flips).map( | ||||||
|  |             |(Field { ident, .. }, flip)| { | ||||||
|  |                 let ident: &Ident = 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.len(); | ||||||
|  |         quote_spanned! {ident.span()=> | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 type MaskType = #mask_type_ident #type_generics; | ||||||
|  |                 type MatchVariant = #mask_type_match_variant_ident #type_generics; | ||||||
|  |                 type MatchActiveScope = (); | ||||||
|  |                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||||
|  |                     <Self as ::fayalite::ty::Type>::MatchVariant, | ||||||
|  |                 >; | ||||||
|  |                 type MatchVariantsIter = ::fayalite::__std::iter::Once< | ||||||
|  |                     <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||||
|  |                 >; | ||||||
|  |                 fn match_variants( | ||||||
|  |                     __this: ::fayalite::expr::Expr<Self>, | ||||||
|  |                     __module_builder: &mut ::fayalite::module::ModuleBuilder< | ||||||
|  |                         ::fayalite::module::NormalModule, | ||||||
|  |                     >, | ||||||
|  |                     __source_location: ::fayalite::source_location::SourceLocation, | ||||||
|  |                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||||
|  |                     let __retval = #mask_type_match_variant_ident { | ||||||
|  |                         #(#match_variant_body_fields)* | ||||||
|  |                     }; | ||||||
|  |                     ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) | ||||||
|  |                 } | ||||||
|  |                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||||
|  |                     *self | ||||||
|  |                 } | ||||||
|  |                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||||
|  |                     ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) | ||||||
|  |                 } | ||||||
|  |                 #[track_caller] | ||||||
|  |                 fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||||
|  |                     let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { | ||||||
|  |                         ::fayalite::__std::panic!("expected bundle"); | ||||||
|  |                     }; | ||||||
|  |                     let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); | ||||||
|  |                     ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); | ||||||
|  |                     Self { | ||||||
|  |                         #(#from_canonical_body_fields)* | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|  |                     ::fayalite::source_location::SourceLocation::caller() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 type Builder = #unfilled_mask_type_builder_ty; | ||||||
|  |                 type FilledBuilder = #filled_mask_type_builder_ty; | ||||||
|  |                 fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { | ||||||
|  |                     ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { | ||||||
|  |                     let __this = *__this; | ||||||
|  |                     let __retval = #mask_type_match_variant_ident { | ||||||
|  |                         #(#match_variant_body_fields)* | ||||||
|  |                     }; | ||||||
|  |                     ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 type MaskType = #mask_type_ident #type_generics; | ||||||
|  |                 type MatchVariant = #match_variant_ident #type_generics; | ||||||
|  |                 type MatchActiveScope = (); | ||||||
|  |                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||||
|  |                     <Self as ::fayalite::ty::Type>::MatchVariant, | ||||||
|  |                 >; | ||||||
|  |                 type MatchVariantsIter = ::fayalite::__std::iter::Once< | ||||||
|  |                     <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||||
|  |                 >; | ||||||
|  |                 fn match_variants( | ||||||
|  |                     __this: ::fayalite::expr::Expr<Self>, | ||||||
|  |                     __module_builder: &mut ::fayalite::module::ModuleBuilder< | ||||||
|  |                         ::fayalite::module::NormalModule, | ||||||
|  |                     >, | ||||||
|  |                     __source_location: ::fayalite::source_location::SourceLocation, | ||||||
|  |                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||||
|  |                     let __retval = #match_variant_ident { | ||||||
|  |                         #(#match_variant_body_fields)* | ||||||
|  |                     }; | ||||||
|  |                     ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) | ||||||
|  |                 } | ||||||
|  |                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||||
|  |                     #mask_type_ident { | ||||||
|  |                         #(#mask_type_body_fields)* | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||||
|  |                     ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) | ||||||
|  |                 } | ||||||
|  |                 #[track_caller] | ||||||
|  |                 fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||||
|  |                     let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { | ||||||
|  |                         ::fayalite::__std::panic!("expected bundle"); | ||||||
|  |                     }; | ||||||
|  |                     let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); | ||||||
|  |                     ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); | ||||||
|  |                     Self { | ||||||
|  |                         #(#from_canonical_body_fields)* | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|  |                     ::fayalite::source_location::SourceLocation::caller() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 type Builder = #unfilled_builder_ty; | ||||||
|  |                 type FilledBuilder = #filled_builder_ty; | ||||||
|  |                 fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { | ||||||
|  |                     ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 fn expr_deref(__this: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant { | ||||||
|  |                     let __this = *__this; | ||||||
|  |                     let __retval = #match_variant_ident { | ||||||
|  |                         #(#match_variant_body_fields)* | ||||||
|  |                     }; | ||||||
|  |                     ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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) | ||||||
|  | } | ||||||
							
								
								
									
										551
									
								
								crates/fayalite-proc-macros-impl/src/hdl_enum.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										551
									
								
								crates/fayalite-proc-macros-impl/src/hdl_enum.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,551 @@ | ||||||
|  | use crate::{ | ||||||
|  |     hdl_type_common::{common_derives, get_target, TypeOptions, 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 ParsedField { | ||||||
|  |     pub(crate) paren_token: Paren, | ||||||
|  |     pub(crate) attrs: Vec<Attribute>, | ||||||
|  |     pub(crate) options: HdlAttr<FieldOptions>, | ||||||
|  |     pub(crate) ty: 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<ParsedField>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ParsedVariant { | ||||||
|  |     fn parse(errors: &mut Errors, variant: Variant) -> 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) { | ||||||
|  |                     // FIXME: use mutability as the spanned tokens,
 | ||||||
|  |                     // blocked on https://github.com/dtolnay/syn/issues/1717
 | ||||||
|  |                     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(ParsedField { | ||||||
|  |                     paren_token, | ||||||
|  |                     attrs, | ||||||
|  |                     options, | ||||||
|  |                     ty, | ||||||
|  |                     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<TypeOptions>, | ||||||
|  |     pub(crate) vis: Visibility, | ||||||
|  |     pub(crate) enum_token: Token![enum], | ||||||
|  |     pub(crate) ident: Ident, | ||||||
|  |     pub(crate) generics: 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, | ||||||
|  |             generics, | ||||||
|  |             brace_token, | ||||||
|  |             variants, | ||||||
|  |         } = item; | ||||||
|  |         let mut errors = Errors::new(); | ||||||
|  |         let options = errors | ||||||
|  |             .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) | ||||||
|  |             .unwrap_or_default(); | ||||||
|  |         attrs.retain(|attr| { | ||||||
|  |             if attr.path().is_ident("repr") { | ||||||
|  |                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); | ||||||
|  |                 false | ||||||
|  |             } else { | ||||||
|  |                 true | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         let variants = Punctuated::from_iter( | ||||||
|  |             variants | ||||||
|  |                 .into_pairs() | ||||||
|  |                 .map_pair_value(|v| ParsedVariant::parse(&mut errors, v)), | ||||||
|  |         ); | ||||||
|  |         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 TypeOptions { | ||||||
|  |             outline_generated: _, | ||||||
|  |             connect_inexact, | ||||||
|  |             target, | ||||||
|  |         } = &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(ParsedField { | ||||||
|  |                     paren_token, | ||||||
|  |                     attrs: _, | ||||||
|  |                     options, | ||||||
|  |                     ty, | ||||||
|  |                     comma_token: _, | ||||||
|  |                 }) = field | ||||||
|  |                 { | ||||||
|  |                     let FieldOptions {} = options.body; | ||||||
|  |                     colon_token = Token); | ||||||
|  |                     ty.clone() | ||||||
|  |                 } 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.clone(), | ||||||
|  |             fields: if struct_fields.is_empty() { | ||||||
|  |                 Fields::Unit | ||||||
|  |             } else { | ||||||
|  |                 Fields::Named(FieldsNamed { | ||||||
|  |                     brace_token: *brace_token, | ||||||
|  |                     named: struct_fields, | ||||||
|  |                 }) | ||||||
|  |             }, | ||||||
|  |             semi_token: None, | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |         let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); | ||||||
|  |         let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||||
|  |         let tokens = wrapped_in_const.inner(); | ||||||
|  |         { | ||||||
|  |             let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); | ||||||
|  |             let tokens = wrapped_in_const.inner(); | ||||||
|  |             let mut enum_attrs = attrs.clone(); | ||||||
|  |             enum_attrs.push(parse_quote_spanned! {ident.span()=> | ||||||
|  |                 #[allow(dead_code)] | ||||||
|  |             }); | ||||||
|  |             ItemEnum { | ||||||
|  |                 attrs: enum_attrs, | ||||||
|  |                 vis: vis.clone(), | ||||||
|  |                 enum_token: *enum_token, | ||||||
|  |                 ident: ident.clone(), | ||||||
|  |                 generics: generics.clone(), | ||||||
|  |                 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(ParsedField { | ||||||
|  |                                 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(), | ||||||
|  |                                     }, | ||||||
|  |                                     *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.clone(), | ||||||
|  |             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(ParsedField { | ||||||
|  |                             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(ParsedField { ty, .. }) = field { | ||||||
|  |                 quote_spanned! {ident.span()=> | ||||||
|  |                     #[automatically_derived] | ||||||
|  |                     impl #impl_generics #target #type_generics | ||||||
|  |                     #where_clause | ||||||
|  |                     { | ||||||
|  |                         #[allow(non_snake_case, dead_code)] | ||||||
|  |                         #vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>( | ||||||
|  |                             self, | ||||||
|  |                             v: __V, | ||||||
|  |                         ) -> ::fayalite::expr::Expr<Self> { | ||||||
|  |                             ::fayalite::expr::ToExpr::to_expr( | ||||||
|  |                                 &::fayalite::expr::ops::EnumLiteral::new( | ||||||
|  |                                     self, | ||||||
|  |                                     #index, | ||||||
|  |                                     ::fayalite::__std::option::Option::Some( | ||||||
|  |                                         ::fayalite::expr::Expr::canonical( | ||||||
|  |                                             ::fayalite::expr::ToExpr::to_expr(&v), | ||||||
|  |                                         ), | ||||||
|  |                                     ), | ||||||
|  |                                 ), | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 quote_spanned! {ident.span()=> | ||||||
|  |                     #[automatically_derived] | ||||||
|  |                     impl #impl_generics #target #type_generics | ||||||
|  |                     #where_clause | ||||||
|  |                     { | ||||||
|  |                         #[allow(non_snake_case, dead_code)] | ||||||
|  |                         #vis fn #ident(self) -> ::fayalite::expr::Expr<Self> { | ||||||
|  |                             ::fayalite::expr::ToExpr::to_expr( | ||||||
|  |                                 &::fayalite::expr::ops::EnumLiteral::new( | ||||||
|  |                                     self, | ||||||
|  |                                     #index, | ||||||
|  |                                     ::fayalite::__std::option::Option::None, | ||||||
|  |                                 ), | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .to_tokens(tokens); | ||||||
|  |         } | ||||||
|  |         let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map( | ||||||
|  |             |(index, ParsedVariant { ident, field, .. })| { | ||||||
|  |                 let ident_str = ident.to_string(); | ||||||
|  |                 let var = format_ident!("_variant_{}", ident); | ||||||
|  |                 let val = if field.is_some() { | ||||||
|  |                     let missing_value_msg = format!("expected variant {ident} to have a field"); | ||||||
|  |                     quote_spanned! {ident.span()=> | ||||||
|  |                         ::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg)) | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     quote_spanned! {ident.span()=> | ||||||
|  |                         ::fayalite::__std::assert!(ty.is_none()); | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  |                 quote_spanned! {ident.span()=> | ||||||
|  |                     #ident: { | ||||||
|  |                         let ::fayalite::enum_::EnumVariant { | ||||||
|  |                             name, | ||||||
|  |                             ty, | ||||||
|  |                         } = variants[#index]; | ||||||
|  |                         ::fayalite::__std::assert_eq!(&*name, #ident_str); | ||||||
|  |                         #val | ||||||
|  |                     }, | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         )); | ||||||
|  |         let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map( | ||||||
|  |             |(index, ParsedVariant { ident, field, .. })| { | ||||||
|  |                 if field.is_some() { | ||||||
|  |                     quote_spanned! {ident.span()=> | ||||||
|  |                         #index => #match_variant_ident::#ident( | ||||||
|  |                             ::fayalite::expr::ToExpr::to_expr( | ||||||
|  |                                 &::fayalite::expr::ops::VariantAccess::new_unchecked( | ||||||
|  |                                     variant_access.base(), | ||||||
|  |                                     variant_access.variant_index(), | ||||||
|  |                                 ), | ||||||
|  |                             ), | ||||||
|  |                         ), | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     quote_spanned! {ident.span()=> | ||||||
|  |                         #index => #match_variant_ident::#ident, | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         )); | ||||||
|  |         let variants_body_variants = Vec::from_iter(variants.iter().map( | ||||||
|  |             |ParsedVariant { | ||||||
|  |                  attrs: _, | ||||||
|  |                  options, | ||||||
|  |                  ident, | ||||||
|  |                  field, | ||||||
|  |              }| { | ||||||
|  |                 let VariantOptions {} = options.body; | ||||||
|  |                 let ident_str = ident.to_string(); | ||||||
|  |                 match field { | ||||||
|  |                     Some(ParsedField { options, .. }) => { | ||||||
|  |                         let FieldOptions {} = options.body; | ||||||
|  |                         quote_spanned! {ident.span()=> | ||||||
|  |                             ::fayalite::enum_::EnumVariant { | ||||||
|  |                                 name: ::fayalite::intern::Intern::intern(#ident_str), | ||||||
|  |                                 ty: ::fayalite::__std::option::Option::Some( | ||||||
|  |                                     ::fayalite::ty::Type::canonical(&self.#ident), | ||||||
|  |                                 ), | ||||||
|  |                             }, | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     None => quote_spanned! {ident.span()=> | ||||||
|  |                         ::fayalite::enum_::EnumVariant { | ||||||
|  |                             name: ::fayalite::intern::Intern::intern(#ident_str), | ||||||
|  |                             ty: ::fayalite::__std::option::Option::None, | ||||||
|  |                         }, | ||||||
|  |                     }, | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         )); | ||||||
|  |         let variants_len = variants.len(); | ||||||
|  |         quote_spanned! {ident.span()=> | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 type MaskType = ::fayalite::int::Bool; | ||||||
|  |                 type MatchVariant = #match_variant_ident #type_generics; | ||||||
|  |                 type MatchActiveScope = ::fayalite::module::Scope; | ||||||
|  |                 type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; | ||||||
|  |                 type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>; | ||||||
|  | 
 | ||||||
|  |                 fn match_variants( | ||||||
|  |                     this: ::fayalite::expr::Expr<Self>, | ||||||
|  |                     module_builder: &mut ::fayalite::module::ModuleBuilder<::fayalite::module::NormalModule>, | ||||||
|  |                     source_location: ::fayalite::source_location::SourceLocation, | ||||||
|  |                 ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter { | ||||||
|  |                     module_builder.enum_match_variants_helper(this, source_location) | ||||||
|  |                 } | ||||||
|  |                 fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType { | ||||||
|  |                     ::fayalite::int::Bool | ||||||
|  |                 } | ||||||
|  |                 fn canonical(&self) -> ::fayalite::ty::CanonicalType { | ||||||
|  |                     ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self))) | ||||||
|  |                 } | ||||||
|  |                 #[track_caller] | ||||||
|  |                 #[allow(non_snake_case)] | ||||||
|  |                 fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self { | ||||||
|  |                     let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else { | ||||||
|  |                         ::fayalite::__std::panic!("expected enum"); | ||||||
|  |                     }; | ||||||
|  |                     let variants = ::fayalite::enum_::EnumType::variants(&enum_); | ||||||
|  |                     ::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants"); | ||||||
|  |                     Self { | ||||||
|  |                         #(#from_canonical_body_fields)* | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|  |                     ::fayalite::source_location::SourceLocation::caller() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             #[automatically_derived] | ||||||
|  |             impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics | ||||||
|  |             #where_clause | ||||||
|  |             { | ||||||
|  |                 fn match_activate_scope( | ||||||
|  |                     v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||||
|  |                 ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) { | ||||||
|  |                     let (variant_access, scope) = v.activate(); | ||||||
|  |                     ( | ||||||
|  |                         match variant_access.variant_index() { | ||||||
|  |                             #(#match_active_scope_match_arms)* | ||||||
|  |                             #variants_len.. => ::fayalite::__std::panic!("invalid variant index"), | ||||||
|  |                         }, | ||||||
|  |                         scope, | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |                 fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> { | ||||||
|  |                     ::fayalite::intern::Intern::intern(&[ | ||||||
|  |                         #(#variants_body_variants)* | ||||||
|  |                     ][..]) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .to_tokens(tokens); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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) | ||||||
|  | } | ||||||
							
								
								
									
										415
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								crates/fayalite-proc-macros-impl/src/hdl_type_common.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,415 @@ | ||||||
|  | use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt}; | ||||||
|  | use proc_macro2::{Span, TokenStream}; | ||||||
|  | use quote::{quote, quote_spanned, ToTokens}; | ||||||
|  | use std::fmt; | ||||||
|  | use syn::{ | ||||||
|  |     parse::{Parse, ParseStream}, | ||||||
|  |     parse_quote, parse_quote_spanned, | ||||||
|  |     punctuated::Punctuated, | ||||||
|  |     token::Paren, | ||||||
|  |     Attribute, ConstParam, GenericParam, Generics, Ident, ImplGenerics, LifetimeParam, Path, | ||||||
|  |     PredicateType, Token, Turbofish, Type, TypeGenerics, TypeParam, WhereClause, WherePredicate, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | crate::options! { | ||||||
|  |     #[options = TypeOptions] | ||||||
|  |     pub(crate) enum TypeOption { | ||||||
|  |         OutlineGenerated(outline_generated), | ||||||
|  |         ConnectInexact(connect_inexact), | ||||||
|  |         Target(target, Path), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) struct WrappedInConst<'a> { | ||||||
|  |     outer: &'a mut TokenStream, | ||||||
|  |     span: Span, | ||||||
|  |     inner: TokenStream, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> WrappedInConst<'a> { | ||||||
|  |     pub(crate) fn new(outer: &'a mut TokenStream, span: Span) -> Self { | ||||||
|  |         Self { | ||||||
|  |             outer, | ||||||
|  |             span, | ||||||
|  |             inner: TokenStream::new(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub(crate) fn inner(&mut self) -> &mut TokenStream { | ||||||
|  |         &mut self.inner | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for WrappedInConst<'_> { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         let inner = &self.inner; | ||||||
|  |         quote_spanned! {self.span=> | ||||||
|  |             const _: () = { | ||||||
|  |                 #inner | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         .to_tokens(self.outer); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path { | ||||||
|  |     match target { | ||||||
|  |         Some((_, _, target)) => target.clone(), | ||||||
|  |         None => item_ident.clone().into(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn common_derives(span: Span) -> Attribute { | ||||||
|  |     parse_quote_spanned! {span=> | ||||||
|  |         #[::fayalite::__std::prelude::v1::derive(
 | ||||||
|  |             ::fayalite::__std::fmt::Debug, | ||||||
|  |             ::fayalite::__std::cmp::Eq, | ||||||
|  |             ::fayalite::__std::cmp::PartialEq, | ||||||
|  |             ::fayalite::__std::hash::Hash, | ||||||
|  |             ::fayalite::__std::marker::Copy, | ||||||
|  |             ::fayalite::__std::clone::Clone, | ||||||
|  |         )] | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | crate::options! { | ||||||
|  |     #[options = LifetimeParamOptions] | ||||||
|  |     enum LifetimeParamOption {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | crate::options! { | ||||||
|  |     #[options = TypeParamOptions] | ||||||
|  |     pub(crate) enum TypeParamOption {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | crate::options! { | ||||||
|  |     #[options = ConstParamOptions] | ||||||
|  |     pub(crate) enum ConstParamOption {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) enum ParsedType {} | ||||||
|  | 
 | ||||||
|  | impl ParsedType { | ||||||
|  |     pub(crate) fn parse(ty: &mut Type) -> syn::Result<Self> { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedTypeParam { | ||||||
|  |     pub(crate) attrs: Vec<Attribute>, | ||||||
|  |     pub(crate) options: HdlAttr<TypeParamOptions>, | ||||||
|  |     pub(crate) ident: Ident, | ||||||
|  |     pub(crate) default: Option<(Token![=], ParsedType)>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) enum ParsedGenericParam { | ||||||
|  |     Type(ParsedTypeParam), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedGenerics { | ||||||
|  |     pub(crate) lt_token: Option<Token![<]>, | ||||||
|  |     pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>, | ||||||
|  |     pub(crate) gt_token: Option<Token![>]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ParsedGenerics { | ||||||
|  |     pub(crate) fn parse(generics: &mut Generics) -> syn::Result<Self> { | ||||||
|  |         let Generics { | ||||||
|  |             lt_token, | ||||||
|  |             params, | ||||||
|  |             gt_token, | ||||||
|  |             where_clause, | ||||||
|  |         } = generics; | ||||||
|  |         let mut errors = Errors::new(); | ||||||
|  |         let mut predicates: Vec<WherePredicate> = Vec::with_capacity(params.len()); | ||||||
|  |         let params = Punctuated::from_iter(params.pairs_mut().filter_map_pair_value_mut(|param| { | ||||||
|  |             Some(match param { | ||||||
|  |                 GenericParam::Lifetime(param) => { | ||||||
|  |                     errors.unwrap_or_default(HdlAttr::<LifetimeParamOptions>::parse_and_take_attr( | ||||||
|  |                         &mut param.attrs, | ||||||
|  |                     )); | ||||||
|  |                     errors.error(param, "lifetime generics are not supported by #[hdl]"); | ||||||
|  |                     return None; | ||||||
|  |                 } | ||||||
|  |                 GenericParam::Type(TypeParam { | ||||||
|  |                     attrs, | ||||||
|  |                     ident, | ||||||
|  |                     colon_token, | ||||||
|  |                     bounds, | ||||||
|  |                     eq_token, | ||||||
|  |                     default, | ||||||
|  |                 }) => { | ||||||
|  |                     let span = ident.span(); | ||||||
|  |                     let options = errors | ||||||
|  |                         .unwrap_or_default(HdlAttr::<TypeParamOptions>::parse_and_take_attr(attrs)) | ||||||
|  |                         .unwrap_or_default(); | ||||||
|  |                     if !bounds.is_empty() { | ||||||
|  |                         predicates.push(WherePredicate::Type(PredicateType { | ||||||
|  |                             lifetimes: None, | ||||||
|  |                             bounded_ty: parse_quote! { #ident }, | ||||||
|  |                             colon_token: colon_token.unwrap_or_else(|| Token), | ||||||
|  |                             bounds: bounds.clone(), | ||||||
|  |                         })); | ||||||
|  |                     } | ||||||
|  |                     let default = default | ||||||
|  |                         .as_mut() | ||||||
|  |                         .and_then(|v| errors.ok(ParsedType::parse(v))); | ||||||
|  |                     ParsedGenericParam::Type(ParsedTypeParam { | ||||||
|  |                         attrs: attrs.clone(), | ||||||
|  |                         options, | ||||||
|  |                         ident: ident.clone(), | ||||||
|  |                         default: default.map(|v| (eq_token.unwrap_or_else(|| Token), v)), | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |                 GenericParam::Const(ConstParam { | ||||||
|  |                     attrs, | ||||||
|  |                     const_token, | ||||||
|  |                     ident, | ||||||
|  |                     colon_token, | ||||||
|  |                     ty, | ||||||
|  |                     eq_token, | ||||||
|  |                     default, | ||||||
|  |                 }) => { | ||||||
|  |                     let options = errors | ||||||
|  |                         .unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr(attrs)) | ||||||
|  |                         .unwrap_or_default(); | ||||||
|  |                     todo!() | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         })); | ||||||
|  |         if let Some(where_clause) = where_clause { | ||||||
|  |             for predicate in &mut where_clause.predicates { | ||||||
|  |                 let WherePredicate::Type(PredicateType { | ||||||
|  |                     lifetimes, | ||||||
|  |                     bounded_ty, | ||||||
|  |                     colon_token, | ||||||
|  |                     bounds, | ||||||
|  |                 }) = predicate | ||||||
|  |                 else { | ||||||
|  |                     errors.error(predicate, "unsupported where predicate kind"); | ||||||
|  |                     continue; | ||||||
|  |                 }; | ||||||
|  |                 todo!(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         errors.finish()?; | ||||||
|  |         Ok(Self { | ||||||
|  |             lt_token: *lt_token, | ||||||
|  |             params, | ||||||
|  |             gt_token: *gt_token, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedGenerics { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedGenericsImplGenerics<'a>(&'a ParsedGenerics); | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedGenericsImplGenerics<'_> { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedGenericsTurbofish<'a>(&'a ParsedGenerics); | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedGenericsTurbofish<'_> { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedGenericsTypeGenerics<'a>(&'a ParsedGenerics); | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedGenericsTypeGenerics<'_> { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) struct ParsedGenericsWhereClause<'a>(&'a ParsedGenerics); | ||||||
|  | 
 | ||||||
|  | impl ToTokens for ParsedGenericsWhereClause<'_> { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait AsTurbofish { | ||||||
|  |     type Turbofish<'a>: 'a + ToTokens + Clone + fmt::Debug | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     fn as_turbofish(&self) -> Self::Turbofish<'_>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl AsTurbofish for TypeGenerics<'_> { | ||||||
|  |     type Turbofish<'a> = Turbofish<'a> where Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn as_turbofish(&self) -> Self::Turbofish<'_> { | ||||||
|  |         TypeGenerics::as_turbofish(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl AsTurbofish for ParsedGenericsTypeGenerics<'_> { | ||||||
|  |     type Turbofish<'a> = ParsedGenericsTurbofish<'a> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn as_turbofish(&self) -> Self::Turbofish<'_> { | ||||||
|  |         ParsedGenericsTurbofish(self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait SplitForImpl { | ||||||
|  |     type ImplGenerics<'a>: 'a + ToTokens + Clone + fmt::Debug | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     type TypeGenerics<'a>: 'a + AsTurbofish + ToTokens + Clone + fmt::Debug | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     type WhereClause<'a>: 'a + ToTokens + Clone + fmt::Debug | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     fn split_for_impl( | ||||||
|  |         &self, | ||||||
|  |     ) -> ( | ||||||
|  |         Self::ImplGenerics<'_>, | ||||||
|  |         Self::TypeGenerics<'_>, | ||||||
|  |         Self::WhereClause<'_>, | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl SplitForImpl for Generics { | ||||||
|  |     type ImplGenerics<'a> = ImplGenerics<'a>; | ||||||
|  |     type TypeGenerics<'a> = TypeGenerics<'a>; | ||||||
|  |     type WhereClause<'a> = Option<&'a WhereClause>; | ||||||
|  |     fn split_for_impl( | ||||||
|  |         &self, | ||||||
|  |     ) -> ( | ||||||
|  |         Self::ImplGenerics<'_>, | ||||||
|  |         Self::TypeGenerics<'_>, | ||||||
|  |         Self::WhereClause<'_>, | ||||||
|  |     ) { | ||||||
|  |         Generics::split_for_impl(&self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl SplitForImpl for ParsedGenerics { | ||||||
|  |     type ImplGenerics<'a> = ParsedGenericsImplGenerics<'a> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     type TypeGenerics<'a> = ParsedGenericsTypeGenerics<'a> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     type WhereClause<'a> = ParsedGenericsWhereClause<'a> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn split_for_impl( | ||||||
|  |         &self, | ||||||
|  |     ) -> ( | ||||||
|  |         Self::ImplGenerics<'_>, | ||||||
|  |         Self::TypeGenerics<'_>, | ||||||
|  |         Self::WhereClause<'_>, | ||||||
|  |     ) { | ||||||
|  |         ( | ||||||
|  |             ParsedGenericsImplGenerics(self), | ||||||
|  |             ParsedGenericsTypeGenerics(self), | ||||||
|  |             ParsedGenericsWhereClause(self), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub(crate) enum MaybeParsed<P, U> { | ||||||
|  |     Unrecognized(U), | ||||||
|  |     Parsed(P), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<P: ToTokens, U: ToTokens> ToTokens for MaybeParsed<P, U> { | ||||||
|  |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|  |         match self { | ||||||
|  |             MaybeParsed::Unrecognized(v) => v.to_tokens(tokens), | ||||||
|  |             MaybeParsed::Parsed(v) => v.to_tokens(tokens), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn to_token_stream(&self) -> TokenStream { | ||||||
|  |         match self { | ||||||
|  |             MaybeParsed::Unrecognized(v) => v.to_token_stream(), | ||||||
|  |             MaybeParsed::Parsed(v) => v.to_token_stream(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn into_token_stream(self) -> TokenStream { | ||||||
|  |         match self { | ||||||
|  |             MaybeParsed::Unrecognized(v) => v.into_token_stream(), | ||||||
|  |             MaybeParsed::Parsed(v) => v.into_token_stream(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<P: AsTurbofish, U: AsTurbofish> AsTurbofish for MaybeParsed<P, U> { | ||||||
|  |     type Turbofish<'a> = MaybeParsed<P::Turbofish<'a>, U::Turbofish<'a>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn as_turbofish(&self) -> Self::Turbofish<'_> { | ||||||
|  |         match self { | ||||||
|  |             MaybeParsed::Unrecognized(v) => MaybeParsed::Unrecognized(v.as_turbofish()), | ||||||
|  |             MaybeParsed::Parsed(v) => MaybeParsed::Parsed(v.as_turbofish()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<P: SplitForImpl, U: SplitForImpl> SplitForImpl for MaybeParsed<P, U> { | ||||||
|  |     type ImplGenerics<'a> = MaybeParsed<P::ImplGenerics<'a>, U::ImplGenerics<'a>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     type TypeGenerics<'a> = MaybeParsed<P::TypeGenerics<'a>, U::TypeGenerics<'a>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  |     type WhereClause<'a> = MaybeParsed<P::WhereClause<'a>, U::WhereClause<'a>> | ||||||
|  |     where | ||||||
|  |         Self: 'a; | ||||||
|  | 
 | ||||||
|  |     fn split_for_impl( | ||||||
|  |         &self, | ||||||
|  |     ) -> ( | ||||||
|  |         Self::ImplGenerics<'_>, | ||||||
|  |         Self::TypeGenerics<'_>, | ||||||
|  |         Self::WhereClause<'_>, | ||||||
|  |     ) { | ||||||
|  |         match self { | ||||||
|  |             MaybeParsed::Unrecognized(v) => { | ||||||
|  |                 let (i, t, w) = v.split_for_impl(); | ||||||
|  |                 ( | ||||||
|  |                     MaybeParsed::Unrecognized(i), | ||||||
|  |                     MaybeParsed::Unrecognized(t), | ||||||
|  |                     MaybeParsed::Unrecognized(w), | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             MaybeParsed::Parsed(v) => { | ||||||
|  |                 let (i, t, w) = v.split_for_impl(); | ||||||
|  |                 ( | ||||||
|  |                     MaybeParsed::Parsed(i), | ||||||
|  |                     MaybeParsed::Parsed(t), | ||||||
|  |                     MaybeParsed::Parsed(w), | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -13,6 +13,9 @@ use syn::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| mod fold; | mod fold; | ||||||
|  | mod hdl_bundle; | ||||||
|  | mod hdl_enum; | ||||||
|  | mod hdl_type_common; | ||||||
| mod module; | mod module; | ||||||
| mod value_derive_common; | mod value_derive_common; | ||||||
| mod value_derive_enum; | mod value_derive_enum; | ||||||
|  | @ -728,3 +731,15 @@ pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> { | ||||||
|         )), |         )), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||||
|  |     let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?; | ||||||
|  |     match item { | ||||||
|  |         Item::Enum(item) => hdl_enum::hdl_enum(item), | ||||||
|  |         Item::Struct(item) => hdl_bundle::hdl_bundle(item), | ||||||
|  |         _ => Err(syn::Error::new( | ||||||
|  |             Span::call_site(), | ||||||
|  |             "top-level #[hdl] can only be used on structs or enums", | ||||||
|  |         )), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1290,7 +1290,10 @@ impl Visitor { | ||||||
|                 memory, |                 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, | ||||||
|  |  | ||||||
|  | @ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { | ||||||
|         Err(err) => err.into_compile_error().into(), |         Err(err) => err.into_compile_error().into(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // intentionally not documented here, see `fayalite::hdl` for docs
 | ||||||
|  | #[proc_macro_attribute] | ||||||
|  | pub fn hdl( | ||||||
|  |     attr: proc_macro::TokenStream, | ||||||
|  |     item: proc_macro::TokenStream, | ||||||
|  | ) -> proc_macro::TokenStream { | ||||||
|  |     match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) { | ||||||
|  |         Ok(retval) => retval.into(), | ||||||
|  |         Err(err) => err.into_compile_error().into(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,671 +1,185 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     bundle::{BundleType, BundleValue}, |     expr::{ops::ArrayIndex, Expr, ToExpr}, | ||||||
|     expr::{ |     int::{DynSize, KnownSize, Size}, | ||||||
|         ops::{ArrayIndex, ArrayLiteral, ExprIndex}, |     intern::{Intern, Interned}, | ||||||
|         Expr, ToExpr, |     module::{ModuleBuilder, NormalModule}, | ||||||
|     }, |  | ||||||
|     intern::{Intern, Interned, InternedCompare, Memoize}, |  | ||||||
|     module::{ |  | ||||||
|         transform::visit::{Fold, Folder, Visit, Visitor}, |  | ||||||
|         ModuleBuilder, NormalModule, |  | ||||||
|     }, |  | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{ | ||||||
|         CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, |         CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, | ||||||
|         DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType, |  | ||||||
|         StaticValue, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |     }, | ||||||
|     util::{ConstBool, GenericConstBool, MakeMutSlice}, |     util::ConstUsize, | ||||||
| }; |  | ||||||
| use bitvec::{slice::BitSlice, vec::BitVec}; |  | ||||||
| use std::{ |  | ||||||
|     any::Any, |  | ||||||
|     borrow::{Borrow, BorrowMut}, |  | ||||||
|     fmt, |  | ||||||
|     hash::Hash, |  | ||||||
|     marker::PhantomData, |  | ||||||
|     ops::IndexMut, |  | ||||||
|     sync::Arc, |  | ||||||
| }; | }; | ||||||
|  | use std::ops::Index; | ||||||
| 
 | 
 | ||||||
| mod sealed { | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     pub trait Sealed {} | struct ArrayImpl<T: Type, Len: Size> { | ||||||
|  |     element: T, | ||||||
|  |     len: Len::SizeType, | ||||||
|  |     type_properties: TypeProperties, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait ValueArrayOrSlice: | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
|     sealed::Sealed | pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | ||||||
|     + BorrowMut<[<Self as ValueArrayOrSlice>::Element]> |     impl_: Interned<ArrayImpl<T, Len>>, | ||||||
|     + AsRef<[<Self as ValueArrayOrSlice>::Element]> |  | ||||||
|     + AsMut<[<Self as ValueArrayOrSlice>::Element]> |  | ||||||
|     + Hash |  | ||||||
|     + fmt::Debug |  | ||||||
|     + Eq |  | ||||||
|     + Send |  | ||||||
|     + Sync |  | ||||||
|     + 'static |  | ||||||
|     + IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element> |  | ||||||
|     + ToOwned |  | ||||||
|     + InternedCompare |  | ||||||
| { |  | ||||||
|     type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>; |  | ||||||
|     type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>; |  | ||||||
|     type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync; |  | ||||||
|     type Match: 'static |  | ||||||
|         + Clone |  | ||||||
|         + Eq |  | ||||||
|         + fmt::Debug |  | ||||||
|         + Hash |  | ||||||
|         + Send |  | ||||||
|         + Sync |  | ||||||
|         + BorrowMut<[Expr<Self::Element>]>; |  | ||||||
|     type MaskVA: ValueArrayOrSlice< |  | ||||||
|             Element = <Self::ElementType as Type>::MaskValue, |  | ||||||
|             ElementType = <Self::ElementType as Type>::MaskType, |  | ||||||
|             LenType = Self::LenType, |  | ||||||
|             MaskVA = Self::MaskVA, |  | ||||||
|         > + ?Sized; |  | ||||||
|     type IsStaticLen: GenericConstBool; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType>; |  | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match; |  | ||||||
|     fn len_from_len_type(v: Self::LenType) -> usize; |  | ||||||
|     #[allow(clippy::result_unit_err)] |  | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>; |  | ||||||
|     fn len_type(&self) -> Self::LenType; |  | ||||||
|     fn len(&self) -> usize; |  | ||||||
|     fn is_empty(&self) -> bool; |  | ||||||
|     fn iter(&self) -> std::slice::Iter<Self::Element> { |  | ||||||
|         Borrow::<[_]>::borrow(self).iter() |  | ||||||
|     } |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self>; |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self; |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T> sealed::Sealed for [T] {} | impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> { | ||||||
| 
 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
| impl<V: Value> ValueArrayOrSlice for [V] |         let ArrayImpl { | ||||||
| where |             element, len: _, .. | ||||||
|     V::Type: Type<Value = V>, |         } = *self.impl_; | ||||||
| { |         write!(f, "Array<{element:?}, {}>", self.len()) | ||||||
|     type Element = V; |  | ||||||
|     type ElementType = V::Type; |  | ||||||
|     type LenType = usize; |  | ||||||
|     type Match = Box<[Expr<V>]>; |  | ||||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue]; |  | ||||||
|     type IsStaticLen = ConstBool<false>; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = None; |  | ||||||
| 
 |  | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match { |  | ||||||
|         (0..array.canonical_type().len()) |  | ||||||
|             .map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()) |  | ||||||
|             .collect() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_from_len_type(v: Self::LenType) -> usize { |  | ||||||
|         v |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { |  | ||||||
|         Ok(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_type(&self) -> Self::LenType { |  | ||||||
|         self.len() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len(&self) -> usize { |  | ||||||
|         <[_]>::len(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn is_empty(&self) -> bool { |  | ||||||
|         <[_]>::is_empty(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self> { |  | ||||||
|         Arc::from(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { |  | ||||||
|         MakeMutSlice::make_mut_slice(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { |  | ||||||
|         self |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T, const N: usize> sealed::Sealed for [T; N] {} | pub type Array< | ||||||
|  |     T = CanonicalType, | ||||||
|  |     const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE }, | ||||||
|  | > = ArrayType<T, ConstUsize<LEN>>; | ||||||
| 
 | 
 | ||||||
| impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N] | #[allow(non_upper_case_globals)] | ||||||
| where | pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics; | ||||||
|     V::Type: Type<Value = V>, |  | ||||||
| { |  | ||||||
|     type Element = V; |  | ||||||
|     type ElementType = V::Type; |  | ||||||
|     type LenType = (); |  | ||||||
|     type Match = [Expr<V>; N]; |  | ||||||
|     type MaskVA = [<Self::ElementType as Type>::MaskValue; N]; |  | ||||||
|     type IsStaticLen = ConstBool<true>; |  | ||||||
|     const FIXED_LEN_TYPE: Option<Self::LenType> = Some(()); |  | ||||||
| 
 | 
 | ||||||
|     fn make_match(array: Expr<Array<Self>>) -> Self::Match { | impl<T: Type, Len: Size> ArrayType<T, Len> { | ||||||
|         std::array::from_fn(|index| { |     pub fn new(element: T, len: Len::SizeType) -> Self { | ||||||
|             ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr() |         let element_props = element.canonical().type_properties(); | ||||||
|         }) |         let type_properties = TypeProperties { | ||||||
|     } |             is_passive: element_props.is_passive, | ||||||
| 
 |             is_storable: element_props.is_storable, | ||||||
|     fn len_from_len_type(_v: Self::LenType) -> usize { |             is_castable_from_bits: element_props.is_castable_from_bits, | ||||||
|         N |             bit_width: element_props | ||||||
|     } |                 .bit_width | ||||||
| 
 |                 .checked_mul(Len::as_usize(len)) | ||||||
|     fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> { |                 .expect("array too big"), | ||||||
|         if v == N { |  | ||||||
|             Ok(()) |  | ||||||
|         } else { |  | ||||||
|             Err(()) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn len_type(&self) -> Self::LenType {} |  | ||||||
| 
 |  | ||||||
|     fn len(&self) -> usize { |  | ||||||
|         N |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn is_empty(&self) -> bool { |  | ||||||
|         N == 0 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn clone_to_arc(&self) -> Arc<Self> { |  | ||||||
|         Arc::new(self.clone()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self { |  | ||||||
|         Arc::make_mut(v) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> { |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, PartialEq, Eq, Hash)] |  | ||||||
| pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> { |  | ||||||
|     element: VA::ElementType, |  | ||||||
|     len: VA::LenType, |  | ||||||
|     bit_width: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait ArrayTypeTrait: |  | ||||||
|     Type< |  | ||||||
|         CanonicalType = ArrayType<[DynCanonicalValue]>, |  | ||||||
|         Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>, |  | ||||||
|         CanonicalValue = Array<[DynCanonicalValue]>, |  | ||||||
|         MaskType = ArrayType< |  | ||||||
|             <<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA, |  | ||||||
|         >, |  | ||||||
|     > + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> |  | ||||||
|     + sealed::Sealed |  | ||||||
|     + Connect<Self> |  | ||||||
| { |  | ||||||
|     type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType> |  | ||||||
|         + ?Sized; |  | ||||||
|     type Element: Value<Type = Self::ElementType>; |  | ||||||
|     type ElementType: Type<Value = Self::Element>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> { |  | ||||||
|     type ValueArrayOrSlice = VA; |  | ||||||
|     type Element = VA::Element; |  | ||||||
|     type ElementType = VA::ElementType; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         Self { |  | ||||||
|             element: self.element.clone(), |  | ||||||
|             len: self.len, |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> { |  | ||||||
|     pub fn element(&self) -> &VA::ElementType { |  | ||||||
|         &self.element |  | ||||||
|     } |  | ||||||
|     pub fn len(&self) -> usize { |  | ||||||
|         VA::len_from_len_type(self.len) |  | ||||||
|     } |  | ||||||
|     pub fn is_empty(&self) -> bool { |  | ||||||
|         self.len() == 0 |  | ||||||
|     } |  | ||||||
|     pub fn bit_width(&self) -> usize { |  | ||||||
|         self.bit_width |  | ||||||
|     } |  | ||||||
|     pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { |  | ||||||
|         ArrayType { |  | ||||||
|             len: self.len(), |  | ||||||
|             element: self.element, |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_with_len(element: VA::ElementType, len: usize) -> Self { |  | ||||||
|         Self::new_with_len_type( |  | ||||||
|             element, |  | ||||||
|             VA::try_len_type_from_len(len).expect("length should match"), |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self { |  | ||||||
|         let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else { |  | ||||||
|             panic!("array is too big: bit-width overflowed"); |  | ||||||
|         }; |         }; | ||||||
|         ArrayType { |         Self { | ||||||
|             element, |             impl_: ArrayImpl { | ||||||
|             len, |                 element, | ||||||
|             bit_width, |                 len, | ||||||
|  |                 type_properties, | ||||||
|  |             } | ||||||
|  |             .intern_sized(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } |     pub fn element(&self) -> &T { | ||||||
| 
 |         &self.impl_.element | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA> |  | ||||||
| where |  | ||||||
|     VA::ElementType: Fold<State>, |  | ||||||
| { |  | ||||||
|     fn fold(self, state: &mut State) -> Result<Self, State::Error> { |  | ||||||
|         state.fold_array_type(self) |  | ||||||
|     } |     } | ||||||
|     fn default_fold(self, state: &mut State) -> Result<Self, State::Error> { |     pub fn len(self) -> usize { | ||||||
|         Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) |         Len::as_usize(self.impl_.len) | ||||||
|  |     } | ||||||
|  |     pub fn type_properties(self) -> TypeProperties { | ||||||
|  |         self.impl_.type_properties | ||||||
|  |     } | ||||||
|  |     pub fn as_dyn_array(self) -> Array { | ||||||
|  |         Array::new_dyn(self.element().canonical(), self.len()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> | impl<T: Type, Len: KnownSize> ArrayType<T, Len> { | ||||||
| where |     pub fn new_static(element: T) -> Self { | ||||||
|     VA::ElementType: Visit<State>, |         Self::new(element, Len::SizeType::default()) | ||||||
| { |  | ||||||
|     fn visit(&self, state: &mut State) -> Result<(), State::Error> { |  | ||||||
|         state.visit_array_type(self) |  | ||||||
|     } |  | ||||||
|     fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { |  | ||||||
|         self.element.visit(state) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> { | impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> { | ||||||
|     pub fn new_array(element: V::Type) -> Self { |  | ||||||
|         ArrayType::new_with_len_type(element, ()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> { |  | ||||||
|     fn static_type() -> Self { |     fn static_type() -> Self { | ||||||
|         Self::new_array(StaticType::static_type()) |         Self::new_static(T::static_type()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> { | impl<T: Type> Array<T> { | ||||||
|     pub fn new_slice(element: V::Type, len: usize) -> Self { |     pub fn new_dyn(element: T, len: usize) -> Self { | ||||||
|         ArrayType::new_with_len_type(element, len) |         Self::new(element, len) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> { | impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|     type CanonicalType = ArrayType<[DynCanonicalValue]>; |     type MaskType = ArrayType<T::MaskType, Len>; | ||||||
|     type Value = Array<VA>; |     type MatchVariant = Len::ArrayMatch<T>; | ||||||
|     type CanonicalValue = Array<[DynCanonicalValue]>; |  | ||||||
|     type MaskType = ArrayType<VA::MaskVA>; |  | ||||||
|     type MaskValue = Array<VA::MaskVA>; |  | ||||||
|     type MatchVariant = VA::Match; |  | ||||||
|     type MatchActiveScope = (); |     type MatchActiveScope = (); | ||||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>; |     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; | ||||||
|     type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; |     type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; | ||||||
| 
 | 
 | ||||||
|     fn match_variants<IO: BundleValue>( |     fn match_variants( | ||||||
|         this: Expr<Self::Value>, |         this: Expr<Self>, | ||||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, |         module_builder: &mut ModuleBuilder<NormalModule>, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Self::MatchVariantsIter |     ) -> Self::MatchVariantsIter { | ||||||
|     where |         let base = Expr::as_dyn_array(this); | ||||||
|         IO::Type: BundleType<Value = IO>, |         let base_ty = Expr::ty(base); | ||||||
|     { |  | ||||||
|         let _ = module_builder; |         let _ = module_builder; | ||||||
|         let _ = source_location; |         let _ = source_location; | ||||||
|         std::iter::once(MatchVariantWithoutScope(VA::make_match(this))) |         let retval = Vec::from_iter( | ||||||
|  |             (0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()), | ||||||
|  |         ); | ||||||
|  |         std::iter::once(MatchVariantWithoutScope( | ||||||
|  |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|  |                 .ok() | ||||||
|  |                 .expect("unreachable"), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         #[derive(Clone, Hash, Eq, PartialEq)] |         ArrayType::new(self.element().mask_type(), self.impl_.len) | ||||||
|         struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>); |  | ||||||
|         impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {} |  | ||||||
|         impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> { |  | ||||||
|             type Input = ArrayType<T::ValueArrayOrSlice>; |  | ||||||
|             type InputOwned = ArrayType<T::ValueArrayOrSlice>; |  | ||||||
|             type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType; |  | ||||||
| 
 |  | ||||||
|             fn inner(self, input: &Self::Input) -> Self::Output { |  | ||||||
|                 ArrayType::new_with_len_type(input.element.mask_type(), input.len) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         ArrayMaskTypeMemoize::<Self>(PhantomData).get(self) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |     fn canonical(&self) -> CanonicalType { | ||||||
|         ArrayType { |         CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len())) | ||||||
|             element: self.element.canonical_dyn(), |  | ||||||
|             len: self.len(), |  | ||||||
|             bit_width: self.bit_width, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn source_location(&self) -> SourceLocation { |     #[track_caller] | ||||||
|  |     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|  |         let CanonicalType::Array(array) = canonical_type else { | ||||||
|  |             panic!("expected array"); | ||||||
|  |         }; | ||||||
|  |         Self::new( | ||||||
|  |             T::from_canonical(*array.element()), | ||||||
|  |             Len::from_usize(array.len()), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         SourceLocation::builtin() | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     fn type_enum(&self) -> TypeEnum { | impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||||
|         TypeEnum::ArrayType(self.canonical()) |     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { | ||||||
|     } |         let base = Expr::as_dyn_array(*this); | ||||||
| 
 |         let base_ty = Expr::ty(base); | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |         let retval = Vec::from_iter( | ||||||
|         Self { |             (0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()), | ||||||
|             element: VA::ElementType::from_dyn_canonical_type(t.element), |         ); | ||||||
|             len: VA::try_len_type_from_len(t.len).expect("length should match"), |         Interned::<_>::into_inner(Intern::intern_sized( | ||||||
|             bit_width: t.bit_width, |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|         } |                 .ok() | ||||||
|     } |                 .expect("unreachable"), | ||||||
| 
 |         )) | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>( |  | ||||||
|             this, |  | ||||||
|         )?) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||||
|     for ArrayType<Lhs> | pub struct ArrayWithoutGenerics; | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| impl CanonicalType for ArrayType<[DynCanonicalValue]> { | impl<T: Type> Index<T> for ArrayWithoutGenerics { | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; |     type Output = ArrayWithoutLen<T>; | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, PartialEq, Eq, Hash)] |     fn index(&self, element: T) -> &Self::Output { | ||||||
| pub struct Array<VA: ValueArrayOrSlice + ?Sized> { |         Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) | ||||||
|     element_ty: VA::ElementType, |  | ||||||
|     value: Arc<VA>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         Self { |  | ||||||
|             element_ty: self.element_ty.clone(), |  | ||||||
|             value: self.value.clone(), |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> { | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     type Type = ArrayType<VA>; | pub struct ArrayWithoutLen<T: Type> { | ||||||
| 
 |     element: T, | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> { | impl<T: Type> Index<usize> for ArrayWithoutLen<T> { | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |     type Output = Array<T>; | ||||||
|         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 { |     fn index(&self, len: usize) -> &Self::Output { | ||||||
|                 let mut bits = BitVec::with_capacity(input.ty().bit_width()); |         Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len))) | ||||||
|                 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 |  | ||||||
|     VA::Element: StaticValue, |  | ||||||
| { |  | ||||||
|     fn from(value: T) -> Self { |  | ||||||
|         Self::new(StaticType::static_type(), value.into()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] { |  | ||||||
|     type Type = ArrayType<[T::Value]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         ArrayType::new_with_len_type(StaticType::static_type(), self.len()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         let elements = Intern::intern_owned(Vec::from_iter( |  | ||||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), |  | ||||||
|         )); |  | ||||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> { |  | ||||||
|     type Type = ArrayType<[T::Value]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         <[E]>::ty(self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         <[E]>::to_expr(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] { |  | ||||||
|     type Type = ArrayType<[T::Value; N]>; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         ArrayType::new_with_len_type(StaticType::static_type(), ()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         let elements = Intern::intern_owned(Vec::from_iter( |  | ||||||
|             self.iter().map(|v| v.to_expr().to_canonical_dyn()), |  | ||||||
|         )); |  | ||||||
|         ArrayLiteral::new_unchecked(elements, self.ty()).to_expr() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct ArrayIntoIter<VA: ValueArrayOrSlice> { |  | ||||||
|     array: Arc<VA>, |  | ||||||
|     indexes: std::ops::Range<usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> { |  | ||||||
|     type Item = VA::Element; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(self.array[self.indexes.next()?].clone()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn size_hint(&self) -> (usize, Option<usize>) { |  | ||||||
|         self.indexes.size_hint() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(self.array[self.indexes.next_back()?].clone()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Array<VA> { |  | ||||||
|     pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> { |  | ||||||
|         self.value.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> { |  | ||||||
|     type Item = &'a VA::Element; |  | ||||||
|     type IntoIter = std::slice::Iter<'a, VA::Element>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.value.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> { |  | ||||||
|     type Item = VA::Element; |  | ||||||
|     type IntoIter = ArrayIntoIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         ArrayIntoIter { |  | ||||||
|             indexes: 0..self.len(), |  | ||||||
|             array: self.value, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct ArrayExprIter<VA: ValueArrayOrSlice> { |  | ||||||
|     array: Expr<Array<VA>>, |  | ||||||
|     indexes: std::ops::Range<usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next()?)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn size_hint(&self) -> (usize, Option<usize>) { |  | ||||||
|         self.indexes.size_hint() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {} |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |  | ||||||
|         Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
|     type IntoIter = ArrayExprIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> { |  | ||||||
|     type Item = Expr<VA::Element>; |  | ||||||
|     type IntoIter = ArrayExprIter<VA>; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         self.iter() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<VA: ValueArrayOrSlice> Expr<Array<VA>> { |  | ||||||
|     pub fn len(self) -> usize { |  | ||||||
|         self.canonical_type().len() |  | ||||||
|     } |  | ||||||
|     pub fn is_empty(self) -> bool { |  | ||||||
|         self.canonical_type().is_empty() |  | ||||||
|     } |  | ||||||
|     pub fn iter(self) -> ArrayExprIter<VA> { |  | ||||||
|         ArrayExprIter { |  | ||||||
|             indexes: 0..self.len(), |  | ||||||
|             array: self, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,150 +1,58 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| #![allow(clippy::type_complexity)] | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     bundle::{BundleValue, TypeHintTrait}, |  | ||||||
|     expr::{ops::VariantAccess, Expr, ToExpr}, |     expr::{ops::VariantAccess, Expr, ToExpr}, | ||||||
|     int::{UInt, UIntType}, |     hdl, | ||||||
|     intern::{Intern, Interned, MemoizeGeneric}, |     int::Bool, | ||||||
|  |     intern::{Intern, Interned}, | ||||||
|     module::{ |     module::{ | ||||||
|         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, |         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, | ||||||
|         NormalModule, Scope, |         NormalModule, Scope, | ||||||
|     }, |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{CanonicalType, MatchVariantAndInactiveScope, Type, TypeProperties}, | ||||||
|         CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, |  | ||||||
|         DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; |  | ||||||
| use hashbrown::HashMap; | use hashbrown::HashMap; | ||||||
| use std::{ | use std::iter::FusedIterator; | ||||||
|     borrow::Cow, |  | ||||||
|     fmt, |  | ||||||
|     hash::{Hash, Hasher}, |  | ||||||
|     iter::FusedIterator, |  | ||||||
|     marker::PhantomData, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
| pub struct VariantType<T> { | pub struct EnumVariant { | ||||||
|     pub name: Interned<str>, |     pub name: Interned<str>, | ||||||
|     pub ty: Option<T>, |     pub ty: Option<CanonicalType>, | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>); |  | ||||||
| 
 |  | ||||||
| impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         let VariantType { name, ref ty } = *self.0; |  | ||||||
|         if let Some(ty) = ty { |  | ||||||
|             write!(f, "{name}({ty:?})") |  | ||||||
|         } else { |  | ||||||
|             write!(f, "{name}") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         fmt::Debug::fmt(self, f) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T> VariantType<T> { |  | ||||||
|     pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> { |  | ||||||
|         let Self { name, ty } = self; |  | ||||||
|         VariantType { name, ty: f(ty) } |  | ||||||
|     } |  | ||||||
|     pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> { |  | ||||||
|         let Self { name, ty } = self; |  | ||||||
|         VariantType { |  | ||||||
|             name, |  | ||||||
|             ty: ty.map(f), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn as_ref_ty(&self) -> VariantType<&T> { |  | ||||||
|         VariantType { |  | ||||||
|             name: self.name, |  | ||||||
|             ty: self.ty.as_ref(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> { |  | ||||||
|         FmtDebugInEnum(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type> VariantType<T> { |  | ||||||
|     pub fn canonical(&self) -> VariantType<T::CanonicalType> { |  | ||||||
|         self.as_ref_ty().map_ty(T::canonical) |  | ||||||
|     } |  | ||||||
|     pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> { |  | ||||||
|         self.as_ref_ty().map_ty(T::to_dyn) |  | ||||||
|     } |  | ||||||
|     pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> { |  | ||||||
|         self.as_ref_ty().map_ty(T::canonical_dyn) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl VariantType<Interned<dyn DynCanonicalType>> { |  | ||||||
|     pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T { |  | ||||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); |  | ||||||
|         let Some(ty) = self.ty else { |  | ||||||
|             panic!("variant {expected_name} has no value but a value is expected"); |  | ||||||
|         }; |  | ||||||
|         T::from_dyn_canonical_type(ty) |  | ||||||
|     } |  | ||||||
|     pub fn from_canonical_type_helper_no_value(self, expected_name: &str) { |  | ||||||
|         assert_eq!(&*self.name, expected_name, "variant name doesn't match"); |  | ||||||
|         assert!( |  | ||||||
|             self.ty.is_none(), |  | ||||||
|             "variant {expected_name} has a value but is expected to have no value" |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Eq)] | #[derive(Clone, Eq)] | ||||||
| struct DynEnumTypeImpl { | struct EnumImpl { | ||||||
|     variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, |     variants: Interned<[EnumVariant]>, | ||||||
|     name_indexes: HashMap<Interned<str>, usize>, |     name_indexes: HashMap<Interned<str>, usize>, | ||||||
|     bit_width: usize, |     type_properties: TypeProperties, | ||||||
|     is_storable: bool, |  | ||||||
|     is_castable_from_bits: bool, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for DynEnumTypeImpl { | impl std::fmt::Debug for EnumImpl { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         write!(f, "DynEnumType ")?; |         f.debug_struct("Enum") | ||||||
|         f.debug_set() |             .field("variants", &self.variants) | ||||||
|             .entries( |             .field("name_indexes", &self.name_indexes) | ||||||
|                 self.variants |             .field("type_properties", &self.type_properties) | ||||||
|                     .iter() |  | ||||||
|                     .map(|variant| variant.fmt_debug_in_enum()), |  | ||||||
|             ) |  | ||||||
|             .finish() |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PartialEq for DynEnumTypeImpl { | impl std::hash::Hash for EnumImpl { | ||||||
|  |     fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||||||
|  |         self.variants.hash(state); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PartialEq for EnumImpl { | ||||||
|     fn eq(&self, other: &Self) -> bool { |     fn eq(&self, other: &Self) -> bool { | ||||||
|         self.variants == other.variants |         self.variants == other.variants | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Hash for DynEnumTypeImpl { | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | pub struct Enum(Interned<EnumImpl>); | ||||||
|         self.variants.hash(state); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Hash, PartialEq, Eq)] |  | ||||||
| pub struct DynEnumType(Interned<DynEnumTypeImpl>); |  | ||||||
| 
 |  | ||||||
| impl fmt::Debug for DynEnumType { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         self.0.fmt(f) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| fn discriminant_bit_width_impl(variant_count: usize) -> usize { | fn discriminant_bit_width_impl(variant_count: usize) -> usize { | ||||||
|     variant_count |     variant_count | ||||||
|  | @ -153,38 +61,47 @@ fn discriminant_bit_width_impl(variant_count: usize) -> usize { | ||||||
|         .unwrap_or(0) as usize |         .unwrap_or(0) as usize | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl DynEnumType { | impl Enum { | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { |     pub fn new(variants: Interned<[EnumVariant]>) -> Self { | ||||||
|         assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); |         assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); | ||||||
|         let mut name_indexes = HashMap::with_capacity(variants.len()); |         let mut name_indexes = HashMap::with_capacity(variants.len()); | ||||||
|         let mut body_bit_width = 0usize; |         let mut type_properties = TypeProperties { | ||||||
|         let mut is_storable = true; |             is_passive: true, | ||||||
|         let mut is_castable_from_bits = true; |             is_storable: true, | ||||||
|         for (index, &VariantType { name, ty }) in variants.iter().enumerate() { |             is_castable_from_bits: true, | ||||||
|             if let Some(old_index) = name_indexes.insert(name, index) { |             bit_width: 0, | ||||||
|  |         }; | ||||||
|  |         for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { | ||||||
|  |             if let Some(old_index) = name_indexes.insert(*name, index) { | ||||||
|                 panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); |                 panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); | ||||||
|             } |             } | ||||||
|             if let Some(ty) = ty { |             if let Some(ty) = ty { | ||||||
|                 assert!( |                 let TypeProperties { | ||||||
|                     ty.is_passive(), |                     is_passive, | ||||||
|                     "variant type must be a passive type: {ty:?}" |                     is_storable, | ||||||
|                 ); |                     is_castable_from_bits, | ||||||
|                 body_bit_width = body_bit_width.max(ty.bit_width()); |                     bit_width, | ||||||
|                 is_storable &= ty.is_storable(); |                 } = ty.type_properties(); | ||||||
|                 is_castable_from_bits &= ty.is_castable_from_bits(); |                 assert!(is_passive, "variant type must be a passive type: {ty:?}"); | ||||||
|  |                 type_properties = TypeProperties { | ||||||
|  |                     is_passive: true, | ||||||
|  |                     is_storable: type_properties.is_storable & is_storable, | ||||||
|  |                     is_castable_from_bits: type_properties.is_castable_from_bits | ||||||
|  |                         & is_castable_from_bits, | ||||||
|  |                     bit_width: type_properties.bit_width.max(bit_width), | ||||||
|  |                 }; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         let bit_width = body_bit_width |         type_properties.bit_width = type_properties | ||||||
|  |             .bit_width | ||||||
|             .checked_add(discriminant_bit_width_impl(variants.len())) |             .checked_add(discriminant_bit_width_impl(variants.len())) | ||||||
|             .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); |             .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); | ||||||
|         Self( |         Self( | ||||||
|             DynEnumTypeImpl { |             EnumImpl { | ||||||
|                 variants, |                 variants, | ||||||
|                 name_indexes, |                 name_indexes, | ||||||
|                 bit_width, |                 type_properties, | ||||||
|                 is_storable, |  | ||||||
|                 is_castable_from_bits, |  | ||||||
|             } |             } | ||||||
|             .intern_sized(), |             .intern_sized(), | ||||||
|         ) |         ) | ||||||
|  | @ -192,233 +109,31 @@ impl DynEnumType { | ||||||
|     pub fn discriminant_bit_width(self) -> usize { |     pub fn discriminant_bit_width(self) -> usize { | ||||||
|         discriminant_bit_width_impl(self.variants().len()) |         discriminant_bit_width_impl(self.variants().len()) | ||||||
|     } |     } | ||||||
|     pub fn is_passive(self) -> bool { |     pub fn type_properties(self) -> TypeProperties { | ||||||
|         true |         self.0.type_properties | ||||||
|     } |  | ||||||
|     pub fn is_storable(self) -> bool { |  | ||||||
|         self.0.is_storable |  | ||||||
|     } |  | ||||||
|     pub fn is_castable_from_bits(self) -> bool { |  | ||||||
|         self.0.is_castable_from_bits |  | ||||||
|     } |  | ||||||
|     pub fn bit_width(self) -> usize { |  | ||||||
|         self.0.bit_width |  | ||||||
|     } |     } | ||||||
|     pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { |     pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { | ||||||
|         &self.0.name_indexes |         &self.0.name_indexes | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Hash, PartialEq, Eq)] |  | ||||||
| pub struct DynEnum { |  | ||||||
|     ty: DynEnumType, |  | ||||||
|     variant_index: usize, |  | ||||||
|     variant_value: Option<DynCanonicalValue>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl DynEnum { |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_by_index( |  | ||||||
|         ty: DynEnumType, |  | ||||||
|         variant_index: usize, |  | ||||||
|         variant_value: Option<DynCanonicalValue>, |  | ||||||
|     ) -> Self { |  | ||||||
|         let variant = ty.variants()[variant_index]; |  | ||||||
|         assert_eq!( |  | ||||||
|             variant_value.as_ref().map(|v| v.ty()), |  | ||||||
|             variant.ty, |  | ||||||
|             "variant value doesn't match type" |  | ||||||
|         ); |  | ||||||
|         Self { |  | ||||||
|             ty, |  | ||||||
|             variant_index, |  | ||||||
|             variant_value, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     #[track_caller] |  | ||||||
|     pub fn new_by_name( |  | ||||||
|         ty: DynEnumType, |  | ||||||
|         variant_name: Interned<str>, |  | ||||||
|         variant_value: Option<DynCanonicalValue>, |  | ||||||
|     ) -> Self { |  | ||||||
|         let variant_index = ty.name_indexes()[&variant_name]; |  | ||||||
|         Self::new_by_index(ty, variant_index, variant_value) |  | ||||||
|     } |  | ||||||
|     pub fn variant_index(&self) -> usize { |  | ||||||
|         self.variant_index |  | ||||||
|     } |  | ||||||
|     pub fn variant_value(&self) -> &Option<DynCanonicalValue> { |  | ||||||
|         &self.variant_value |  | ||||||
|     } |  | ||||||
|     pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> { |  | ||||||
|         self.ty.variants()[self.variant_index] |  | ||||||
|     } |  | ||||||
|     pub fn variant_name(&self) -> Interned<str> { |  | ||||||
|         self.variant_with_type().name |  | ||||||
|     } |  | ||||||
|     pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> { |  | ||||||
|         self.variant_with_type().ty |  | ||||||
|     } |  | ||||||
|     pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> { |  | ||||||
|         self.variant_with_type() |  | ||||||
|             .map_opt_ty(|_| self.variant_value.as_ref()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] |  | ||||||
| pub struct VariantsHint { |  | ||||||
|     pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>, |  | ||||||
|     pub more_variants: bool, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl VariantsHint { |  | ||||||
|     pub fn new( |  | ||||||
|         known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>, |  | ||||||
|         more_variants: bool, |  | ||||||
|     ) -> Self { |  | ||||||
|         let known_variants = Intern::intern_owned(Vec::from_iter(known_variants)); |  | ||||||
|         Self { |  | ||||||
|             known_variants, |  | ||||||
|             more_variants, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn check_variant( |  | ||||||
|         self, |  | ||||||
|         index: usize, |  | ||||||
|         variant: VariantType<&dyn DynType>, |  | ||||||
|     ) -> Result<(), String> { |  | ||||||
|         let Some(&known_variant) = self.known_variants.get(index) else { |  | ||||||
|             return if self.more_variants { |  | ||||||
|                 Ok(()) |  | ||||||
|             } else { |  | ||||||
|                 Err(format!( |  | ||||||
|                     "too many variants: name={:?} index={index}", |  | ||||||
|                     variant.name |  | ||||||
|                 )) |  | ||||||
|             }; |  | ||||||
|         }; |  | ||||||
|         let VariantType { |  | ||||||
|             name: known_name, |  | ||||||
|             ty: type_hint, |  | ||||||
|         } = known_variant; |  | ||||||
|         let VariantType { name, ty } = variant; |  | ||||||
|         if name != known_name { |  | ||||||
|             Err(format!( |  | ||||||
|                 "wrong variant name {name:?}, expected {known_name:?}" |  | ||||||
|             )) |  | ||||||
|         } else { |  | ||||||
|             match (ty, type_hint) { |  | ||||||
|                 (Some(ty), Some(type_hint)) => type_hint.matches(ty), |  | ||||||
|                 (None, None) => Ok(()), |  | ||||||
|                 (None, Some(_)) => Err(format!( |  | ||||||
|                     "expected variant {name:?} to have type, no type provided" |  | ||||||
|                 )), |  | ||||||
|                 (Some(_), None) => Err(format!( |  | ||||||
|                     "expected variant {name:?} to have no type, but a type was provided" |  | ||||||
|                 )), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait EnumType: | pub trait EnumType: | ||||||
|     Type< |     Type< | ||||||
|         CanonicalType = DynEnumType, |     MaskType = Bool, | ||||||
|         CanonicalValue = DynEnum, |     MatchActiveScope = Scope, | ||||||
|         MaskType = UIntType<1>, |     MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, | ||||||
|         MaskValue = UInt<1>, |     MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||||
|         MatchActiveScope = Scope, | > | ||||||
|         MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, |  | ||||||
|         MatchVariantsIter = EnumMatchVariantsIter<Self>, |  | ||||||
|     > + Connect<Self> |  | ||||||
| where |  | ||||||
|     Self::Value: EnumValue + ToExpr<Type = Self>, |  | ||||||
| { | { | ||||||
|     type Builder; |     fn variants(&self) -> Interned<[EnumVariant]>; | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         v: Self::MatchVariantAndInactiveScope, | ||||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope); |     ) -> (Self::MatchVariant, Self::MatchActiveScope); | ||||||
|     fn builder() -> Self::Builder; |  | ||||||
|     fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>; |  | ||||||
|     fn variants_hint() -> VariantsHint; |  | ||||||
|     #[allow(clippy::result_unit_err)] |  | ||||||
|     fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>( |  | ||||||
|         &self, |  | ||||||
|         variant_index: usize, |  | ||||||
|         variant_value: Option<&VariantValue>, |  | ||||||
|     ) -> Result<Interned<BitSlice>, ()> { |  | ||||||
|         #[derive(Hash, Eq, PartialEq)] |  | ||||||
|         struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>); |  | ||||||
|         impl<E, V> Clone for VariantToBitsMemoize<E, V> { |  | ||||||
|             fn clone(&self) -> Self { |  | ||||||
|                 *self |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         impl<E, V> Copy for VariantToBitsMemoize<E, V> {} |  | ||||||
|         impl< |  | ||||||
|                 E: EnumType<Value: EnumValue<Type = E>>, |  | ||||||
|                 V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone, |  | ||||||
|             > MemoizeGeneric for VariantToBitsMemoize<E, V> |  | ||||||
|         { |  | ||||||
|             type InputRef<'a> = (&'a E, usize, Option<&'a V>); |  | ||||||
|             type InputOwned = (E, usize, Option<V>); |  | ||||||
|             type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>); |  | ||||||
|             type Output = Result<Interned<BitSlice>, ()>; |  | ||||||
| 
 |  | ||||||
|             fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { |  | ||||||
|                 (&input.0, input.1, input.2.as_ref()) |  | ||||||
|             } |  | ||||||
|             fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { |  | ||||||
|                 a == b |  | ||||||
|             } |  | ||||||
|             fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { |  | ||||||
|                 (input.0.into_owned(), input.1, input.2.map(Cow::into_owned)) |  | ||||||
|             } |  | ||||||
|             fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { |  | ||||||
|                 (&input.0, input.1, input.2.as_deref()) |  | ||||||
|             } |  | ||||||
|             fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { |  | ||||||
|                 (Cow::Owned(input.0), input.1, input.2.map(Cow::Owned)) |  | ||||||
|             } |  | ||||||
|             fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { |  | ||||||
|                 (Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed)) |  | ||||||
|             } |  | ||||||
|             fn inner(self, input: Self::InputRef<'_>) -> Self::Output { |  | ||||||
|                 let (ty, variant_index, variant_value) = input; |  | ||||||
|                 let ty = ty.canonical(); |  | ||||||
|                 let mut bits = BitVec::with_capacity(ty.bit_width()); |  | ||||||
|                 bits.extend_from_bitslice( |  | ||||||
|                     &variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()], |  | ||||||
|                 ); |  | ||||||
|                 if let Some(variant_value) = variant_value { |  | ||||||
|                     bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?); |  | ||||||
|                 } |  | ||||||
|                 bits.resize(ty.bit_width(), false); |  | ||||||
|                 Ok(Intern::intern_owned(bits)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get(( |  | ||||||
|             self, |  | ||||||
|             variant_index, |  | ||||||
|             variant_value, |  | ||||||
|         )) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait EnumValue: Value | pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>); | ||||||
| where |  | ||||||
|     <Self as ToExpr>::Type: EnumType<Value = Self>, |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>) | impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>; |  | ||||||
| 
 |  | ||||||
| impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> |  | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     type MatchVariant = T::MatchVariant; |     type MatchVariant = T::MatchVariant; | ||||||
|     type MatchActiveScope = Scope; |     type MatchActiveScope = Scope; | ||||||
| 
 | 
 | ||||||
|  | @ -427,36 +142,22 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> | impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> { | ||||||
| where |     pub fn variant_access(&self) -> Interned<VariantAccess> { | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> { |  | ||||||
|         self.0.variant_access() |         self.0.variant_access() | ||||||
|     } |     } | ||||||
|     pub fn activate( |     pub fn activate(self) -> (Interned<VariantAccess>, Scope) { | ||||||
|         self, |  | ||||||
|     ) -> ( |  | ||||||
|         Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>, |  | ||||||
|         Scope, |  | ||||||
|     ) { |  | ||||||
|         self.0.activate() |         self.0.activate() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct EnumMatchVariantsIter<T: EnumType> | pub struct EnumMatchVariantsIter<T: EnumType> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     pub(crate) inner: EnumMatchVariantsIterImpl<T>, |     pub(crate) inner: EnumMatchVariantsIterImpl<T>, | ||||||
|     pub(crate) variant_index: std::ops::Range<usize>, |     pub(crate) variant_index: std::ops::Range<usize>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> | impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     type Item = EnumMatchVariantAndInactiveScope<T>; |     type Item = EnumMatchVariantAndInactiveScope<T>; | ||||||
| 
 | 
 | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|  | @ -470,21 +171,15 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> | impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     fn len(&self) -> usize { |     fn len(&self) -> usize { | ||||||
|         self.variant_index.len() |         self.variant_index.len() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {} | impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {} | ||||||
| 
 | 
 | ||||||
| impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> | impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> { | ||||||
| where |  | ||||||
|     T::Value: EnumValue<Type = T>, |  | ||||||
| { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |     fn next_back(&mut self) -> Option<Self::Item> { | ||||||
|         self.variant_index.next_back().map(|variant_index| { |         self.variant_index.next_back().map(|variant_index| { | ||||||
|             EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) |             EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) | ||||||
|  | @ -492,129 +187,55 @@ where | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Type for DynEnumType { | impl EnumType for Enum { | ||||||
|     type CanonicalType = DynEnumType; |  | ||||||
|     type Value = DynEnum; |  | ||||||
|     type CanonicalValue = DynEnum; |  | ||||||
|     type MaskType = UIntType<1>; |  | ||||||
|     type MaskValue = UInt<1>; |  | ||||||
|     type MatchVariant = Option<Expr<DynCanonicalValue>>; |  | ||||||
|     type MatchActiveScope = Scope; |  | ||||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; |  | ||||||
|     type MatchVariantsIter = EnumMatchVariantsIter<Self>; |  | ||||||
| 
 |  | ||||||
|     fn match_variants<IO: BundleValue>( |  | ||||||
|         this: Expr<Self::Value>, |  | ||||||
|         module_builder: &mut ModuleBuilder<IO, NormalModule>, |  | ||||||
|         source_location: SourceLocation, |  | ||||||
|     ) -> Self::MatchVariantsIter |  | ||||||
|     where |  | ||||||
|         IO::Type: crate::bundle::BundleType<Value = IO>, |  | ||||||
|     { |  | ||||||
|         module_builder.enum_match_variants_helper(this, source_location) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn mask_type(&self) -> Self::MaskType { |  | ||||||
|         UIntType::new() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> Self::CanonicalType { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location(&self) -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn type_enum(&self) -> TypeEnum { |  | ||||||
|         TypeEnum::EnumType(*self) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn from_canonical_type(t: Self::CanonicalType) -> Self { |  | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { |  | ||||||
|         Some(this) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Connect<Self> for DynEnumType {} |  | ||||||
| 
 |  | ||||||
| pub struct NoBuilder; |  | ||||||
| 
 |  | ||||||
| impl EnumType for DynEnumType { |  | ||||||
|     type Builder = NoBuilder; |  | ||||||
| 
 |  | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         v: Self::MatchVariantAndInactiveScope, | ||||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope) { |     ) -> (Self::MatchVariant, Self::MatchActiveScope) { | ||||||
|         let (expr, scope) = v.0.activate(); |         let (expr, scope) = v.0.activate(); | ||||||
|         (expr.variant_type().ty.map(|_| expr.to_expr()), scope) |         (expr.variant_type().map(|_| expr.to_expr()), scope) | ||||||
|     } |     } | ||||||
| 
 |     fn variants(&self) -> Interned<[EnumVariant]> { | ||||||
|     fn builder() -> Self::Builder { |  | ||||||
|         NoBuilder |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> { |  | ||||||
|         self.0.variants |         self.0.variants | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     fn variants_hint() -> VariantsHint { | impl Type for Enum { | ||||||
|         VariantsHint { |     type MaskType = Bool; | ||||||
|             known_variants: [][..].intern(), |     type MatchVariant = Option<Expr<CanonicalType>>; | ||||||
|             more_variants: true, |     type MatchActiveScope = Scope; | ||||||
|         } |     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||||
|  |     type MatchVariantsIter = EnumMatchVariantsIter<Self>; | ||||||
|  | 
 | ||||||
|  |     fn match_variants( | ||||||
|  |         this: Expr<Self>, | ||||||
|  |         module_builder: &mut ModuleBuilder<NormalModule>, | ||||||
|  |         source_location: crate::source_location::SourceLocation, | ||||||
|  |     ) -> Self::MatchVariantsIter { | ||||||
|  |         module_builder.enum_match_variants_helper(this, source_location) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn mask_type(&self) -> Self::MaskType { | ||||||
|  |         Bool | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn canonical(&self) -> CanonicalType { | ||||||
|  |         CanonicalType::Enum(*self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[track_caller] | ||||||
|  |     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|  |         let CanonicalType::Enum(retval) = canonical_type else { | ||||||
|  |             panic!("expected enum"); | ||||||
|  |         }; | ||||||
|  |         retval | ||||||
|  |     } | ||||||
|  |     fn source_location() -> SourceLocation { | ||||||
|  |         SourceLocation::builtin() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl CanonicalType for DynEnumType { | #[hdl] | ||||||
|     const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType; | pub enum HdlOption<T: Type> { | ||||||
| } |     None, | ||||||
| 
 |     Some(T), | ||||||
| impl ToExpr for DynEnum { |  | ||||||
|     type Type = DynEnumType; |  | ||||||
| 
 |  | ||||||
|     fn ty(&self) -> Self::Type { |  | ||||||
|         self.ty |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { |  | ||||||
|         Expr::from_value(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Value for DynEnum { |  | ||||||
|     fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { |  | ||||||
|         self.clone() |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         this.ty |  | ||||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) |  | ||||||
|             .unwrap() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl EnumValue for DynEnum {} |  | ||||||
| 
 |  | ||||||
| impl CanonicalValue for DynEnum { |  | ||||||
|     fn value_enum_impl(this: &Self) -> ValueEnum { |  | ||||||
|         ValueEnum::Enum(this.clone()) |  | ||||||
|     } |  | ||||||
|     fn to_bits_impl(this: &Self) -> Interned<BitSlice> { |  | ||||||
|         this.ty |  | ||||||
|             .variant_to_bits(this.variant_index, this.variant_value.as_ref()) |  | ||||||
|             .unwrap() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| mod impl_option { |  | ||||||
|     #[allow(dead_code)] |  | ||||||
|     #[derive(crate::ty::Value)] |  | ||||||
|     #[hdl(target(std::option::Option), connect_inexact, outline_generated)] |  | ||||||
|     pub enum Option<T> { |  | ||||||
|         None, |  | ||||||
|         Some(T), |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -12,7 +12,6 @@ extern crate self as fayalite; | ||||||
| pub use std as __std; | pub use std as __std; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| #[doc(alias = "hdl")] |  | ||||||
| /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | ||||||
| /// a [`Module`][`::fayalite::module::Module`] when called.
 | /// a [`Module`][`::fayalite::module::Module`] when called.
 | ||||||
| /// In the function body it will implicitly create a
 | /// In the function body it will implicitly create a
 | ||||||
|  | @ -21,25 +20,82 @@ 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; | ||||||
|  | 
 | ||||||
| #[cfg(feature = "unstable-doc")] | #[cfg(feature = "unstable-doc")] | ||||||
| pub mod _docs; | pub mod _docs; | ||||||
| 
 | 
 | ||||||
| pub mod annotations; | // FIXME: finish
 | ||||||
|  | //pub mod annotations;
 | ||||||
| pub mod array; | pub mod array; | ||||||
| pub mod bundle; | pub mod bundle; | ||||||
| pub mod cli; | //pub mod cli;
 | ||||||
| pub mod clock; | //pub mod clock;
 | ||||||
| pub mod enum_; | pub mod enum_; | ||||||
| pub mod expr; | pub mod expr; | ||||||
| pub mod firrtl; | //pub mod firrtl;
 | ||||||
| pub mod int; | pub mod int; | ||||||
| pub mod intern; | pub mod intern; | ||||||
| pub mod memory; | //pub mod memory;
 | ||||||
| pub mod module; | //pub mod module;
 | ||||||
| pub mod reg; | //pub mod reg;
 | ||||||
| pub mod reset; | //pub mod reset;
 | ||||||
| pub mod source_location; | pub mod source_location; | ||||||
| pub mod ty; | pub mod ty; | ||||||
| pub mod util; | pub mod util; | ||||||
| pub mod valueless; | //pub mod valueless;
 | ||||||
| pub mod wire; | //pub mod wire;
 | ||||||
|  | 
 | ||||||
|  | // FIXME: switch to real module mod
 | ||||||
|  | pub mod module { | ||||||
|  |     use crate::{ | ||||||
|  |         enum_::{EnumMatchVariantsIter, EnumType}, | ||||||
|  |         expr::{ops::VariantAccess, Expr}, | ||||||
|  |         intern::Interned, | ||||||
|  |         source_location::SourceLocation, | ||||||
|  |     }; | ||||||
|  |     use std::marker::PhantomData; | ||||||
|  | 
 | ||||||
|  |     pub struct NormalModule; | ||||||
|  | 
 | ||||||
|  |     pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>); | ||||||
|  | 
 | ||||||
|  |     impl ModuleBuilder<NormalModule> { | ||||||
|  |         pub fn enum_match_variants_helper<Enum: EnumType>( | ||||||
|  |             &mut self, | ||||||
|  |             _base: Expr<Enum>, | ||||||
|  |             _source_location: SourceLocation, | ||||||
|  |         ) -> EnumMatchVariantsIter<Enum> { | ||||||
|  |             todo!() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub struct Scope(()); | ||||||
|  | 
 | ||||||
|  |     pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> { | ||||||
|  |         variant_access: Interned<VariantAccess>, | ||||||
|  |         _phantom: PhantomData<T>, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> { | ||||||
|  |         pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) { | ||||||
|  |             (self.variant_access, Scope(())) | ||||||
|  |         } | ||||||
|  |         pub(crate) fn variant_access(&self) -> Interned<VariantAccess> { | ||||||
|  |             self.variant_access | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[derive(Clone)] | ||||||
|  |     pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>); | ||||||
|  | 
 | ||||||
|  |     impl<T: EnumType> EnumMatchVariantsIterImpl<T> { | ||||||
|  |         pub(crate) fn for_variant_index( | ||||||
|  |             &self, | ||||||
|  |             _variant_index: usize, | ||||||
|  |         ) -> EnumMatchVariantAndInactiveScopeImpl<T> { | ||||||
|  |             todo!() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,10 +3,13 @@ | ||||||
| 
 | 
 | ||||||
| mod const_bool; | mod const_bool; | ||||||
| mod const_cmp; | mod const_cmp; | ||||||
|  | mod const_usize; | ||||||
| mod misc; | mod misc; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||||
|  | #[doc(inline)] | ||||||
|  | pub use const_usize::{ConstUsize, GenericConstUsize}; | ||||||
| 
 | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use const_cmp::{ | pub use const_cmp::{ | ||||||
|  |  | ||||||
							
								
								
									
										29
									
								
								crates/fayalite/src/util/const_usize.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								crates/fayalite/src/util/const_usize.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
|  | // See Notices.txt for copyright information
 | ||||||
|  | use std::{fmt::Debug, hash::Hash}; | ||||||
|  | 
 | ||||||
|  | mod sealed { | ||||||
|  |     pub trait Sealed {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// the only implementation is `ConstUsize<Self::VALUE>`
 | ||||||
|  | pub trait GenericConstUsize: | ||||||
|  |     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||||
|  | { | ||||||
|  |     const VALUE: usize; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||||
|  | pub struct ConstUsize<const VALUE: usize>; | ||||||
|  | 
 | ||||||
|  | impl<const VALUE: usize> Debug for ConstUsize<VALUE> { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         f.debug_tuple("ConstUsize").field(&Self::VALUE).finish() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {} | ||||||
|  | 
 | ||||||
|  | impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> { | ||||||
|  |     const VALUE: usize = VALUE; | ||||||
|  | } | ||||||
|  | @ -1,32 +1,21 @@ | ||||||
| // 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::{hdl, int::UInt, ty::Type}; | ||||||
|     int::UInt, |  | ||||||
|     ty::{StaticValue, Value}, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] |  | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated)] | ||||||
| pub struct S<T> { | pub struct S<T: Type> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|     b: UInt<3>, |     b: UInt<3>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] |  | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated)] | ||||||
| pub enum E<T> { | pub enum E<T: Type> { | ||||||
|     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: Type> { | ||||||
| pub struct S2<T> { |  | ||||||
|     pub v: E<T>, |     pub v: E<T>, | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue