forked from libre-chip/fayalite
		
	Compare commits
	
		
			No commits in common. "c0c5b550bc1ca2ddbb2917084211cf420091df75" and "86a1bb46be4690e854470cf4a1a6e90c92268391" have entirely different histories.
		
	
	
		
			c0c5b550bc
			...
			86a1bb46be
		
	
		
					 28 changed files with 70 additions and 1908 deletions
				
			
		|  | @ -12,10 +12,10 @@ jobs: | ||||||
|     outputs: |     outputs: | ||||||
|       cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} |       cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} | ||||||
|     steps: |     steps: | ||||||
|       - uses: https://git.libre-chip.org/mirrors/checkout@v3 |       - uses: https://code.forgejo.org/actions/checkout@v3 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 |       - uses: https://code.forgejo.org/actions/cache/restore@v3 | ||||||
|         id: restore-deps |         id: restore-deps | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|  | @ -58,19 +58,19 @@ jobs: | ||||||
|       - name: Get SymbiYosys |       - name: Get SymbiYosys | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         run: | | ||||||
|           git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby |           git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby | ||||||
|       - name: Build Z3 |       - name: Build Z3 | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         run: | | ||||||
|           git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3 |           git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3 | ||||||
|           (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) |           (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) | ||||||
|           make -C deps/z3/build -j"$(nproc)" |           make -C deps/z3/build -j"$(nproc)" | ||||||
|       - name: Build Yosys |       - name: Build Yosys | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         run: | | ||||||
|           git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys |           git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys | ||||||
|           make -C deps/yosys -j"$(nproc)" |           make -C deps/yosys -j"$(nproc)" | ||||||
|       - uses: https://git.libre-chip.org/mirrors/cache/save@v3 |       - uses: https://code.forgejo.org/actions/cache/save@v3 | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ jobs: | ||||||
|     runs-on: debian-12 |     runs-on: debian-12 | ||||||
|     needs: deps |     needs: deps | ||||||
|     steps: |     steps: | ||||||
|       - uses: https://git.libre-chip.org/mirrors/checkout@v3 |       - uses: https://code.forgejo.org/actions/checkout@v3 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - run: | |       - run: | | ||||||
|  | @ -41,7 +41,7 @@ jobs: | ||||||
|           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 |           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 | ||||||
|           source "$HOME/.cargo/env" |           source "$HOME/.cargo/env" | ||||||
|           echo "$PATH" >> "$GITHUB_PATH" |           echo "$PATH" >> "$GITHUB_PATH" | ||||||
|       - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 |       - uses: https://code.forgejo.org/actions/cache/restore@v3 | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|           key: ${{ needs.deps.outputs.cache-primary-key }} |           key: ${{ needs.deps.outputs.cache-primary-key }} | ||||||
|  | @ -52,7 +52,7 @@ jobs: | ||||||
|           make -C deps/yosys install |           make -C deps/yosys install | ||||||
|           export PATH="$(realpath deps/firtool/bin):$PATH" |           export PATH="$(realpath deps/firtool/bin):$PATH" | ||||||
|           echo "$PATH" >> "$GITHUB_PATH" |           echo "$PATH" >> "$GITHUB_PATH" | ||||||
|       - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 |       - uses: https://github.com/Swatinem/rust-cache@v2 | ||||||
|         with: |         with: | ||||||
|           save-if: ${{ github.ref == 'refs/heads/master' }} |           save-if: ${{ github.ref == 'refs/heads/master' }} | ||||||
|       - run: cargo test |       - run: cargo test | ||||||
|  |  | ||||||
|  | @ -83,7 +83,6 @@ impl ParsedBundle { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|             cmp_eq: _, |  | ||||||
|         } = options.body; |         } = options.body; | ||||||
|         let mut fields = match fields { |         let mut fields = match fields { | ||||||
|             syn::Fields::Named(fields) => fields, |             syn::Fields::Named(fields) => fields, | ||||||
|  | @ -438,7 +437,6 @@ impl ToTokens for ParsedBundle { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|             cmp_eq, |  | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut item_attrs = attrs.clone(); |         let mut item_attrs = attrs.clone(); | ||||||
|  | @ -767,69 +765,6 @@ impl ToTokens for ParsedBundle { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         .to_tokens(tokens); |         .to_tokens(tokens); | ||||||
|         if let Some((cmp_eq,)) = cmp_eq { |  | ||||||
|             let mut where_clause = |  | ||||||
|                 Generics::from(generics) |  | ||||||
|                     .where_clause |  | ||||||
|                     .unwrap_or_else(|| syn::WhereClause { |  | ||||||
|                         where_token: Token, |  | ||||||
|                         predicates: Punctuated::new(), |  | ||||||
|                     }); |  | ||||||
|             let mut fields_cmp_eq = vec![]; |  | ||||||
|             let mut fields_cmp_ne = vec![]; |  | ||||||
|             for field in fields.named() { |  | ||||||
|                 let field_ident = field.ident(); |  | ||||||
|                 let field_ty = field.ty(); |  | ||||||
|                 where_clause |  | ||||||
|                     .predicates |  | ||||||
|                     .push(parse_quote_spanned! {cmp_eq.span=> |  | ||||||
|                         #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> |  | ||||||
|                     }); |  | ||||||
|                 fields_cmp_eq.push(quote_spanned! {span=> |  | ||||||
|                     ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) |  | ||||||
|                 }); |  | ||||||
|                 fields_cmp_ne.push(quote_spanned! {span=> |  | ||||||
|                     ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             let cmp_eq_body; |  | ||||||
|             let cmp_ne_body; |  | ||||||
|             if fields_len == 0 { |  | ||||||
|                 cmp_eq_body = quote_spanned! {span=> |  | ||||||
|                     ::fayalite::expr::ToExpr::to_expr(&true) |  | ||||||
|                 }; |  | ||||||
|                 cmp_ne_body = quote_spanned! {span=> |  | ||||||
|                     ::fayalite::expr::ToExpr::to_expr(&false) |  | ||||||
|                 }; |  | ||||||
|             } else { |  | ||||||
|                 cmp_eq_body = quote_spanned! {span=> |  | ||||||
|                     #(#fields_cmp_eq)&* |  | ||||||
|                 }; |  | ||||||
|                 cmp_ne_body = quote_spanned! {span=> |  | ||||||
|                     #(#fields_cmp_ne)|* |  | ||||||
|                 }; |  | ||||||
|             }; |  | ||||||
|             quote_spanned! {span=> |  | ||||||
|                 #[automatically_derived] |  | ||||||
|                 impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics |  | ||||||
|                 #where_clause |  | ||||||
|                 { |  | ||||||
|                     fn cmp_eq( |  | ||||||
|                         __lhs: ::fayalite::expr::Expr<Self>, |  | ||||||
|                         __rhs: ::fayalite::expr::Expr<Self>, |  | ||||||
|                     ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { |  | ||||||
|                         #cmp_eq_body |  | ||||||
|                     } |  | ||||||
|                     fn cmp_ne( |  | ||||||
|                         __lhs: ::fayalite::expr::Expr<Self>, |  | ||||||
|                         __rhs: ::fayalite::expr::Expr<Self>, |  | ||||||
|                     ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { |  | ||||||
|                         #cmp_ne_body |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .to_tokens(tokens); |  | ||||||
|         } |  | ||||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { |         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||||
|             let static_generics = generics.clone().for_static_type(); |             let static_generics = generics.clone().for_static_type(); | ||||||
|             let (static_impl_generics, static_type_generics, static_where_clause) = |             let (static_impl_generics, static_type_generics, static_where_clause) = | ||||||
|  |  | ||||||
|  | @ -155,11 +155,7 @@ impl ParsedEnum { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|             cmp_eq, |  | ||||||
|         } = options.body; |         } = options.body; | ||||||
|         if let Some((cmp_eq,)) = cmp_eq { |  | ||||||
|             errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); |  | ||||||
|         } |  | ||||||
|         attrs.retain(|attr| { |         attrs.retain(|attr| { | ||||||
|             if attr.path().is_ident("repr") { |             if attr.path().is_ident("repr") { | ||||||
|                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); |                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); | ||||||
|  | @ -215,7 +211,6 @@ impl ToTokens for ParsedEnum { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|             cmp_eq: _, // TODO: implement cmp_eq for enums
 |  | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut struct_attrs = attrs.clone(); |         let mut struct_attrs = attrs.clone(); | ||||||
|  |  | ||||||
|  | @ -49,14 +49,10 @@ impl ParsedTypeAlias { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|             cmp_eq, |  | ||||||
|         } = options.body; |         } = options.body; | ||||||
|         if let Some((no_static,)) = no_static { |         if let Some((no_static,)) = no_static { | ||||||
|             errors.error(no_static, "no_static is not valid on type aliases"); |             errors.error(no_static, "no_static is not valid on type aliases"); | ||||||
|         } |         } | ||||||
|         if let Some((cmp_eq,)) = cmp_eq { |  | ||||||
|             errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); |  | ||||||
|         } |  | ||||||
|         let generics = if custom_bounds.is_some() { |         let generics = if custom_bounds.is_some() { | ||||||
|             MaybeParsed::Unrecognized(generics) |             MaybeParsed::Unrecognized(generics) | ||||||
|         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { |         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { | ||||||
|  | @ -99,7 +95,6 @@ impl ToTokens for ParsedTypeAlias { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|             cmp_eq: _, |  | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut type_attrs = attrs.clone(); |         let mut type_attrs = attrs.clone(); | ||||||
|  |  | ||||||
|  | @ -26,7 +26,6 @@ crate::options! { | ||||||
|         CustomBounds(custom_bounds), |         CustomBounds(custom_bounds), | ||||||
|         NoStatic(no_static), |         NoStatic(no_static), | ||||||
|         NoRuntimeGenerics(no_runtime_generics), |         NoRuntimeGenerics(no_runtime_generics), | ||||||
|         CmpEq(cmp_eq), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2070,16 +2069,11 @@ macro_rules! impl_bounds { | ||||||
|             $( |             $( | ||||||
|                 $Variant:ident, |                 $Variant:ident, | ||||||
|             )* |             )* | ||||||
|             $( |  | ||||||
|                 #[unknown] |  | ||||||
|                 $Unknown:ident, |  | ||||||
|             )? |  | ||||||
|         } |         } | ||||||
|     ) => { |     ) => { | ||||||
|         #[derive(Clone, Debug)] |         #[derive(Clone, Debug)] | ||||||
|         $vis enum $enum_type { |         $vis enum $enum_type { | ||||||
|             $($Variant(known_items::$Variant),)* |             $($Variant(known_items::$Variant),)* | ||||||
|             $($Unknown(syn::TypeParamBound),)? |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $(impl From<known_items::$Variant> for $enum_type { |         $(impl From<known_items::$Variant> for $enum_type { | ||||||
|  | @ -2092,54 +2086,28 @@ macro_rules! impl_bounds { | ||||||
|             fn to_tokens(&self, tokens: &mut TokenStream) { |             fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|                 match self { |                 match self { | ||||||
|                     $(Self::$Variant(v) => v.to_tokens(tokens),)* |                     $(Self::$Variant(v) => v.to_tokens(tokens),)* | ||||||
|                     $(Self::$Unknown(v) => v.to_tokens(tokens),)? |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl $enum_type { |         impl $enum_type { | ||||||
|             $vis fn parse_path(path: Path) -> Result<Self, Path> { |             $vis fn parse_path(path: Path) -> Result<Self, Path> { | ||||||
|                 #![allow(unreachable_code)] |  | ||||||
|                 $(let path = match known_items::$Variant::parse_path(path) { |                 $(let path = match known_items::$Variant::parse_path(path) { | ||||||
|                     Ok(v) => return Ok(Self::$Variant(v)), |                     Ok(v) => return Ok(Self::$Variant(v)), | ||||||
|                     Err(path) => path, |                     Err(path) => path, | ||||||
|                 };)* |                 };)* | ||||||
|                 $(return Ok(Self::$Unknown(syn::TraitBound { |  | ||||||
|                     paren_token: None, |  | ||||||
|                     modifier: syn::TraitBoundModifier::None, |  | ||||||
|                     lifetimes: None, |  | ||||||
|                     path, |  | ||||||
|                 }.into()));)? |  | ||||||
|                 Err(path) |                 Err(path) | ||||||
|             } |             } | ||||||
|             $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> { |  | ||||||
|                 #![allow(unreachable_code)] |  | ||||||
|                 if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { |  | ||||||
|                     if let syn::TraitBound { |  | ||||||
|                         paren_token: _, |  | ||||||
|                         modifier: syn::TraitBoundModifier::None, |  | ||||||
|                         lifetimes: None, |  | ||||||
|                         path: _, |  | ||||||
|                     } = trait_bound { |  | ||||||
|                         match Self::parse_path(trait_bound.path) { |  | ||||||
|                             Ok(retval) => return Ok(retval), |  | ||||||
|                             Err(path) => trait_bound.path = path, |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     type_param_bound = trait_bound.into(); |  | ||||||
|                 } |  | ||||||
|                 $(return Ok(Self::$Unknown(type_param_bound));)? |  | ||||||
|                 Err(type_param_bound) |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl Parse for $enum_type { |         impl Parse for $enum_type { | ||||||
|             fn parse(input: ParseStream) -> syn::Result<Self> { |             fn parse(input: ParseStream) -> syn::Result<Self> { | ||||||
|                 Self::parse_type_param_bound(input.parse()?) |                 Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { | ||||||
|                     .map_err(|type_param_bound| syn::Error::new_spanned( |                     syn::Error::new_spanned( | ||||||
|                         type_param_bound, |                         path, | ||||||
|                         format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), |                         format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), | ||||||
|                     )) |                     ) | ||||||
|  |                 }) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -2147,7 +2115,6 @@ macro_rules! impl_bounds { | ||||||
|         #[allow(non_snake_case)] |         #[allow(non_snake_case)] | ||||||
|         $vis struct $struct_type { |         $vis struct $struct_type { | ||||||
|             $($vis $Variant: Option<known_items::$Variant>,)* |             $($vis $Variant: Option<known_items::$Variant>,)* | ||||||
|             $($vis $Unknown: Vec<syn::TypeParamBound>,)? |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl ToTokens for $struct_type { |         impl ToTokens for $struct_type { | ||||||
|  | @ -2159,63 +2126,42 @@ macro_rules! impl_bounds { | ||||||
|                     separator = Some(<Token![+]>::default()); |                     separator = Some(<Token![+]>::default()); | ||||||
|                     v.to_tokens(tokens); |                     v.to_tokens(tokens); | ||||||
|                 })* |                 })* | ||||||
|                 $(for v in &self.$Unknown { |  | ||||||
|                     separator.to_tokens(tokens); |  | ||||||
|                     separator = Some(<Token![+]>::default()); |  | ||||||
|                     v.to_tokens(tokens); |  | ||||||
|                 })* |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const _: () = { |         const _: () = { | ||||||
|             #[derive(Clone, Debug)] |             #[derive(Clone, Debug)] | ||||||
|             #[allow(non_snake_case)] |             $vis struct Iter($vis $struct_type); | ||||||
|             $vis struct Iter { |  | ||||||
|                 $($Variant: Option<known_items::$Variant>,)* |  | ||||||
|                 $($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)? |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             impl IntoIterator for $struct_type { |             impl IntoIterator for $struct_type { | ||||||
|                 type Item = $enum_type; |                 type Item = $enum_type; | ||||||
|                 type IntoIter = Iter; |                 type IntoIter = Iter; | ||||||
| 
 | 
 | ||||||
|                 fn into_iter(self) -> Self::IntoIter { |                 fn into_iter(self) -> Self::IntoIter { | ||||||
|                     Iter { |                     Iter(self) | ||||||
|                         $($Variant: self.$Variant,)* |  | ||||||
|                         $($Unknown: self.$Unknown.into_iter(),)? |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             impl Iterator for Iter { |             impl Iterator for Iter { | ||||||
|                 type Item = $enum_type; |                 type Item = $enum_type; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|                 fn next(&mut self) -> Option<Self::Item> { |                 fn next(&mut self) -> Option<Self::Item> { | ||||||
|                     $( |                     $( | ||||||
|                         if let Some(value) = self.$Variant.take() { |                         if let Some(value) = self.0.$Variant.take() { | ||||||
|                             return Some($enum_type::$Variant(value)); |                             return Some($enum_type::$Variant(value)); | ||||||
|                         } |                         } | ||||||
|                     )* |                     )* | ||||||
|                     $( |  | ||||||
|                         if let Some(value) = self.$Unknown.next() { |  | ||||||
|                             return Some($enum_type::$Unknown(value)); |  | ||||||
|                         } |  | ||||||
|                     )? |  | ||||||
|                     None |                     None | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 #[allow(unused_mut, unused_variables)] |                 #[allow(unused_mut, unused_variables)] | ||||||
|                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { |                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { | ||||||
|                     $( |                     $( | ||||||
|                         if let Some(value) = self.$Variant.take() { |                         if let Some(value) = self.0.$Variant.take() { | ||||||
|                             init = f(init, $enum_type::$Variant(value)); |                             init = f(init, $enum_type::$Variant(value)); | ||||||
|                         } |                         } | ||||||
|                     )* |                     )* | ||||||
|                     $( |  | ||||||
|                         if let Some(value) = self.$Unknown.next() { |  | ||||||
|                             init = f(init, $enum_type::$Unknown(value)); |  | ||||||
|                         } |  | ||||||
|                     )? |  | ||||||
|                     init |                     init | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -2227,9 +2173,6 @@ macro_rules! impl_bounds { | ||||||
|                     $($enum_type::$Variant(v) => { |                     $($enum_type::$Variant(v) => { | ||||||
|                         self.$Variant = Some(v); |                         self.$Variant = Some(v); | ||||||
|                     })* |                     })* | ||||||
|                     $($enum_type::$Unknown(v) => { |  | ||||||
|                         self.$Unknown.push(v); |  | ||||||
|                     })? |  | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -2248,7 +2191,6 @@ macro_rules! impl_bounds { | ||||||
|                     $(if let Some(v) = v.$Variant { |                     $(if let Some(v) = v.$Variant { | ||||||
|                         self.$Variant = Some(v); |                         self.$Variant = Some(v); | ||||||
|                     })* |                     })* | ||||||
|                     $(self.$Unknown.extend(v.$Unknown);)* |  | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -2302,8 +2244,6 @@ impl_bounds! { | ||||||
|         Size, |         Size, | ||||||
|         StaticType, |         StaticType, | ||||||
|         Type, |         Type, | ||||||
|         #[unknown] |  | ||||||
|         Unknown, |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2317,8 +2257,6 @@ impl_bounds! { | ||||||
|         ResetType, |         ResetType, | ||||||
|         StaticType, |         StaticType, | ||||||
|         Type, |         Type, | ||||||
|         #[unknown] |  | ||||||
|         Unknown, |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2332,7 +2270,6 @@ impl From<ParsedTypeBound> for ParsedBound { | ||||||
|             ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), |             ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), | ||||||
|             ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), |             ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), | ||||||
|             ParsedTypeBound::Type(v) => ParsedBound::Type(v), |             ParsedTypeBound::Type(v) => ParsedBound::Type(v), | ||||||
|             ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2347,7 +2284,6 @@ impl From<ParsedTypeBounds> for ParsedBounds { | ||||||
|             ResetType, |             ResetType, | ||||||
|             StaticType, |             StaticType, | ||||||
|             Type, |             Type, | ||||||
|             Unknown, |  | ||||||
|         } = value; |         } = value; | ||||||
|         Self { |         Self { | ||||||
|             BoolOrIntType, |             BoolOrIntType, | ||||||
|  | @ -2359,7 +2295,6 @@ impl From<ParsedTypeBounds> for ParsedBounds { | ||||||
|             Size: None, |             Size: None, | ||||||
|             StaticType, |             StaticType, | ||||||
|             Type, |             Type, | ||||||
|             Unknown, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2395,7 +2330,6 @@ impl ParsedTypeBound { | ||||||
|                 ParsedTypeBound::Type(known_items::Type(span)), |                 ParsedTypeBound::Type(known_items::Type(span)), | ||||||
|             ]), |             ]), | ||||||
|             Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), |             Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), | ||||||
|             Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2430,7 +2364,6 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds { | ||||||
|             Size, |             Size, | ||||||
|             StaticType: None, |             StaticType: None, | ||||||
|             Type: None, |             Type: None, | ||||||
|             Unknown: vec![], |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2458,7 +2391,6 @@ impl ParsedBounds { | ||||||
|     fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { |     fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { | ||||||
|         let mut type_bounds = None; |         let mut type_bounds = None; | ||||||
|         let mut size_type_bounds = None; |         let mut size_type_bounds = None; | ||||||
|         let mut unknown_bounds = vec![]; |  | ||||||
|         self.into_iter().for_each(|bound| match bound.categorize() { |         self.into_iter().for_each(|bound| match bound.categorize() { | ||||||
|             ParsedBoundCategory::Type(bound) => { |             ParsedBoundCategory::Type(bound) => { | ||||||
|                 type_bounds |                 type_bounds | ||||||
|  | @ -2470,37 +2402,15 @@ impl ParsedBounds { | ||||||
|                     .get_or_insert_with(ParsedSizeTypeBounds::default) |                     .get_or_insert_with(ParsedSizeTypeBounds::default) | ||||||
|                     .extend([bound]); |                     .extend([bound]); | ||||||
|             } |             } | ||||||
|             ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), |  | ||||||
|         }); |         }); | ||||||
|         match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { |         match (type_bounds, size_type_bounds) { | ||||||
|             (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { |             (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||||
|                 Type: Some(known_items::Type(span)), |                 Type: Some(known_items::Type(span)), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|             }), |             }), | ||||||
|             (None, None, false) => { |             (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), | ||||||
|                 errors.error( |             (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), | ||||||
|                     unknown_bounds.remove(0), |             (Some(type_bounds), Some(size_type_bounds)) => { | ||||||
|                     "unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds", |  | ||||||
|                 ); |  | ||||||
|                 ParsedBoundsCategory::Type(ParsedTypeBounds { |  | ||||||
|                     Unknown: unknown_bounds, |  | ||||||
|                     ..Default::default() |  | ||||||
|                 }) |  | ||||||
|             } |  | ||||||
|             (None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds), |  | ||||||
|             (None, Some(bounds), false) => { |  | ||||||
|                 // TODO: implement
 |  | ||||||
|                 errors.error( |  | ||||||
|                     unknown_bounds.remove(0), |  | ||||||
|                     "unknown bounds with `Size` bounds are not implemented", |  | ||||||
|                 ); |  | ||||||
|                 ParsedBoundsCategory::SizeType(bounds) |  | ||||||
|             } |  | ||||||
|             (Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds { |  | ||||||
|                 Unknown: unknown_bounds, |  | ||||||
|                 ..bounds |  | ||||||
|             }), |  | ||||||
|             (Some(type_bounds), Some(size_type_bounds), _) => { |  | ||||||
|                 errors.error( |                 errors.error( | ||||||
|                     size_type_bounds |                     size_type_bounds | ||||||
|                         .Size |                         .Size | ||||||
|  | @ -2517,7 +2427,6 @@ impl ParsedBounds { | ||||||
| pub(crate) enum ParsedBoundCategory { | pub(crate) enum ParsedBoundCategory { | ||||||
|     Type(ParsedTypeBound), |     Type(ParsedTypeBound), | ||||||
|     SizeType(ParsedSizeTypeBound), |     SizeType(ParsedSizeTypeBound), | ||||||
|     Unknown(syn::TypeParamBound), |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ParsedBound { | impl ParsedBound { | ||||||
|  | @ -2532,14 +2441,12 @@ impl ParsedBound { | ||||||
|             Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), |             Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), | ||||||
|             Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), |             Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), | ||||||
|             Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), |             Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), | ||||||
|             Self::Unknown(v) => ParsedBoundCategory::Unknown(v), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn implied_bounds(self) -> ParsedBounds { |     fn implied_bounds(self) -> ParsedBounds { | ||||||
|         match self.categorize() { |         match self.categorize() { | ||||||
|             ParsedBoundCategory::Type(v) => v.implied_bounds().into(), |             ParsedBoundCategory::Type(v) => v.implied_bounds().into(), | ||||||
|             ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), |             ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), | ||||||
|             ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -3418,7 +3325,7 @@ impl ParsedGenerics { | ||||||
|                                 | ParsedTypeBound::EnumType(_) |                                 | ParsedTypeBound::EnumType(_) | ||||||
|                                 | ParsedTypeBound::IntType(_) |                                 | ParsedTypeBound::IntType(_) | ||||||
|                                 | ParsedTypeBound::ResetType(_) => { |                                 | ParsedTypeBound::ResetType(_) => { | ||||||
|                                     errors.error(bound, "bounds on mask types are not implemented"); |                                     errors.error(bound, "bound on mask type not implemented"); | ||||||
|                                 } |                                 } | ||||||
|                                 ParsedTypeBound::StaticType(bound) => { |                                 ParsedTypeBound::StaticType(bound) => { | ||||||
|                                     if bounds.StaticType.is_none() { |                                     if bounds.StaticType.is_none() { | ||||||
|  | @ -3430,12 +3337,6 @@ impl ParsedGenerics { | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                                 ParsedTypeBound::Type(_) => {} |                                 ParsedTypeBound::Type(_) => {} | ||||||
|                                 ParsedTypeBound::Unknown(_) => { |  | ||||||
|                                     errors.error( |  | ||||||
|                                         bound, |  | ||||||
|                                         "unknown bounds on mask types are not implemented", |  | ||||||
|                                     ); |  | ||||||
|                                 } |  | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         bounds.add_implied_bounds(); |                         bounds.add_implied_bounds(); | ||||||
|  |  | ||||||
|  | @ -72,14 +72,13 @@ mod kw { | ||||||
|     custom_keyword!(cfg); |     custom_keyword!(cfg); | ||||||
|     custom_keyword!(cfg_attr); |     custom_keyword!(cfg_attr); | ||||||
|     custom_keyword!(clock_domain); |     custom_keyword!(clock_domain); | ||||||
|     custom_keyword!(cmp_eq); |  | ||||||
|     custom_keyword!(connect_inexact); |     custom_keyword!(connect_inexact); | ||||||
|     custom_keyword!(custom_bounds); |     custom_keyword!(custom_bounds); | ||||||
|     custom_keyword!(flip); |     custom_keyword!(flip); | ||||||
|     custom_keyword!(hdl); |     custom_keyword!(hdl); | ||||||
|     custom_keyword!(hdl_module); |     custom_keyword!(hdl_module); | ||||||
|     custom_keyword!(incomplete_wire); |  | ||||||
|     custom_keyword!(input); |     custom_keyword!(input); | ||||||
|  |     custom_keyword!(incomplete_wire); | ||||||
|     custom_keyword!(instance); |     custom_keyword!(instance); | ||||||
|     custom_keyword!(m); |     custom_keyword!(m); | ||||||
|     custom_keyword!(memory); |     custom_keyword!(memory); | ||||||
|  |  | ||||||
|  | @ -2,11 +2,8 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ |     expr::{ops::ArrayIndex, Expr, ToExpr}, | ||||||
|         ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, |     int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, | ||||||
|         CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, |  | ||||||
|     }, |  | ||||||
|     int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, |  | ||||||
|     intern::{Intern, Interned, LazyInterned}, |     intern::{Intern, Interned, LazyInterned}, | ||||||
|     module::transform::visit::{Fold, Folder, Visit, Visitor}, |     module::transform::visit::{Fold, Folder, Visit, Visitor}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|  | @ -15,7 +12,7 @@ use crate::{ | ||||||
|     }, |     }, | ||||||
|     util::ConstUsize, |     util::ConstUsize, | ||||||
| }; | }; | ||||||
| use std::{iter::FusedIterator, ops::Index}; | use std::ops::Index; | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | ||||||
|  | @ -151,8 +148,10 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|         this: Expr<Self>, |         this: Expr<Self>, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Self::MatchVariantsIter { |     ) -> Self::MatchVariantsIter { | ||||||
|  |         let base = Expr::as_dyn_array(this); | ||||||
|  |         let base_ty = Expr::ty(base); | ||||||
|         let _ = source_location; |         let _ = source_location; | ||||||
|         let retval = Vec::from_iter(this); |         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||||
|         std::iter::once(MatchVariantWithoutScope( |         std::iter::once(MatchVariantWithoutScope( | ||||||
|             Len::ArrayMatch::<T>::try_from(retval) |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|                 .ok() |                 .ok() | ||||||
|  | @ -185,7 +184,9 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
| 
 | 
 | ||||||
| impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||||
|     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { |     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { | ||||||
|         let retval = Vec::from_iter(*this); |         let base = Expr::as_dyn_array(*this); | ||||||
|  |         let base_ty = Expr::ty(base); | ||||||
|  |         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||||
|         Interned::into_inner(Intern::intern_sized( |         Interned::into_inner(Intern::intern_sized( | ||||||
|             Len::ArrayMatch::<T>::try_from(retval) |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|                 .ok() |                 .ok() | ||||||
|  | @ -217,131 +218,3 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> { | ||||||
|         Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) |         Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len> |  | ||||||
| where |  | ||||||
|     Lhs: ExprPartialEq<Rhs>, |  | ||||||
| { |  | ||||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> { |  | ||||||
|         let lhs_ty = Expr::ty(lhs); |  | ||||||
|         let rhs_ty = Expr::ty(rhs); |  | ||||||
|         assert_eq!(lhs_ty.len(), rhs_ty.len()); |  | ||||||
|         lhs.into_iter() |  | ||||||
|             .zip(rhs) |  | ||||||
|             .map(|(l, r)| l.cmp_eq(r)) |  | ||||||
|             .collect::<Expr<Array<Bool>>>() |  | ||||||
|             .cast_to_bits() |  | ||||||
|             .all_one_bits() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> { |  | ||||||
|         let lhs_ty = Expr::ty(lhs); |  | ||||||
|         let rhs_ty = Expr::ty(rhs); |  | ||||||
|         assert_eq!(lhs_ty.len(), rhs_ty.len()); |  | ||||||
|         lhs.into_iter() |  | ||||||
|             .zip(rhs) |  | ||||||
|             .map(|(l, r)| l.cmp_ne(r)) |  | ||||||
|             .collect::<Expr<Array<Bool>>>() |  | ||||||
|             .cast_to_bits() |  | ||||||
|             .any_one_bits() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> { |  | ||||||
|     type Item = T; |  | ||||||
|     type ExprIntoIter = ExprArrayIter<T, Len>; |  | ||||||
| 
 |  | ||||||
|     fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter { |  | ||||||
|         ExprArrayIter { |  | ||||||
|             base: e, |  | ||||||
|             indexes: 0..Expr::ty(e).len(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct ExprArrayIter<T: Type, Len: Size> { |  | ||||||
|     base: Expr<ArrayType<T, Len>>, |  | ||||||
|     indexes: std::ops::Range<usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> ExprArrayIter<T, Len> { |  | ||||||
|     pub fn base(&self) -> Expr<ArrayType<T, Len>> { |  | ||||||
|         self.base |  | ||||||
|     } |  | ||||||
|     pub fn indexes(&self) -> std::ops::Range<usize> { |  | ||||||
|         self.indexes.clone() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> Iterator for ExprArrayIter<T, Len> { |  | ||||||
|     type Item = Expr<T>; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         self.indexes.next().map(|i| self.base[i]) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn size_hint(&self) -> (usize, Option<usize>) { |  | ||||||
|         self.indexes.size_hint() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn count(self) -> usize { |  | ||||||
|         self.indexes.count() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn last(mut self) -> Option<Self::Item> { |  | ||||||
|         self.next_back() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn nth(&mut self, n: usize) -> Option<Self::Item> { |  | ||||||
|         self.indexes.nth(n).map(|i| self.base[i]) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn fold<B, F>(self, init: B, mut f: F) -> B |  | ||||||
|     where |  | ||||||
|         F: FnMut(B, Self::Item) -> B, |  | ||||||
|     { |  | ||||||
|         self.indexes.fold(init, |b, i| f(b, self.base[i])) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> DoubleEndedIterator for ExprArrayIter<T, Len> { |  | ||||||
|     fn next_back(&mut self) -> Option<Self::Item> { |  | ||||||
|         self.indexes.next_back().map(|i| self.base[i]) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn nth_back(&mut self, n: usize) -> Option<Self::Item> { |  | ||||||
|         self.indexes.nth_back(n).map(|i| self.base[i]) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn rfold<B, F>(self, init: B, mut f: F) -> B |  | ||||||
|     where |  | ||||||
|         F: FnMut(B, Self::Item) -> B, |  | ||||||
|     { |  | ||||||
|         self.indexes.rfold(init, |b, i| f(b, self.base[i])) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> ExactSizeIterator for ExprArrayIter<T, Len> { |  | ||||||
|     fn len(&self) -> usize { |  | ||||||
|         self.indexes.len() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: Type, Len: Size> FusedIterator for ExprArrayIter<T, Len> {} |  | ||||||
| 
 |  | ||||||
| impl<A: StaticType> ExprFromIterator<Expr<A>> for Array<A> { |  | ||||||
|     fn expr_from_iter<T: IntoIterator<Item = Expr<A>>>(iter: T) -> Expr<Self> { |  | ||||||
|         ArrayLiteral::new( |  | ||||||
|             A::TYPE, |  | ||||||
|             iter.into_iter().map(|v| Expr::canonical(v)).collect(), |  | ||||||
|         ) |  | ||||||
|         .to_expr() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a, A: StaticType> ExprFromIterator<&'a Expr<A>> for Array<A> { |  | ||||||
|     fn expr_from_iter<T: IntoIterator<Item = &'a Expr<A>>>(iter: T) -> Expr<Self> { |  | ||||||
|         iter.into_iter().copied().collect() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -2,11 +2,7 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ |     expr::{ops::BundleLiteral, Expr, ToExpr}, | ||||||
|         ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, |  | ||||||
|         CastToBits, Expr, ReduceBits, ToExpr, |  | ||||||
|     }, |  | ||||||
|     int::{Bool, DynSize}, |  | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     sim::{SimValue, ToSimValue}, |     sim::{SimValue, ToSimValue}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|  | @ -329,19 +325,7 @@ macro_rules! impl_tuple_builder_fields { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| macro_rules! impl_tuples { | macro_rules! impl_tuples { | ||||||
|     ( |     ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { | ||||||
|         [$({ |  | ||||||
|             #[ |  | ||||||
|                 num = $num:tt, |  | ||||||
|                 field = $field:ident, |  | ||||||
|                 ty = $ty_var:ident: $Ty:ident, |  | ||||||
|                 lhs = $lhs_var:ident: $Lhs:ident, |  | ||||||
|                 rhs = $rhs_var:ident: $Rhs:ident |  | ||||||
|             ] |  | ||||||
|             $var:ident: $T:ident |  | ||||||
|         })*] |  | ||||||
|         [] |  | ||||||
|     ) => { |  | ||||||
|         impl_tuple_builder_fields! { |         impl_tuple_builder_fields! { | ||||||
|             {} |             {} | ||||||
|             [$({ |             [$({ | ||||||
|  | @ -514,29 +498,6 @@ macro_rules! impl_tuples { | ||||||
|                 Self::into_sim_value(*self, ty) |                 Self::into_sim_value(*self, ty) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { |  | ||||||
|             fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> { |  | ||||||
|                 let ($($lhs_var,)*) = *lhs; |  | ||||||
|                 let ($($rhs_var,)*) = *rhs; |  | ||||||
|                 ArrayLiteral::<Bool, DynSize>::new( |  | ||||||
|                     Bool, |  | ||||||
|                     FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), |  | ||||||
|                 ) |  | ||||||
|                 .cast_to_bits() |  | ||||||
|                 .all_one_bits() |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> { |  | ||||||
|                 let ($($lhs_var,)*) = *lhs; |  | ||||||
|                 let ($($rhs_var,)*) = *rhs; |  | ||||||
|                 ArrayLiteral::<Bool, DynSize>::new( |  | ||||||
|                     Bool, |  | ||||||
|                     FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), |  | ||||||
|                 ) |  | ||||||
|                 .cast_to_bits() |  | ||||||
|                 .any_one_bits() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
|     ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { |     ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { | ||||||
|         impl_tuples!([$($lhs)*] []); |         impl_tuples!([$($lhs)*] []); | ||||||
|  | @ -546,18 +507,18 @@ macro_rules! impl_tuples { | ||||||
| 
 | 
 | ||||||
| impl_tuples! { | impl_tuples! { | ||||||
|     [] [ |     [] [ | ||||||
|         {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} |         {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} | ||||||
|         {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} |         {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} | ||||||
|         {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} |         {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} | ||||||
|         {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} |         {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} | ||||||
|         {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} |         {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} | ||||||
|         {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} |         {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} | ||||||
|         {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} |         {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} | ||||||
|         {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} |         {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} | ||||||
|         {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} |         {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} | ||||||
|         {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} |         {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} | ||||||
|         {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} |         {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} | ||||||
|         {#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} |         {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,10 +2,7 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ |     expr::{ops::VariantAccess, Expr, ToExpr}, | ||||||
|         ops::{ExprPartialEq, VariantAccess}, |  | ||||||
|         Expr, ToExpr, |  | ||||||
|     }, |  | ||||||
|     hdl, |     hdl, | ||||||
|     int::Bool, |     int::Bool, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|  | @ -363,60 +360,6 @@ pub enum HdlOption<T: Type> { | ||||||
|     HdlSome(T), |     HdlSome(T), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> { |  | ||||||
|     #[hdl] |  | ||||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> { |  | ||||||
|         #[hdl] |  | ||||||
|         let cmp_eq = wire(); |  | ||||||
|         #[hdl] |  | ||||||
|         match lhs { |  | ||||||
|             HdlSome(lhs) => |  | ||||||
|             { |  | ||||||
|                 #[hdl] |  | ||||||
|                 match rhs { |  | ||||||
|                     HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), |  | ||||||
|                     HdlNone => connect(cmp_eq, false), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             HdlNone => |  | ||||||
|             { |  | ||||||
|                 #[hdl] |  | ||||||
|                 match rhs { |  | ||||||
|                     HdlSome(_) => connect(cmp_eq, false), |  | ||||||
|                     HdlNone => connect(cmp_eq, true), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         cmp_eq |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> { |  | ||||||
|         #[hdl] |  | ||||||
|         let cmp_ne = wire(); |  | ||||||
|         #[hdl] |  | ||||||
|         match lhs { |  | ||||||
|             HdlSome(lhs) => |  | ||||||
|             { |  | ||||||
|                 #[hdl] |  | ||||||
|                 match rhs { |  | ||||||
|                     HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), |  | ||||||
|                     HdlNone => connect(cmp_ne, true), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             HdlNone => |  | ||||||
|             { |  | ||||||
|                 #[hdl] |  | ||||||
|                 match rhs { |  | ||||||
|                     HdlSome(_) => connect(cmp_ne, true), |  | ||||||
|                     HdlNone => connect(cmp_ne, false), |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         cmp_ne |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[allow(non_snake_case)] | #[allow(non_snake_case)] | ||||||
| pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { | pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { | ||||||
|     HdlOption[T::TYPE].HdlNone() |     HdlOption[T::TYPE].HdlNone() | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ use crate::{ | ||||||
|         transform::visit::{Fold, Folder, Visit, Visitor}, |         transform::visit::{Fold, Folder, Visit, Visitor}, | ||||||
|         Instance, ModuleIO, |         Instance, ModuleIO, | ||||||
|     }, |     }, | ||||||
|     phantom_const::PhantomConst, |  | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, |     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, | ||||||
|     ty::{CanonicalType, StaticType, Type, TypeWithDeref}, |     ty::{CanonicalType, StaticType, Type, TypeWithDeref}, | ||||||
|  | @ -110,7 +109,6 @@ expr_enum! { | ||||||
|         UIntLiteral(Interned<UIntValue>), |         UIntLiteral(Interned<UIntValue>), | ||||||
|         SIntLiteral(Interned<SIntValue>), |         SIntLiteral(Interned<SIntValue>), | ||||||
|         BoolLiteral(bool), |         BoolLiteral(bool), | ||||||
|         PhantomConst(PhantomConst), |  | ||||||
|         BundleLiteral(ops::BundleLiteral), |         BundleLiteral(ops::BundleLiteral), | ||||||
|         ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), |         ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), | ||||||
|         EnumLiteral(ops::EnumLiteral), |         EnumLiteral(ops::EnumLiteral), | ||||||
|  | @ -757,27 +755,3 @@ pub fn repeat<T: Type, L: SizeType>( | ||||||
|     ) |     ) | ||||||
|     .to_expr() |     .to_expr() | ||||||
| } | } | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToExpr for PhantomConst<T> { |  | ||||||
|     type Type = Self; |  | ||||||
| 
 |  | ||||||
|     fn to_expr(&self) -> Expr<Self::Type> { |  | ||||||
|         Expr { |  | ||||||
|             __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), |  | ||||||
|             __ty: *self, |  | ||||||
|             __flow: Flow::Source, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> GetTarget for PhantomConst<T> { |  | ||||||
|     fn target(&self) -> Option<Interned<Target>> { |  | ||||||
|         None |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToLiteralBits for PhantomConst<T> { |  | ||||||
|     fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> { |  | ||||||
|         Ok(Interned::default()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -11,15 +11,14 @@ use crate::{ | ||||||
|             GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, |             GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, | ||||||
|             TargetPathDynArrayElement, TargetPathElement, |             TargetPathDynArrayElement, TargetPathElement, | ||||||
|         }, |         }, | ||||||
|         CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, |         CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, | ||||||
|         HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, |         ToExpr, ToLiteralBits, | ||||||
|     }, |     }, | ||||||
|     int::{ |     int::{ | ||||||
|         Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, |         Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, | ||||||
|         UIntType, UIntValue, |         UIntType, UIntValue, | ||||||
|     }, |     }, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     phantom_const::{PhantomConst, PhantomConstValue}, |  | ||||||
|     reset::{ |     reset::{ | ||||||
|         AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, |         AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, | ||||||
|         ToSyncReset, |         ToSyncReset, | ||||||
|  | @ -1893,26 +1892,6 @@ impl ExprCastTo<Clock> for Clock { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: ?Sized + PhantomConstValue> ExprCastTo<()> for PhantomConst<T> { |  | ||||||
|     fn cast_to(src: Expr<Self>, to_type: ()) -> Expr<()> { |  | ||||||
|         src.cast_to_bits().cast_bits_to(to_type) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> for () { |  | ||||||
|     fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> { |  | ||||||
|         src.cast_to_bits().cast_bits_to(to_type) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue, U: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> |  | ||||||
|     for PhantomConst<U> |  | ||||||
| { |  | ||||||
|     fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> { |  | ||||||
|         src.cast_to_bits().cast_bits_to(to_type) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct FieldAccess<FieldType: Type = CanonicalType> { | pub struct FieldAccess<FieldType: Type = CanonicalType> { | ||||||
|     base: Expr<Bundle>, |     base: Expr<Bundle>, | ||||||
|  | @ -2729,47 +2708,3 @@ impl<T: Type> ToExpr for Uninit<T> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| pub trait ExprIntoIterator: Type { |  | ||||||
|     type Item: Type; |  | ||||||
|     type ExprIntoIter: Iterator<Item = Expr<Self::Item>>; |  | ||||||
| 
 |  | ||||||
|     fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ExprIntoIterator> IntoIterator for Expr<T> { |  | ||||||
|     type Item = Expr<T::Item>; |  | ||||||
|     type IntoIter = T::ExprIntoIter; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         T::expr_into_iter(self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ExprIntoIterator> IntoIterator for &'_ Expr<T> { |  | ||||||
|     type Item = Expr<T::Item>; |  | ||||||
|     type IntoIter = T::ExprIntoIter; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         T::expr_into_iter(*self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ExprIntoIterator> IntoIterator for &'_ mut Expr<T> { |  | ||||||
|     type Item = Expr<T::Item>; |  | ||||||
|     type IntoIter = T::ExprIntoIter; |  | ||||||
| 
 |  | ||||||
|     fn into_iter(self) -> Self::IntoIter { |  | ||||||
|         T::expr_into_iter(*self) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait ExprFromIterator<A>: Type { |  | ||||||
|     fn expr_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Expr<Self>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> { |  | ||||||
|     fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self { |  | ||||||
|         This::expr_from_iter(iter) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ use crate::{ | ||||||
|         target::{ |         target::{ | ||||||
|             Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, |             Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, | ||||||
|         }, |         }, | ||||||
|         CastBitsTo, Expr, ExprEnum, |         Expr, ExprEnum, | ||||||
|     }, |     }, | ||||||
|     formal::FormalKind, |     formal::FormalKind, | ||||||
|     int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, |     int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, | ||||||
|  | @ -447,7 +447,6 @@ impl TypeState { | ||||||
|             CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), |             CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), | ||||||
|             CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), |             CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), | ||||||
|             CanonicalType::Reset(Reset {}) => "Reset".into(), |             CanonicalType::Reset(Reset {}) => "Reset".into(), | ||||||
|             CanonicalType::PhantomConst(_) => "{}".into(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1153,7 +1152,6 @@ impl<'a> Exporter<'a> { | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::Reset(_) => format!("asUInt({value_str})"), |             | CanonicalType::Reset(_) => format!("asUInt({value_str})"), | ||||||
|             CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn expr_cast_bits_to_bundle( |     fn expr_cast_bits_to_bundle( | ||||||
|  | @ -1359,12 +1357,6 @@ impl<'a> Exporter<'a> { | ||||||
|             CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), |             CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), | ||||||
|             CanonicalType::SyncReset(_) => value_str, |             CanonicalType::SyncReset(_) => value_str, | ||||||
|             CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), |             CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), | ||||||
|             CanonicalType::PhantomConst(_) => { |  | ||||||
|                 let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); |  | ||||||
|                 definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); |  | ||||||
|                 definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); |  | ||||||
|                 return retval.to_string(); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn expr_unary<T: Type>( |     fn expr_unary<T: Type>( | ||||||
|  | @ -1403,11 +1395,6 @@ impl<'a> Exporter<'a> { | ||||||
|             ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), |             ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), | ||||||
|             ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), |             ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), | ||||||
|             ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), |             ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), | ||||||
|             ExprEnum::PhantomConst(ty) => self.expr( |  | ||||||
|                 UInt[0].zero().cast_bits_to(ty.canonical()), |  | ||||||
|                 definitions, |  | ||||||
|                 const_ty, |  | ||||||
|             ), |  | ||||||
|             ExprEnum::ArrayLiteral(array_literal) => { |             ExprEnum::ArrayLiteral(array_literal) => { | ||||||
|                 self.array_literal_expr(array_literal, definitions, const_ty) |                 self.array_literal_expr(array_literal, definitions, const_ty) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -96,7 +96,6 @@ pub mod int; | ||||||
| pub mod intern; | pub mod intern; | ||||||
| pub mod memory; | pub mod memory; | ||||||
| pub mod module; | pub mod module; | ||||||
| pub mod phantom_const; |  | ||||||
| pub mod prelude; | pub mod prelude; | ||||||
| pub mod reg; | pub mod reg; | ||||||
| pub mod reset; | pub mod reset; | ||||||
|  |  | ||||||
|  | @ -1082,7 +1082,6 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> { | ||||||
|             ) |             ) | ||||||
|             .to_expr(), |             .to_expr(), | ||||||
|         )), |         )), | ||||||
|         CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1490,9 +1490,6 @@ impl TargetState { | ||||||
|                         }) |                         }) | ||||||
|                         .collect(), |                         .collect(), | ||||||
|                 }, |                 }, | ||||||
|                 CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { |  | ||||||
|                     subtargets: HashMap::new(), |  | ||||||
|                 }, |  | ||||||
|                 CanonicalType::Array(ty) => TargetStateInner::Decomposed { |                 CanonicalType::Array(ty) => TargetStateInner::Decomposed { | ||||||
|                     subtargets: (0..ty.len()) |                     subtargets: (0..ty.len()) | ||||||
|                         .map(|index| { |                         .map(|index| { | ||||||
|  |  | ||||||
|  | @ -155,7 +155,6 @@ impl ResetsLayout { | ||||||
|                     CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, |                     CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, | ||||||
|                     CanonicalType::Reset(_) => ResetsLayout::Reset, |                     CanonicalType::Reset(_) => ResetsLayout::Reset, | ||||||
|                     CanonicalType::Clock(_) => ResetsLayout::NoResets, |                     CanonicalType::Clock(_) => ResetsLayout::NoResets, | ||||||
|                     CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -408,8 +407,7 @@ impl Resets { | ||||||
|             | CanonicalType::Bool(_) |             | CanonicalType::Bool(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | CanonicalType::SyncReset(_) | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) => Ok(self.ty), | ||||||
|             | CanonicalType::PhantomConst(_) => Ok(self.ty), |  | ||||||
|             CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( |             CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( | ||||||
|                 self.array_elements().substituted_type( |                 self.array_elements().substituted_type( | ||||||
|                     reset_graph, |                     reset_graph, | ||||||
|  | @ -1000,8 +998,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>( | ||||||
|                             CanonicalType::Array(_) |                             CanonicalType::Array(_) | ||||||
|                             | CanonicalType::Enum(_) |                             | CanonicalType::Enum(_) | ||||||
|                             | CanonicalType::Bundle(_) |                             | CanonicalType::Bundle(_) | ||||||
|                             | CanonicalType::Reset(_) |                             | CanonicalType::Reset(_) => unreachable!(), | ||||||
|                             | CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|                             $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* |                             $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* | ||||||
|                         } |                         } | ||||||
|                     }; |                     }; | ||||||
|  | @ -1013,7 +1010,6 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>( | ||||||
|                             | CanonicalType::Enum(_) |                             | CanonicalType::Enum(_) | ||||||
|                             | CanonicalType::Bundle(_) |                             | CanonicalType::Bundle(_) | ||||||
|                             | CanonicalType::Reset(_) => unreachable!(), |                             | CanonicalType::Reset(_) => unreachable!(), | ||||||
|                             CanonicalType::PhantomConst(_) => Expr::expr_enum(arg), |  | ||||||
|                             $(CanonicalType::$Variant(_) => { |                             $(CanonicalType::$Variant(_) => { | ||||||
|                                 let arg = Expr::<$Variant>::from_canonical(arg); |                                 let arg = Expr::<$Variant>::from_canonical(arg); | ||||||
|                                 match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) |                                 match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) | ||||||
|  | @ -1044,7 +1040,6 @@ impl<P: Pass> RunPass<P> for ExprEnum { | ||||||
|             ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |  | ||||||
|             ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), |             ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|  | @ -1675,8 +1670,7 @@ impl RunPassDispatch for AnyReg { | ||||||
|                 | CanonicalType::Enum(_) |                 | CanonicalType::Enum(_) | ||||||
|                 | CanonicalType::Bundle(_) |                 | CanonicalType::Bundle(_) | ||||||
|                 | CanonicalType::Reset(_) |                 | CanonicalType::Reset(_) | ||||||
|                 | CanonicalType::Clock(_) |                 | CanonicalType::Clock(_) => unreachable!(), | ||||||
|                 | CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  | @ -1775,7 +1769,6 @@ impl_run_pass_copy!([] SVAttributeAnnotation); | ||||||
| impl_run_pass_copy!([] UInt); | impl_run_pass_copy!([] UInt); | ||||||
| impl_run_pass_copy!([] usize); | impl_run_pass_copy!([] usize); | ||||||
| impl_run_pass_copy!([] FormalKind); | impl_run_pass_copy!([] FormalKind); | ||||||
| impl_run_pass_copy!([] PhantomConst); |  | ||||||
| 
 | 
 | ||||||
| macro_rules! impl_run_pass_for_struct { | macro_rules! impl_run_pass_for_struct { | ||||||
|     ( |     ( | ||||||
|  |  | ||||||
|  | @ -69,8 +69,7 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool { | ||||||
|                 | CanonicalType::AsyncReset(_) |                 | CanonicalType::AsyncReset(_) | ||||||
|                 | CanonicalType::SyncReset(_) |                 | CanonicalType::SyncReset(_) | ||||||
|                 | CanonicalType::Reset(_) |                 | CanonicalType::Reset(_) | ||||||
|                 | CanonicalType::Clock(_) |                 | CanonicalType::Clock(_) => false, | ||||||
|                 | CanonicalType::PhantomConst(_) => false, |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -513,8 +512,7 @@ impl State { | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | CanonicalType::SyncReset(_) | ||||||
|             | CanonicalType::Reset(_) |             | CanonicalType::Reset(_) | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) => unreachable!(), | ||||||
|             | CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -579,8 +577,7 @@ fn connect_port( | ||||||
|         | (CanonicalType::Clock(_), _) |         | (CanonicalType::Clock(_), _) | ||||||
|         | (CanonicalType::AsyncReset(_), _) |         | (CanonicalType::AsyncReset(_), _) | ||||||
|         | (CanonicalType::SyncReset(_), _) |         | (CanonicalType::SyncReset(_), _) | ||||||
|         | (CanonicalType::Reset(_), _) |         | (CanonicalType::Reset(_), _) => unreachable!( | ||||||
|         | (CanonicalType::PhantomConst(_), _) => unreachable!( |  | ||||||
|             "trying to connect memory ports:\n{:?}\n{:?}", |             "trying to connect memory ports:\n{:?}\n{:?}", | ||||||
|             Expr::ty(lhs), |             Expr::ty(lhs), | ||||||
|             Expr::ty(rhs), |             Expr::ty(rhs), | ||||||
|  | @ -668,7 +665,6 @@ impl Folder for State { | ||||||
|             ExprEnum::UIntLiteral(_) |             ExprEnum::UIntLiteral(_) | ||||||
|             | ExprEnum::SIntLiteral(_) |             | ExprEnum::SIntLiteral(_) | ||||||
|             | ExprEnum::BoolLiteral(_) |             | ExprEnum::BoolLiteral(_) | ||||||
|             | ExprEnum::PhantomConst(_) |  | ||||||
|             | ExprEnum::BundleLiteral(_) |             | ExprEnum::BundleLiteral(_) | ||||||
|             | ExprEnum::ArrayLiteral(_) |             | ExprEnum::ArrayLiteral(_) | ||||||
|             | ExprEnum::Uninit(_) |             | ExprEnum::Uninit(_) | ||||||
|  | @ -927,8 +923,7 @@ impl Folder for State { | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | CanonicalType::SyncReset(_) | ||||||
|             | CanonicalType::Reset(_) |             | CanonicalType::Reset(_) => canonical_type.default_fold(self), | ||||||
|             | CanonicalType::PhantomConst(_) => canonical_type.default_fold(self), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -62,7 +62,6 @@ enum MemSplit { | ||||||
|     Bundle { |     Bundle { | ||||||
|         fields: Rc<[MemSplit]>, |         fields: Rc<[MemSplit]>, | ||||||
|     }, |     }, | ||||||
|     PhantomConst, |  | ||||||
|     Single { |     Single { | ||||||
|         output_mem: Option<Mem>, |         output_mem: Option<Mem>, | ||||||
|         element_type: SingleType, |         element_type: SingleType, | ||||||
|  | @ -77,7 +76,6 @@ impl MemSplit { | ||||||
|     fn mark_changed_element_type(self) -> Self { |     fn mark_changed_element_type(self) -> Self { | ||||||
|         match self { |         match self { | ||||||
|             MemSplit::Bundle { fields: _ } => self, |             MemSplit::Bundle { fields: _ } => self, | ||||||
|             MemSplit::PhantomConst => self, |  | ||||||
|             MemSplit::Single { |             MemSplit::Single { | ||||||
|                 output_mem, |                 output_mem, | ||||||
|                 element_type, |                 element_type, | ||||||
|  | @ -99,7 +97,6 @@ impl MemSplit { | ||||||
|                     .map(|field| Self::new(field.ty).mark_changed_element_type()) |                     .map(|field| Self::new(field.ty).mark_changed_element_type()) | ||||||
|                     .collect(), |                     .collect(), | ||||||
|             }, |             }, | ||||||
|             CanonicalType::PhantomConst(_) => MemSplit::PhantomConst, |  | ||||||
|             CanonicalType::Array(ty) => { |             CanonicalType::Array(ty) => { | ||||||
|                 let element = MemSplit::new(ty.element()); |                 let element = MemSplit::new(ty.element()); | ||||||
|                 if let Self::Single { |                 if let Self::Single { | ||||||
|  | @ -342,7 +339,6 @@ impl SplitMemState<'_, '_> { | ||||||
|                     self.split_state_stack.pop(); |                     self.split_state_stack.pop(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             MemSplit::PhantomConst => {} |  | ||||||
|             MemSplit::Single { |             MemSplit::Single { | ||||||
|                 output_mem, |                 output_mem, | ||||||
|                 element_type: single_type, |                 element_type: single_type, | ||||||
|  | @ -542,12 +538,7 @@ impl ModuleState { | ||||||
|             }; |             }; | ||||||
|         loop { |         loop { | ||||||
|             match input_element_type { |             match input_element_type { | ||||||
|                 CanonicalType::Bundle(_) => { |                 CanonicalType::Bundle(_) => unreachable!("bundle types are always split"), | ||||||
|                     unreachable!("bundle types are always split") |  | ||||||
|                 } |  | ||||||
|                 CanonicalType::PhantomConst(_) => { |  | ||||||
|                     unreachable!("PhantomConst are always removed") |  | ||||||
|                 } |  | ||||||
|                 CanonicalType::Enum(_) |                 CanonicalType::Enum(_) | ||||||
|                     if input_array_types |                     if input_array_types | ||||||
|                         .first() |                         .first() | ||||||
|  | @ -752,8 +743,7 @@ impl ModuleState { | ||||||
|                 .. |                 .. | ||||||
|             } |             } | ||||||
|             | MemSplit::Bundle { .. } |             | MemSplit::Bundle { .. } | ||||||
|             | MemSplit::Array { .. } |             | MemSplit::Array { .. } => { | ||||||
|             | MemSplit::PhantomConst => { |  | ||||||
|                 let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); |                 let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); | ||||||
|                 let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len()); |                 let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len()); | ||||||
|                 let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len()); |                 let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len()); | ||||||
|  |  | ||||||
|  | @ -28,7 +28,6 @@ use crate::{ | ||||||
|         NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, |         NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, | ||||||
|         StmtInstance, StmtMatch, StmtReg, StmtWire, |         StmtInstance, StmtMatch, StmtReg, StmtWire, | ||||||
|     }, |     }, | ||||||
|     phantom_const::PhantomConst, |  | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, ResetType, SyncReset}, |     reset::{AsyncReset, Reset, ResetType, SyncReset}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|  |  | ||||||
|  | @ -1,273 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| 
 |  | ||||||
| use serde::{de::DeserializeOwned, Deserialize, Serialize}; |  | ||||||
| 
 |  | ||||||
| use crate::{ |  | ||||||
|     intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, |  | ||||||
|     source_location::SourceLocation, |  | ||||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, |  | ||||||
| }; |  | ||||||
| use std::{ |  | ||||||
|     any::Any, |  | ||||||
|     fmt, |  | ||||||
|     hash::{Hash, Hasher}, |  | ||||||
|     marker::PhantomData, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #[derive(Clone)] |  | ||||||
| pub struct PhantomConstCanonicalValue { |  | ||||||
|     parsed: serde_json::Value, |  | ||||||
|     serialized: Interned<str>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl PhantomConstCanonicalValue { |  | ||||||
|     pub fn from_json_value(parsed: serde_json::Value) -> Self { |  | ||||||
|         let serialized = Intern::intern_owned( |  | ||||||
|             serde_json::to_string(&parsed) |  | ||||||
|                 .expect("conversion from json value to text shouldn't fail"), |  | ||||||
|         ); |  | ||||||
|         Self { parsed, serialized } |  | ||||||
|     } |  | ||||||
|     pub fn as_json_value(&self) -> &serde_json::Value { |  | ||||||
|         &self.parsed |  | ||||||
|     } |  | ||||||
|     pub fn as_str(&self) -> Interned<str> { |  | ||||||
|         self.serialized |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl fmt::Debug for PhantomConstCanonicalValue { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         f.write_str(&self.serialized) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl fmt::Display for PhantomConstCanonicalValue { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         f.write_str(&self.serialized) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl PartialEq for PhantomConstCanonicalValue { |  | ||||||
|     fn eq(&self, other: &Self) -> bool { |  | ||||||
|         self.serialized == other.serialized |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Eq for PhantomConstCanonicalValue {} |  | ||||||
| 
 |  | ||||||
| impl Hash for PhantomConstCanonicalValue { |  | ||||||
|     fn hash<H: Hasher>(&self, state: &mut H) { |  | ||||||
|         self.serialized.hash(state); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Serialize for PhantomConstCanonicalValue { |  | ||||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|     where |  | ||||||
|         S: serde::Serializer, |  | ||||||
|     { |  | ||||||
|         self.parsed.serialize(serializer) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'de> Deserialize<'de> for PhantomConstCanonicalValue { |  | ||||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |  | ||||||
|     where |  | ||||||
|         D: serde::Deserializer<'de>, |  | ||||||
|     { |  | ||||||
|         Ok(Self::from_json_value(serde_json::Value::deserialize( |  | ||||||
|             deserializer, |  | ||||||
|         )?)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug { |  | ||||||
|     fn deserialize<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error> |  | ||||||
|     where |  | ||||||
|         D: serde::Deserializer<'de>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T> PhantomConstValue for T |  | ||||||
| where |  | ||||||
|     T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug, |  | ||||||
|     Interned<T>: DeserializeOwned, |  | ||||||
| { |  | ||||||
|     fn deserialize<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error> |  | ||||||
|     where |  | ||||||
|         D: serde::Deserializer<'de>, |  | ||||||
|     { |  | ||||||
|         <Interned<T> as Deserialize<'de>>::deserialize(deserializer) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`].
 |  | ||||||
| /// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json].
 |  | ||||||
| pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalValue> { |  | ||||||
|     value: LazyInterned<T>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> fmt::Debug for PhantomConst<T> { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         f.debug_tuple("PhantomConst").field(&self.get()).finish() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Clone for PhantomConst<T> { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Copy for PhantomConst<T> {} |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> PartialEq for PhantomConst<T> { |  | ||||||
|     fn eq(&self, other: &Self) -> bool { |  | ||||||
|         self.get() == other.get() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Eq for PhantomConst<T> {} |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Hash for PhantomConst<T> { |  | ||||||
|     fn hash<H: Hasher>(&self, state: &mut H) { |  | ||||||
|         self.get().hash(state); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct PhantomConstCanonicalMemoize<T: ?Sized, const IS_FROM_CANONICAL: bool>(PhantomData<T>); |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Copy |  | ||||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Clone |  | ||||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> |  | ||||||
| { |  | ||||||
|     fn clone(&self) -> Self { |  | ||||||
|         *self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Eq |  | ||||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> PartialEq |  | ||||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> |  | ||||||
| { |  | ||||||
|     fn eq(&self, _other: &Self) -> bool { |  | ||||||
|         true |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Hash |  | ||||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> |  | ||||||
| { |  | ||||||
|     fn hash<H: Hasher>(&self, _state: &mut H) {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, false> { |  | ||||||
|     type Input = Interned<T>; |  | ||||||
|     type InputOwned = Interned<T>; |  | ||||||
|     type Output = Interned<PhantomConstCanonicalValue>; |  | ||||||
| 
 |  | ||||||
|     fn inner(self, input: &Self::Input) -> Self::Output { |  | ||||||
|         Intern::intern_sized(PhantomConstCanonicalValue::from_json_value( |  | ||||||
|             serde_json::to_value(input) |  | ||||||
|                 .expect("serialization failed when constructing a canonical PhantomConst"), |  | ||||||
|         )) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, true> { |  | ||||||
|     type Input = Interned<PhantomConstCanonicalValue>; |  | ||||||
|     type InputOwned = Interned<PhantomConstCanonicalValue>; |  | ||||||
|     type Output = Interned<T>; |  | ||||||
| 
 |  | ||||||
|     fn inner(self, input: &Self::Input) -> Self::Output { |  | ||||||
|         PhantomConstValue::deserialize(input.as_json_value()).expect("deserialization failed ") |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> PhantomConst<T> |  | ||||||
| where |  | ||||||
|     Interned<T>: Default, |  | ||||||
| { |  | ||||||
|     pub const fn default() -> Self { |  | ||||||
|         PhantomConst { |  | ||||||
|             value: LazyInterned::new_lazy(&Interned::<T>::default), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> PhantomConst<T> { |  | ||||||
|     pub fn new(value: Interned<T>) -> Self { |  | ||||||
|         Self { |  | ||||||
|             value: LazyInterned::Interned(value), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn get(self) -> Interned<T> { |  | ||||||
|         self.value.interned() |  | ||||||
|     } |  | ||||||
|     pub fn type_properties(self) -> TypeProperties { |  | ||||||
|         <()>::TYPE_PROPERTIES |  | ||||||
|     } |  | ||||||
|     pub fn can_connect(self, other: Self) -> bool { |  | ||||||
|         self == other |  | ||||||
|     } |  | ||||||
|     pub fn canonical_phantom_const(self) -> PhantomConst { |  | ||||||
|         if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) { |  | ||||||
|             return retval; |  | ||||||
|         } |  | ||||||
|         <PhantomConst>::new( |  | ||||||
|             PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()), |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|     pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self { |  | ||||||
|         if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) { |  | ||||||
|             return retval; |  | ||||||
|         } |  | ||||||
|         Self::new( |  | ||||||
|             PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()), |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> { |  | ||||||
|     type BaseType = PhantomConst; |  | ||||||
|     type MaskType = (); |  | ||||||
|     impl_match_variant_as_self!(); |  | ||||||
| 
 |  | ||||||
|     fn mask_type(&self) -> Self::MaskType { |  | ||||||
|         () |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn canonical(&self) -> CanonicalType { |  | ||||||
|         CanonicalType::PhantomConst(self.canonical_phantom_const()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self { |  | ||||||
|         let CanonicalType::PhantomConst(phantom_const) = canonical_type else { |  | ||||||
|             panic!("expected PhantomConst"); |  | ||||||
|         }; |  | ||||||
|         Self::from_canonical_phantom_const(phantom_const) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn source_location() -> SourceLocation { |  | ||||||
|         SourceLocation::builtin() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T> |  | ||||||
| where |  | ||||||
|     Interned<T>: Default, |  | ||||||
| { |  | ||||||
|     const TYPE: Self = Self::default(); |  | ||||||
|     const MASK_TYPE: Self::MaskType = (); |  | ||||||
|     const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; |  | ||||||
|     const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; |  | ||||||
| } |  | ||||||
|  | @ -26,7 +26,6 @@ pub use crate::{ | ||||||
|         annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, |         annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, | ||||||
|         memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, |         memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, | ||||||
|     }, |     }, | ||||||
|     phantom_const::PhantomConst, |  | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, |     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|  |  | ||||||
|  | @ -167,14 +167,6 @@ impl<T: Type> CompiledTypeLayout<T> { | ||||||
|                             body: CompiledTypeLayoutBody::Array { element }, |                             body: CompiledTypeLayoutBody::Array { element }, | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     CanonicalType::PhantomConst(_) => { |  | ||||||
|                         let unit_layout = CompiledTypeLayout::get(()); |  | ||||||
|                         CompiledTypeLayout { |  | ||||||
|                             ty: *input, |  | ||||||
|                             layout: unit_layout.layout, |  | ||||||
|                             body: unit_layout.body, |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     CanonicalType::Bundle(bundle) => { |                     CanonicalType::Bundle(bundle) => { | ||||||
|                         let mut layout = TypeLayout::empty(); |                         let mut layout = TypeLayout::empty(); | ||||||
|                         let fields = bundle |                         let fields = bundle | ||||||
|  | @ -1800,7 +1792,7 @@ impl Compiler { | ||||||
|                 } |                 } | ||||||
|                 .into() |                 .into() | ||||||
|             } |             } | ||||||
|             CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), |             CanonicalType::Bundle(_) => unreachable!(), | ||||||
|             CanonicalType::AsyncReset(_) => TraceAsyncReset { |             CanonicalType::AsyncReset(_) => TraceAsyncReset { | ||||||
|                 location: self.make_trace_scalar_helper( |                 location: self.make_trace_scalar_helper( | ||||||
|                     instantiated_module, |                     instantiated_module, | ||||||
|  | @ -2017,13 +2009,6 @@ impl Compiler { | ||||||
|             | CanonicalType::Clock(_) => { |             | CanonicalType::Clock(_) => { | ||||||
|                 self.make_trace_scalar(instantiated_module, target, name, source_location) |                 self.make_trace_scalar(instantiated_module, target, name, source_location) | ||||||
|             } |             } | ||||||
|             CanonicalType::PhantomConst(_) => TraceBundle { |  | ||||||
|                 name, |  | ||||||
|                 fields: Interned::default(), |  | ||||||
|                 ty: Bundle::new(Interned::default()), |  | ||||||
|                 flow: target.flow(), |  | ||||||
|             } |  | ||||||
|             .into(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn make_trace_decl( |     fn make_trace_decl( | ||||||
|  | @ -2484,9 +2469,6 @@ impl Compiler { | ||||||
|                     Expr::field(Expr::<Bundle>::from_canonical(expr.arg()), &field.name) |                     Expr::field(Expr::<Bundle>::from_canonical(expr.arg()), &field.name) | ||||||
|                 }), |                 }), | ||||||
|             ), |             ), | ||||||
|             CanonicalType::PhantomConst(_) => { |  | ||||||
|                 self.compile_cast_aggregate_to_bits(instantiated_module, []) |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn compile_cast_bits_to( |     fn compile_cast_bits_to( | ||||||
|  | @ -2536,10 +2518,6 @@ impl Compiler { | ||||||
|             CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), |             CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), | ||||||
|             CanonicalType::Reset(_) => unreachable!(), |             CanonicalType::Reset(_) => unreachable!(), | ||||||
|             CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)), |             CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)), | ||||||
|             CanonicalType::PhantomConst(ty) => { |  | ||||||
|                 let _ = self.compile_expr(instantiated_module, Expr::canonical(expr.arg())); |  | ||||||
|                 Expr::canonical(ty.to_expr()) |  | ||||||
|             } |  | ||||||
|         }; |         }; | ||||||
|         let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); |         let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); | ||||||
|         self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) |         self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) | ||||||
|  | @ -2589,7 +2567,6 @@ impl Compiler { | ||||||
|                 CanonicalType::SyncReset(_) => false, |                 CanonicalType::SyncReset(_) => false, | ||||||
|                 CanonicalType::Reset(_) => false, |                 CanonicalType::Reset(_) => false, | ||||||
|                 CanonicalType::Clock(_) => false, |                 CanonicalType::Clock(_) => false, | ||||||
|                 CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|             }; |             }; | ||||||
|             let dest_signed = match Expr::ty(expr) { |             let dest_signed = match Expr::ty(expr) { | ||||||
|                 CanonicalType::UInt(_) => false, |                 CanonicalType::UInt(_) => false, | ||||||
|  | @ -2602,7 +2579,6 @@ impl Compiler { | ||||||
|                 CanonicalType::SyncReset(_) => false, |                 CanonicalType::SyncReset(_) => false, | ||||||
|                 CanonicalType::Reset(_) => false, |                 CanonicalType::Reset(_) => false, | ||||||
|                 CanonicalType::Clock(_) => false, |                 CanonicalType::Clock(_) => false, | ||||||
|                 CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|             }; |             }; | ||||||
|             self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { |             self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { | ||||||
|                 match (src_signed, dest_signed) { |                 match (src_signed, dest_signed) { | ||||||
|  | @ -2658,9 +2634,6 @@ impl Compiler { | ||||||
|                     }] |                     }] | ||||||
|                 }) |                 }) | ||||||
|                 .into(), |                 .into(), | ||||||
|             ExprEnum::PhantomConst(_) => self |  | ||||||
|                 .compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default()) |  | ||||||
|                 .into(), |  | ||||||
|             ExprEnum::BundleLiteral(literal) => self |             ExprEnum::BundleLiteral(literal) => self | ||||||
|                 .compile_aggregate_literal( |                 .compile_aggregate_literal( | ||||||
|                     instantiated_module, |                     instantiated_module, | ||||||
|  | @ -3564,7 +3537,6 @@ impl Compiler { | ||||||
|                 CanonicalType::SyncReset(_) => unreachable!(), |                 CanonicalType::SyncReset(_) => unreachable!(), | ||||||
|                 CanonicalType::Reset(_) => unreachable!(), |                 CanonicalType::Reset(_) => unreachable!(), | ||||||
|                 CanonicalType::Clock(_) => unreachable!(), |                 CanonicalType::Clock(_) => unreachable!(), | ||||||
|                 CanonicalType::PhantomConst(_) => unreachable!("PhantomConst mismatch"), |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         let Some(target) = lhs.target() else { |         let Some(target) = lhs.target() else { | ||||||
|  | @ -3929,7 +3901,6 @@ impl Compiler { | ||||||
|                     CanonicalType::SyncReset(_) => false, |                     CanonicalType::SyncReset(_) => false, | ||||||
|                     CanonicalType::Reset(_) => false, |                     CanonicalType::Reset(_) => false, | ||||||
|                     CanonicalType::Clock(_) => false, |                     CanonicalType::Clock(_) => false, | ||||||
|                     CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|                 }; |                 }; | ||||||
|                 let width = data_layout.ty.bit_width(); |                 let width = data_layout.ty.bit_width(); | ||||||
|                 if let Some(MemoryPortReadInsns { |                 if let Some(MemoryPortReadInsns { | ||||||
|  | @ -5938,7 +5909,6 @@ impl SimValue<CanonicalType> { | ||||||
|             CanonicalType::Clock(ty) => { |             CanonicalType::Clock(ty) => { | ||||||
|                 Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) |                 Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) | ||||||
|             } |             } | ||||||
|             CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -6342,8 +6312,7 @@ impl ToSimValue<CanonicalType> for bool { | ||||||
|             | CanonicalType::SInt(_) |             | CanonicalType::SInt(_) | ||||||
|             | CanonicalType::Array(_) |             | CanonicalType::Array(_) | ||||||
|             | CanonicalType::Enum(_) |             | CanonicalType::Enum(_) | ||||||
|             | CanonicalType::Bundle(_) |             | CanonicalType::Bundle(_) => { | ||||||
|             | CanonicalType::PhantomConst(_) => { |  | ||||||
|                 panic!("can't create SimValue from bool: expected value of type: {ty:?}"); |                 panic!("can't create SimValue from bool: expected value of type: {ty:?}"); | ||||||
|             } |             } | ||||||
|             CanonicalType::Bool(_) |             CanonicalType::Bool(_) | ||||||
|  | @ -7008,7 +6977,6 @@ impl SimulationImpl { | ||||||
|                     CanonicalType::SyncReset(_) => false, |                     CanonicalType::SyncReset(_) => false, | ||||||
|                     CanonicalType::Reset(_) => false, |                     CanonicalType::Reset(_) => false, | ||||||
|                     CanonicalType::Clock(_) => false, |                     CanonicalType::Clock(_) => false, | ||||||
|                     CanonicalType::PhantomConst(_) => unreachable!(), |  | ||||||
|                 }; |                 }; | ||||||
|                 match compiled_value.range.len() { |                 match compiled_value.range.len() { | ||||||
|                     TypeLen::A_SMALL_SLOT => read_write_small_scalar( |                     TypeLen::A_SMALL_SLOT => read_write_small_scalar( | ||||||
|  |  | ||||||
|  | @ -9,10 +9,8 @@ use crate::{ | ||||||
|     expr::Expr, |     expr::Expr, | ||||||
|     int::{Bool, SInt, UInt}, |     int::{Bool, SInt, UInt}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     phantom_const::PhantomConst, |  | ||||||
|     reset::{AsyncReset, Reset, SyncReset}, |     reset::{AsyncReset, Reset, SyncReset}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     util::ConstUsize, |  | ||||||
| }; | }; | ||||||
| use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; | use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; | ||||||
| 
 | 
 | ||||||
|  | @ -37,7 +35,6 @@ pub enum CanonicalType { | ||||||
|     SyncReset(SyncReset), |     SyncReset(SyncReset), | ||||||
|     Reset(Reset), |     Reset(Reset), | ||||||
|     Clock(Clock), |     Clock(Clock), | ||||||
|     PhantomConst(PhantomConst), |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for CanonicalType { | impl fmt::Debug for CanonicalType { | ||||||
|  | @ -53,7 +50,6 @@ impl fmt::Debug for CanonicalType { | ||||||
|             Self::SyncReset(v) => v.fmt(f), |             Self::SyncReset(v) => v.fmt(f), | ||||||
|             Self::Reset(v) => v.fmt(f), |             Self::Reset(v) => v.fmt(f), | ||||||
|             Self::Clock(v) => v.fmt(f), |             Self::Clock(v) => v.fmt(f), | ||||||
|             Self::PhantomConst(v) => v.fmt(f), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -71,7 +67,6 @@ impl CanonicalType { | ||||||
|             CanonicalType::SyncReset(v) => v.type_properties(), |             CanonicalType::SyncReset(v) => v.type_properties(), | ||||||
|             CanonicalType::Reset(v) => v.type_properties(), |             CanonicalType::Reset(v) => v.type_properties(), | ||||||
|             CanonicalType::Clock(v) => v.type_properties(), |             CanonicalType::Clock(v) => v.type_properties(), | ||||||
|             CanonicalType::PhantomConst(v) => v.type_properties(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn is_passive(self) -> bool { |     pub fn is_passive(self) -> bool { | ||||||
|  | @ -148,12 +143,6 @@ impl CanonicalType { | ||||||
|                 }; |                 }; | ||||||
|                 lhs.can_connect(rhs) |                 lhs.can_connect(rhs) | ||||||
|             } |             } | ||||||
|             CanonicalType::PhantomConst(lhs) => { |  | ||||||
|                 let CanonicalType::PhantomConst(rhs) = rhs else { |  | ||||||
|                     return false; |  | ||||||
|                 }; |  | ||||||
|                 lhs.can_connect(rhs) |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -177,7 +166,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait FillInDefaultedGenerics { | pub trait FillInDefaultedGenerics { | ||||||
|     type Type; |     type Type: Type; | ||||||
|     fn fill_in_defaulted_generics(self) -> Self::Type; |     fn fill_in_defaulted_generics(self) -> Self::Type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -189,22 +178,6 @@ impl<T: Type> FillInDefaultedGenerics for T { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FillInDefaultedGenerics for usize { |  | ||||||
|     type Type = usize; |  | ||||||
| 
 |  | ||||||
|     fn fill_in_defaulted_generics(self) -> Self::Type { |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<const V: usize> FillInDefaultedGenerics for ConstUsize<V> { |  | ||||||
|     type Type = ConstUsize<V>; |  | ||||||
| 
 |  | ||||||
|     fn fill_in_defaulted_generics(self) -> Self::Type { |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| mod sealed { | mod sealed { | ||||||
|     pub trait TypeOrDefaultSealed {} |     pub trait TypeOrDefaultSealed {} | ||||||
|     pub trait BaseTypeSealed {} |     pub trait BaseTypeSealed {} | ||||||
|  | @ -232,7 +205,6 @@ impl_base_type!(AsyncReset); | ||||||
| impl_base_type!(SyncReset); | impl_base_type!(SyncReset); | ||||||
| impl_base_type!(Reset); | impl_base_type!(Reset); | ||||||
| impl_base_type!(Clock); | impl_base_type!(Clock); | ||||||
| impl_base_type!(PhantomConst); |  | ||||||
| 
 | 
 | ||||||
| impl sealed::BaseTypeSealed for CanonicalType {} | impl sealed::BaseTypeSealed for CanonicalType {} | ||||||
| 
 | 
 | ||||||
|  | @ -327,7 +299,6 @@ impl Type for CanonicalType { | ||||||
|             CanonicalType::SyncReset(v) => v.mask_type().canonical(), |             CanonicalType::SyncReset(v) => v.mask_type().canonical(), | ||||||
|             CanonicalType::Reset(v) => v.mask_type().canonical(), |             CanonicalType::Reset(v) => v.mask_type().canonical(), | ||||||
|             CanonicalType::Clock(v) => v.mask_type().canonical(), |             CanonicalType::Clock(v) => v.mask_type().canonical(), | ||||||
|             CanonicalType::PhantomConst(v) => v.mask_type().canonical(), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn canonical(&self) -> CanonicalType { |     fn canonical(&self) -> CanonicalType { | ||||||
|  |  | ||||||
|  | @ -29,5 +29,4 @@ pub use misc::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub mod job_server; | pub mod job_server; | ||||||
| pub mod prefix_sum; |  | ||||||
| pub mod ready_valid; | pub mod ready_valid; | ||||||
|  |  | ||||||
|  | @ -1,839 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| 
 |  | ||||||
| // code derived from:
 |  | ||||||
| // https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c
 |  | ||||||
| 
 |  | ||||||
| use crate::intern::{Intern, Interned, Memoize}; |  | ||||||
| use std::{borrow::Cow, num::NonZeroUsize}; |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |  | ||||||
| pub struct PrefixSumOp { |  | ||||||
|     pub lhs_index: usize, |  | ||||||
|     pub rhs_and_dest_index: NonZeroUsize, |  | ||||||
|     pub row: u32, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] |  | ||||||
| #[non_exhaustive] |  | ||||||
| pub struct DiagramConfig { |  | ||||||
|     pub space: Cow<'static, str>, |  | ||||||
|     pub vertical_bar: Cow<'static, str>, |  | ||||||
|     pub plus: Cow<'static, str>, |  | ||||||
|     pub slant: Cow<'static, str>, |  | ||||||
|     pub connect: Cow<'static, str>, |  | ||||||
|     pub no_connect: Cow<'static, str>, |  | ||||||
|     pub padding: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl DiagramConfig { |  | ||||||
|     pub const fn new() -> Self { |  | ||||||
|         Self { |  | ||||||
|             space: Cow::Borrowed(" "), |  | ||||||
|             vertical_bar: Cow::Borrowed("|"), |  | ||||||
|             plus: Cow::Borrowed("\u{2295}"), // ⊕
 |  | ||||||
|             slant: Cow::Borrowed(r"\"), |  | ||||||
|             connect: Cow::Borrowed("\u{25CF}"), // ●
 |  | ||||||
|             no_connect: Cow::Borrowed("X"), |  | ||||||
|             padding: 1, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn draw(self, ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize) -> String { |  | ||||||
|         #[derive(Copy, Clone, Debug)] |  | ||||||
|         struct DiagramCell { |  | ||||||
|             slant: bool, |  | ||||||
|             plus: bool, |  | ||||||
|             tee: bool, |  | ||||||
|         } |  | ||||||
|         let mut ops_by_row: Vec<Vec<PrefixSumOp>> = Vec::new(); |  | ||||||
|         let mut last_row = 0; |  | ||||||
|         ops.into_iter().for_each(|op| { |  | ||||||
|             assert!( |  | ||||||
|                 op.lhs_index < op.rhs_and_dest_index.get(), |  | ||||||
|                 "invalid PrefixSumOp! lhs_index must be less \ |  | ||||||
|                 than rhs_and_dest_index: {op:?}",
 |  | ||||||
|             ); |  | ||||||
|             assert!( |  | ||||||
|                 op.row >= last_row, |  | ||||||
|                 "invalid PrefixSumOp! row must \ |  | ||||||
|                 not decrease (row last was: {last_row}): {op:?}",
 |  | ||||||
|             ); |  | ||||||
|             let ops = if op.row > last_row || ops_by_row.is_empty() { |  | ||||||
|                 ops_by_row.push(vec![]); |  | ||||||
|                 ops_by_row.last_mut().expect("just pushed") |  | ||||||
|             } else { |  | ||||||
|                 ops_by_row |  | ||||||
|                     .last_mut() |  | ||||||
|                     .expect("just checked if ops_by_row is empty") |  | ||||||
|             }; |  | ||||||
|             if let Some(last) = ops.last() { |  | ||||||
|                 assert!( |  | ||||||
|                     op.rhs_and_dest_index < last.rhs_and_dest_index, |  | ||||||
|                     "invalid PrefixSumOp! rhs_and_dest_index must strictly \ |  | ||||||
|                     decrease in a row:\nthis op: {op:?}\nlast op: {last:?}",
 |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|             ops.push(op); |  | ||||||
|             last_row = op.row; |  | ||||||
|         }); |  | ||||||
|         let blank_row = || { |  | ||||||
|             vec![ |  | ||||||
|                 DiagramCell { |  | ||||||
|                     slant: false, |  | ||||||
|                     plus: false, |  | ||||||
|                     tee: false |  | ||||||
|                 }; |  | ||||||
|                 item_count |  | ||||||
|             ] |  | ||||||
|         }; |  | ||||||
|         let mut cells = vec![blank_row()]; |  | ||||||
|         for ops in ops_by_row { |  | ||||||
|             let max_distance = ops |  | ||||||
|                 .iter() |  | ||||||
|                 .map( |  | ||||||
|                     |&PrefixSumOp { |  | ||||||
|                          lhs_index, |  | ||||||
|                          rhs_and_dest_index, |  | ||||||
|                          .. |  | ||||||
|                      }| { rhs_and_dest_index.get() - lhs_index }, |  | ||||||
|                 ) |  | ||||||
|                 .max() |  | ||||||
|                 .expect("ops is known to be non-empty"); |  | ||||||
|             cells.extend((0..max_distance).map(|_| blank_row())); |  | ||||||
|             for op in ops { |  | ||||||
|                 let mut y = cells.len() - 1; |  | ||||||
|                 assert!( |  | ||||||
|                     op.rhs_and_dest_index.get() < item_count, |  | ||||||
|                     "invalid PrefixSumOp! rhs_and_dest_index must be \ |  | ||||||
|                     less than item_count ({item_count}): {op:?}",
 |  | ||||||
|                 ); |  | ||||||
|                 let mut x = op.rhs_and_dest_index.get(); |  | ||||||
|                 cells[y][x].plus = true; |  | ||||||
|                 x -= 1; |  | ||||||
|                 y -= 1; |  | ||||||
|                 while op.lhs_index < x { |  | ||||||
|                     cells[y][x].slant = true; |  | ||||||
|                     x -= 1; |  | ||||||
|                     y -= 1; |  | ||||||
|                 } |  | ||||||
|                 cells[y][x].tee = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         let mut retval = String::new(); |  | ||||||
|         let mut row_text = vec![String::new(); 2 * self.padding + 1]; |  | ||||||
|         for cells_row in cells { |  | ||||||
|             for cell in cells_row { |  | ||||||
|                 // top padding
 |  | ||||||
|                 for y in 0..self.padding { |  | ||||||
|                     // top left padding
 |  | ||||||
|                     for x in 0..self.padding { |  | ||||||
|                         row_text[y] += if x == y && (cell.plus || cell.slant) { |  | ||||||
|                             &self.slant |  | ||||||
|                         } else { |  | ||||||
|                             &self.space |  | ||||||
|                         }; |  | ||||||
|                     } |  | ||||||
|                     // top vertical bar
 |  | ||||||
|                     row_text[y] += &self.vertical_bar; |  | ||||||
|                     // top right padding
 |  | ||||||
|                     for _ in 0..self.padding { |  | ||||||
|                         row_text[y] += &self.space; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 // center left padding
 |  | ||||||
|                 for _ in 0..self.padding { |  | ||||||
|                     row_text[self.padding] += &self.space; |  | ||||||
|                 } |  | ||||||
|                 // center
 |  | ||||||
|                 row_text[self.padding] += if cell.plus { |  | ||||||
|                     &self.plus |  | ||||||
|                 } else if cell.tee { |  | ||||||
|                     &self.connect |  | ||||||
|                 } else if cell.slant { |  | ||||||
|                     &self.no_connect |  | ||||||
|                 } else { |  | ||||||
|                     &self.vertical_bar |  | ||||||
|                 }; |  | ||||||
|                 // center right padding
 |  | ||||||
|                 for _ in 0..self.padding { |  | ||||||
|                     row_text[self.padding] += &self.space; |  | ||||||
|                 } |  | ||||||
|                 let bottom_padding_start = self.padding + 1; |  | ||||||
|                 let bottom_padding_last = self.padding * 2; |  | ||||||
|                 // bottom padding
 |  | ||||||
|                 for y in bottom_padding_start..=bottom_padding_last { |  | ||||||
|                     // bottom left padding
 |  | ||||||
|                     for _ in 0..self.padding { |  | ||||||
|                         row_text[y] += &self.space; |  | ||||||
|                     } |  | ||||||
|                     // bottom vertical bar
 |  | ||||||
|                     row_text[y] += &self.vertical_bar; |  | ||||||
|                     // bottom right padding
 |  | ||||||
|                     for x in bottom_padding_start..=bottom_padding_last { |  | ||||||
|                         row_text[y] += if x == y && (cell.tee || cell.slant) { |  | ||||||
|                             &self.slant |  | ||||||
|                         } else { |  | ||||||
|                             &self.space |  | ||||||
|                         }; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             for line in &mut row_text { |  | ||||||
|                 retval += line.trim_end(); |  | ||||||
|                 retval += "\n"; |  | ||||||
|                 line.clear(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         retval |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Default for DiagramConfig { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         Self::new() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl PrefixSumOp { |  | ||||||
|     pub fn diagram(ops: impl IntoIterator<Item = Self>, item_count: usize) -> String { |  | ||||||
|         Self::diagram_with_config(ops, item_count, DiagramConfig::new()) |  | ||||||
|     } |  | ||||||
|     pub fn diagram_with_config( |  | ||||||
|         ops: impl IntoIterator<Item = Self>, |  | ||||||
|         item_count: usize, |  | ||||||
|         config: DiagramConfig, |  | ||||||
|     ) -> String { |  | ||||||
|         config.draw(ops, item_count) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |  | ||||||
| pub enum PrefixSumAlgorithm { |  | ||||||
|     /// Uses the algorithm from:
 |  | ||||||
|     /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel
 |  | ||||||
|     LowLatency, |  | ||||||
|     /// Uses the algorithm from:
 |  | ||||||
|     /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient
 |  | ||||||
|     WorkEfficient, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl PrefixSumAlgorithm { |  | ||||||
|     fn ops_impl(self, item_count: usize) -> Vec<PrefixSumOp> { |  | ||||||
|         let mut retval = Vec::new(); |  | ||||||
|         let mut distance = 1; |  | ||||||
|         let mut row = 0; |  | ||||||
|         while distance < item_count { |  | ||||||
|             let double_distance = distance |  | ||||||
|                 .checked_mul(2) |  | ||||||
|                 .expect("prefix-sum item_count is too big"); |  | ||||||
|             let (start, step) = match self { |  | ||||||
|                 Self::LowLatency => (distance, 1), |  | ||||||
|                 Self::WorkEfficient => (double_distance - 1, double_distance), |  | ||||||
|             }; |  | ||||||
|             for rhs_and_dest_index in (start..item_count).step_by(step).rev() { |  | ||||||
|                 let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { |  | ||||||
|                     unreachable!(); |  | ||||||
|                 }; |  | ||||||
|                 let lhs_index = rhs_and_dest_index.get() - distance; |  | ||||||
|                 retval.push(PrefixSumOp { |  | ||||||
|                     lhs_index, |  | ||||||
|                     rhs_and_dest_index, |  | ||||||
|                     row, |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             distance = double_distance; |  | ||||||
|             row += 1; |  | ||||||
|         } |  | ||||||
|         match self { |  | ||||||
|             Self::LowLatency => {} |  | ||||||
|             Self::WorkEfficient => { |  | ||||||
|                 distance /= 2; |  | ||||||
|                 while distance >= 1 { |  | ||||||
|                     let start = distance |  | ||||||
|                         .checked_mul(3) |  | ||||||
|                         .expect("prefix-sum item_count is too big") |  | ||||||
|                         - 1; |  | ||||||
|                     for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() { |  | ||||||
|                         let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { |  | ||||||
|                             unreachable!(); |  | ||||||
|                         }; |  | ||||||
|                         let lhs_index = rhs_and_dest_index.get() - distance; |  | ||||||
|                         retval.push(PrefixSumOp { |  | ||||||
|                             lhs_index, |  | ||||||
|                             rhs_and_dest_index, |  | ||||||
|                             row, |  | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                     row += 1; |  | ||||||
|                     distance /= 2; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         retval |  | ||||||
|     } |  | ||||||
|     pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { |  | ||||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |  | ||||||
|         struct MyMemoize(PrefixSumAlgorithm); |  | ||||||
|         impl Memoize for MyMemoize { |  | ||||||
|             type Input = usize; |  | ||||||
|             type InputOwned = usize; |  | ||||||
|             type Output = Interned<[PrefixSumOp]>; |  | ||||||
| 
 |  | ||||||
|             fn inner(self, item_count: &Self::Input) -> Self::Output { |  | ||||||
|                 Intern::intern_owned(self.0.ops_impl(*item_count)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         MyMemoize(self).get_owned(item_count) |  | ||||||
|     } |  | ||||||
|     pub fn run<T>(self, items: impl IntoIterator<Item = T>, f: impl FnMut(&T, &T) -> T) -> Vec<T> { |  | ||||||
|         let mut items = Vec::from_iter(items); |  | ||||||
|         self.run_on_slice(&mut items, f); |  | ||||||
|         items |  | ||||||
|     } |  | ||||||
|     pub fn run_on_slice<T>(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] { |  | ||||||
|         self.ops(items.len()).into_iter().for_each( |  | ||||||
|             |PrefixSumOp { |  | ||||||
|                  lhs_index, |  | ||||||
|                  rhs_and_dest_index, |  | ||||||
|                  row: _, |  | ||||||
|              }| { |  | ||||||
|                 items[rhs_and_dest_index.get()] = |  | ||||||
|                     f(&items[lhs_index], &items[rhs_and_dest_index.get()]); |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|         items |  | ||||||
|     } |  | ||||||
|     pub fn filtered_ops( |  | ||||||
|         self, |  | ||||||
|         item_live_out_flags: impl IntoIterator<Item = bool>, |  | ||||||
|     ) -> Vec<PrefixSumOp> { |  | ||||||
|         let mut item_live_out_flags = Vec::from_iter(item_live_out_flags); |  | ||||||
|         let prefix_sum_ops = self.ops(item_live_out_flags.len()); |  | ||||||
|         let mut ops_live_flags = vec![false; prefix_sum_ops.len()]; |  | ||||||
|         for ( |  | ||||||
|             op_index, |  | ||||||
|             &PrefixSumOp { |  | ||||||
|                 lhs_index, |  | ||||||
|                 rhs_and_dest_index, |  | ||||||
|                 row: _, |  | ||||||
|             }, |  | ||||||
|         ) in prefix_sum_ops.iter().enumerate().rev() |  | ||||||
|         { |  | ||||||
|             let live = item_live_out_flags[rhs_and_dest_index.get()]; |  | ||||||
|             item_live_out_flags[lhs_index] |= live; |  | ||||||
|             ops_live_flags[op_index] = live; |  | ||||||
|         } |  | ||||||
|         prefix_sum_ops |  | ||||||
|             .into_iter() |  | ||||||
|             .zip(ops_live_flags) |  | ||||||
|             .filter_map(|(op, live)| live.then_some(op)) |  | ||||||
|             .collect() |  | ||||||
|     } |  | ||||||
|     pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { |  | ||||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |  | ||||||
|         struct MyMemoize(PrefixSumAlgorithm); |  | ||||||
|         impl Memoize for MyMemoize { |  | ||||||
|             type Input = usize; |  | ||||||
|             type InputOwned = usize; |  | ||||||
|             type Output = Interned<[PrefixSumOp]>; |  | ||||||
| 
 |  | ||||||
|             fn inner(self, item_count: &Self::Input) -> Self::Output { |  | ||||||
|                 let mut item_live_out_flags = vec![false; *item_count]; |  | ||||||
|                 let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else { |  | ||||||
|                     return Interned::default(); |  | ||||||
|                 }; |  | ||||||
|                 *last_item_live_out_flag = true; |  | ||||||
|                 Intern::intern_owned(self.0.filtered_ops(item_live_out_flags)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         MyMemoize(self).get_owned(item_count) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> { |  | ||||||
|     PrefixSumAlgorithm::LowLatency.reduce_ops(item_count) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn reduce<T>(items: impl IntoIterator<Item = T>, mut f: impl FnMut(T, T) -> T) -> Option<T> { |  | ||||||
|     let mut items: Vec<_> = items.into_iter().map(Some).collect(); |  | ||||||
|     for op in reduce_ops(items.len()) { |  | ||||||
|         let (Some(lhs), Some(rhs)) = ( |  | ||||||
|             items[op.lhs_index].take(), |  | ||||||
|             items[op.rhs_and_dest_index.get()].take(), |  | ||||||
|         ) else { |  | ||||||
|             unreachable!(); |  | ||||||
|         }; |  | ||||||
|         items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs)); |  | ||||||
|     } |  | ||||||
|     items.last_mut().and_then(Option::take) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
| 
 |  | ||||||
|     fn input_strings() -> [String; 9] { |  | ||||||
|         std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_prefix_sum_strings() { |  | ||||||
|         let input = input_strings(); |  | ||||||
|         let expected: Vec<String> = input |  | ||||||
|             .iter() |  | ||||||
|             .scan(String::new(), |l, r| { |  | ||||||
|                 *l += r; |  | ||||||
|                 Some(l.clone()) |  | ||||||
|             }) |  | ||||||
|             .collect(); |  | ||||||
|         println!("expected: {expected:?}"); |  | ||||||
|         assert_eq!( |  | ||||||
|             *PrefixSumAlgorithm::WorkEfficient |  | ||||||
|                 .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), |  | ||||||
|             *expected |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             *PrefixSumAlgorithm::LowLatency |  | ||||||
|                 .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), |  | ||||||
|             *expected |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_reduce_string() { |  | ||||||
|         let input = input_strings(); |  | ||||||
|         let expected = input.clone().into_iter().reduce(|l, r| l + &r); |  | ||||||
|         assert_eq!(reduce(input, |l, r| l + &r), expected); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp { |  | ||||||
|         PrefixSumOp { |  | ||||||
|             lhs_index, |  | ||||||
|             rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"), |  | ||||||
|             row, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_reduce_ops_9() { |  | ||||||
|         let expected = vec![ |  | ||||||
|             op(7, 8, 0), |  | ||||||
|             op(5, 6, 0), |  | ||||||
|             op(3, 4, 0), |  | ||||||
|             op(1, 2, 0), |  | ||||||
|             op(6, 8, 1), |  | ||||||
|             op(2, 4, 1), |  | ||||||
|             op(4, 8, 2), |  | ||||||
|             op(0, 8, 3), |  | ||||||
|         ]; |  | ||||||
|         println!("expected: {expected:#?}"); |  | ||||||
|         let ops = reduce_ops(9); |  | ||||||
|         println!("ops: {ops:#?}"); |  | ||||||
|         assert_eq!(*ops, *expected); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_reduce_ops_8() { |  | ||||||
|         let expected = vec![ |  | ||||||
|             op(6, 7, 0), |  | ||||||
|             op(4, 5, 0), |  | ||||||
|             op(2, 3, 0), |  | ||||||
|             op(0, 1, 0), |  | ||||||
|             op(5, 7, 1), |  | ||||||
|             op(1, 3, 1), |  | ||||||
|             op(3, 7, 2), |  | ||||||
|         ]; |  | ||||||
|         println!("expected: {expected:#?}"); |  | ||||||
|         let ops = reduce_ops(8); |  | ||||||
|         println!("ops: {ops:#?}"); |  | ||||||
|         assert_eq!(*ops, *expected); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_count_ones() { |  | ||||||
|         for width in 0..=10u32 { |  | ||||||
|             for v in 0..1u32 << width { |  | ||||||
|                 let expected = v.count_ones(); |  | ||||||
|                 assert_eq!( |  | ||||||
|                     reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0), |  | ||||||
|                     expected, |  | ||||||
|                     "v={v:#X}" |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[track_caller] |  | ||||||
|     fn test_diagram(ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize, expected: &str) { |  | ||||||
|         let text = PrefixSumOp::diagram_with_config( |  | ||||||
|             ops, |  | ||||||
|             item_count, |  | ||||||
|             DiagramConfig { |  | ||||||
|                 plus: Cow::Borrowed("@"), |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|         ); |  | ||||||
|         println!("text:\n{text}\n"); |  | ||||||
|         assert_eq!(text, expected); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_work_efficient_diagram_16() { |  | ||||||
|         let item_count = 16; |  | ||||||
|         test_diagram( |  | ||||||
|             PrefixSumAlgorithm::WorkEfficient.ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
|  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  | |  | ||||||
|  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ | |  | ||||||
|  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | \| |  | ||||||
|  |  @  |  @  |  @  |  @  |  @  |  @  |  @  |  @ |  | ||||||
|  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ |  | |  | ||||||
|  |  | \|  |  |  | \|  |  |  | \|  |  |  | \|  | |  | ||||||
|  |  |  X  |  |  |  X  |  |  |  X  |  |  |  X  | |  | ||||||
|  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ | |  | ||||||
|  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | \| |  | ||||||
|  |  |  |  @  |  |  |  @  |  |  |  @  |  |  |  @ |  | ||||||
|  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  | \|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  X  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  @  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  ●  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  ●  |  |  |  ●  |  |  |  @  |  |  |  | |  | ||||||
|  |  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  X  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | \|  |  |  | \|  |  | |  | ||||||
|  |  ●  |  ●  |  @  |  ●  |  @  |  ●  |  @  |  | |  | ||||||
|  |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  | |  | ||||||
|  |  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | |  | ||||||
|  |  |  @  |  @  |  @  |  @  |  @  |  @  |  @  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_low_latency_diagram_16() { |  | ||||||
|         let item_count = 16; |  | ||||||
|         test_diagram( |  | ||||||
|             PrefixSumAlgorithm::LowLatency.ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
|  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  | |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  | \| \| \| \| \| \| \| \| \| \| \| \| \| \| \| |  | ||||||
|  ●  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @ |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  | |  | ||||||
|  | \| \| \| \| \| \| \| \| \| \| \| \| \| \|  | |  | ||||||
|  |  X  X  X  X  X  X  X  X  X  X  X  X  X  X  | |  | ||||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  |  | \| \| \| \| \| \| \| \| \| \| \| \| \| \| |  | ||||||
|  ●  ●  @  @  @  @  @  @  @  @  @  @  @  @  @  @ |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  | |  | ||||||
|  | \| \| \| \| \| \| \| \| \| \| \| \|  |  |  | |  | ||||||
|  |  X  X  X  X  X  X  X  X  X  X  X  X  |  |  | |  | ||||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  |  | |  | ||||||
|  |  | \| \| \| \| \| \| \| \| \| \| \| \|  |  | |  | ||||||
|  |  |  X  X  X  X  X  X  X  X  X  X  X  X  |  | |  | ||||||
|  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  | |  | ||||||
|  |  |  | \| \| \| \| \| \| \| \| \| \| \| \|  | |  | ||||||
|  |  |  |  X  X  X  X  X  X  X  X  X  X  X  X  | |  | ||||||
|  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  |  |  |  | \| \| \| \| \| \| \| \| \| \| \| \| |  | ||||||
|  ●  ●  ●  ●  @  @  @  @  @  @  @  @  @  @  @  @ |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  |  |  | |  | ||||||
|  | \| \| \| \| \| \| \| \|  |  |  |  |  |  |  | |  | ||||||
|  |  X  X  X  X  X  X  X  X  |  |  |  |  |  |  | |  | ||||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  |  | |  | ||||||
|  |  | \| \| \| \| \| \| \| \|  |  |  |  |  |  | |  | ||||||
|  |  |  X  X  X  X  X  X  X  X  |  |  |  |  |  | |  | ||||||
|  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  | |  | ||||||
|  |  |  | \| \| \| \| \| \| \| \|  |  |  |  |  | |  | ||||||
|  |  |  |  X  X  X  X  X  X  X  X  |  |  |  |  | |  | ||||||
|  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  | |  | ||||||
|  |  |  |  | \| \| \| \| \| \| \| \|  |  |  |  | |  | ||||||
|  |  |  |  |  X  X  X  X  X  X  X  X  |  |  |  | |  | ||||||
|  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \| \| \| \| \| \| \| \|  |  |  | |  | ||||||
|  |  |  |  |  |  X  X  X  X  X  X  X  X  |  |  | |  | ||||||
|  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  | |  | ||||||
|  |  |  |  |  |  | \| \| \| \| \| \| \| \|  |  | |  | ||||||
|  |  |  |  |  |  |  X  X  X  X  X  X  X  X  |  | |  | ||||||
|  |  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  | |  | ||||||
|  |  |  |  |  |  |  | \| \| \| \| \| \| \| \|  | |  | ||||||
|  |  |  |  |  |  |  |  X  X  X  X  X  X  X  X  | |  | ||||||
|  |  |  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  |  |  |  |  |  |  |  | \| \| \| \| \| \| \| \| |  | ||||||
|  |  |  |  |  |  |  |  |  @  @  @  @  @  @  @  @ |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_work_efficient_diagram_9() { |  | ||||||
|         let item_count = 9; |  | ||||||
|         test_diagram( |  | ||||||
|             PrefixSumAlgorithm::WorkEfficient.ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
|  ●  |  ●  |  ●  |  ●  |  | |  | ||||||
|  |\ |  |\ |  |\ |  |\ |  | |  | ||||||
|  | \|  | \|  | \|  | \|  | |  | ||||||
|  |  @  |  @  |  @  |  @  | |  | ||||||
|  |  |\ |  |  |  |\ |  |  | |  | ||||||
|  |  | \|  |  |  | \|  |  | |  | ||||||
|  |  |  X  |  |  |  X  |  | |  | ||||||
|  |  |  |\ |  |  |  |\ |  | |  | ||||||
|  |  |  | \|  |  |  | \|  | |  | ||||||
|  |  |  |  @  |  |  |  @  | |  | ||||||
|  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  ●  |  |  |  @  | |  | ||||||
|  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  ●  |  ●  |  @  |  ●  | |  | ||||||
|  |  |\ |  |\ |  |\ |  |\ | |  | ||||||
|  |  | \|  | \|  | \|  | \| |  | ||||||
|  |  |  @  |  @  |  @  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_low_latency_diagram_9() { |  | ||||||
|         let item_count = 9; |  | ||||||
|         test_diagram( |  | ||||||
|             PrefixSumAlgorithm::LowLatency.ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
|  ●  ●  ●  ●  ●  ●  ●  ●  | |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  | \| \| \| \| \| \| \| \| |  | ||||||
|  ●  @  @  @  @  @  @  @  @ |  | ||||||
|  |\ |\ |\ |\ |\ |\ |\ |  | |  | ||||||
|  | \| \| \| \| \| \| \|  | |  | ||||||
|  |  X  X  X  X  X  X  X  | |  | ||||||
|  |  |\ |\ |\ |\ |\ |\ |\ | |  | ||||||
|  |  | \| \| \| \| \| \| \| |  | ||||||
|  ●  ●  @  @  @  @  @  @  @ |  | ||||||
|  |\ |\ |\ |\ |\ |  |  |  | |  | ||||||
|  | \| \| \| \| \|  |  |  | |  | ||||||
|  |  X  X  X  X  X  |  |  | |  | ||||||
|  |  |\ |\ |\ |\ |\ |  |  | |  | ||||||
|  |  | \| \| \| \| \|  |  | |  | ||||||
|  |  |  X  X  X  X  X  |  | |  | ||||||
|  |  |  |\ |\ |\ |\ |\ |  | |  | ||||||
|  |  |  | \| \| \| \| \|  | |  | ||||||
|  |  |  |  X  X  X  X  X  | |  | ||||||
|  |  |  |  |\ |\ |\ |\ |\ | |  | ||||||
|  |  |  |  | \| \| \| \| \| |  | ||||||
|  ●  |  |  |  @  @  @  @  @ |  | ||||||
|  |\ |  |  |  |  |  |  |  | |  | ||||||
|  | \|  |  |  |  |  |  |  | |  | ||||||
|  |  X  |  |  |  |  |  |  | |  | ||||||
|  |  |\ |  |  |  |  |  |  | |  | ||||||
|  |  | \|  |  |  |  |  |  | |  | ||||||
|  |  |  X  |  |  |  |  |  | |  | ||||||
|  |  |  |\ |  |  |  |  |  | |  | ||||||
|  |  |  | \|  |  |  |  |  | |  | ||||||
|  |  |  |  X  |  |  |  |  | |  | ||||||
|  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_reduce_diagram_16() { |  | ||||||
|         let item_count = 16; |  | ||||||
|         test_diagram( |  | ||||||
|             reduce_ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
|  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  | |  | ||||||
|  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ | |  | ||||||
|  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | \| |  | ||||||
|  |  @  |  @  |  @  |  @  |  @  |  @  |  @  |  @ |  | ||||||
|  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ |  | |  | ||||||
|  |  | \|  |  |  | \|  |  |  | \|  |  |  | \|  | |  | ||||||
|  |  |  X  |  |  |  X  |  |  |  X  |  |  |  X  | |  | ||||||
|  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ | |  | ||||||
|  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | \| |  | ||||||
|  |  |  |  @  |  |  |  @  |  |  |  @  |  |  |  @ |  | ||||||
|  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  | \|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  X  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  @  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_reduce_diagram_9() { |  | ||||||
|         let item_count = 9; |  | ||||||
|         test_diagram( |  | ||||||
|             reduce_ops(item_count), |  | ||||||
|             item_count, |  | ||||||
|             &r" |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
|  |  ●  |  ●  |  ●  |  ●  | |  | ||||||
|  |  |\ |  |\ |  |\ |  |\ | |  | ||||||
|  |  | \|  | \|  | \|  | \| |  | ||||||
|  |  |  @  |  @  |  @  |  @ |  | ||||||
|  |  |  |\ |  |  |  |\ |  | |  | ||||||
|  |  |  | \|  |  |  | \|  | |  | ||||||
|  |  |  |  X  |  |  |  X  | |  | ||||||
|  |  |  |  |\ |  |  |  |\ | |  | ||||||
|  |  |  |  | \|  |  |  | \| |  | ||||||
|  |  |  |  |  @  |  |  |  @ |  | ||||||
|  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  |  | \| |  | ||||||
|  ●  |  |  |  |  |  |  |  @ |  | ||||||
|  |\ |  |  |  |  |  |  |  | |  | ||||||
|  | \|  |  |  |  |  |  |  | |  | ||||||
|  |  X  |  |  |  |  |  |  | |  | ||||||
|  |  |\ |  |  |  |  |  |  | |  | ||||||
|  |  | \|  |  |  |  |  |  | |  | ||||||
|  |  |  X  |  |  |  |  |  | |  | ||||||
|  |  |  |\ |  |  |  |  |  | |  | ||||||
|  |  |  | \|  |  |  |  |  | |  | ||||||
|  |  |  |  X  |  |  |  |  | |  | ||||||
|  |  |  |  |\ |  |  |  |  | |  | ||||||
|  |  |  |  | \|  |  |  |  | |  | ||||||
|  |  |  |  |  X  |  |  |  | |  | ||||||
|  |  |  |  |  |\ |  |  |  | |  | ||||||
|  |  |  |  |  | \|  |  |  | |  | ||||||
|  |  |  |  |  |  X  |  |  | |  | ||||||
|  |  |  |  |  |  |\ |  |  | |  | ||||||
|  |  |  |  |  |  | \|  |  | |  | ||||||
|  |  |  |  |  |  |  X  |  | |  | ||||||
|  |  |  |  |  |  |  |\ |  | |  | ||||||
|  |  |  |  |  |  |  | \|  | |  | ||||||
|  |  |  |  |  |  |  |  X  | |  | ||||||
|  |  |  |  |  |  |  |  |\ | |  | ||||||
|  |  |  |  |  |  |  |  | \| |  | ||||||
|  |  |  |  |  |  |  |  |  @ |  | ||||||
|  |  |  |  |  |  |  |  |  | |  | ||||||
| "[1..], // trim newline at start
 |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -191,14 +191,10 @@ circuit check_array_repeat: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait UnknownTrait {} |  | ||||||
| 
 |  | ||||||
| impl<T: ?Sized> UnknownTrait for T {} |  | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | ||||||
| where | where | ||||||
|     T: StaticType + UnknownTrait, |     T: StaticType, | ||||||
|     ConstUsize<N>: KnownSize, |     ConstUsize<N>: KnownSize, | ||||||
|     U: std::fmt::Display, |     U: std::fmt::Display, | ||||||
| { | { | ||||||
|  | @ -380,18 +376,18 @@ circuit check_written_inside_both_if_else: | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(outline_generated, cmp_eq)] | #[hdl(outline_generated)] | ||||||
| pub struct TestStruct<T> { | pub struct TestStruct<T> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|     pub b: UInt<8>, |     pub b: UInt<8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(outline_generated, cmp_eq)] | #[hdl(outline_generated)] | ||||||
| pub struct TestStruct2 { | pub struct TestStruct2 { | ||||||
|     pub v: UInt<8>, |     pub v: UInt<8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(outline_generated, cmp_eq)] | #[hdl(outline_generated)] | ||||||
| pub struct TestStruct3 {} | pub struct TestStruct3 {} | ||||||
| 
 | 
 | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
|  | @ -4425,125 +4421,3 @@ circuit check_let_patterns: | ||||||
| ",
 | ",
 | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] |  | ||||||
| pub fn check_struct_cmp_eq() { |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_cmp_eq: Bool = m.output(); |  | ||||||
|     connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs)); |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_cmp_ne: Bool = m.output(); |  | ||||||
|     connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs)); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_lhs: TestStruct<SInt<8>> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_rhs: TestStruct<SInt<8>> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_cmp_eq: Bool = m.output(); |  | ||||||
|     connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs)); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_cmp_ne: Bool = m.output(); |  | ||||||
|     connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs)); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_lhs: TestStruct2 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_rhs: TestStruct2 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_cmp_eq: Bool = m.output(); |  | ||||||
|     connect( |  | ||||||
|         test_struct_2_cmp_eq, |  | ||||||
|         test_struct_2_lhs.cmp_eq(test_struct_2_rhs), |  | ||||||
|     ); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_cmp_ne: Bool = m.output(); |  | ||||||
|     connect( |  | ||||||
|         test_struct_2_cmp_ne, |  | ||||||
|         test_struct_2_lhs.cmp_ne(test_struct_2_rhs), |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_3_lhs: TestStruct3 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_3_rhs: TestStruct3 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_3_cmp_eq: Bool = m.output(); |  | ||||||
|     connect( |  | ||||||
|         test_struct_3_cmp_eq, |  | ||||||
|         test_struct_3_lhs.cmp_eq(test_struct_3_rhs), |  | ||||||
|     ); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_3_cmp_ne: Bool = m.output(); |  | ||||||
|     connect( |  | ||||||
|         test_struct_3_cmp_ne, |  | ||||||
|         test_struct_3_lhs.cmp_ne(test_struct_3_rhs), |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_struct_cmp_eq() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let m = check_struct_cmp_eq(); |  | ||||||
|     dbg!(m); |  | ||||||
|     #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
 |  | ||||||
|     assert_export_firrtl! { |  | ||||||
|         m => |  | ||||||
|         "/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
 |  | ||||||
| circuit check_struct_cmp_eq: |  | ||||||
|     type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} |  | ||||||
|     type Ty1 = {a: SInt<8>, b: UInt<8>} |  | ||||||
|     type Ty2 = {v: UInt<8>} |  | ||||||
|     type Ty3 = {} |  | ||||||
|     module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1] |  | ||||||
|         input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] |  | ||||||
|         input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] |  | ||||||
|         output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] |  | ||||||
|         output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1] |  | ||||||
|         input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1] |  | ||||||
|         input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1] |  | ||||||
|         output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1] |  | ||||||
|         output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1] |  | ||||||
|         input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1] |  | ||||||
|         input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1] |  | ||||||
|         output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1] |  | ||||||
|         output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1] |  | ||||||
|         input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1] |  | ||||||
|         input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] |  | ||||||
|         output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] |  | ||||||
|         output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] |  | ||||||
|         wire _array_literal_expr: UInt<1>[3] |  | ||||||
|         connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) |  | ||||||
|         connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) |  | ||||||
|         connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) |  | ||||||
|         wire _cast_array_to_bits_expr: UInt<1>[3] |  | ||||||
|         connect _cast_array_to_bits_expr[0], _array_literal_expr[0] |  | ||||||
|         connect _cast_array_to_bits_expr[1], _array_literal_expr[1] |  | ||||||
|         connect _cast_array_to_bits_expr[2], _array_literal_expr[2] |  | ||||||
|         wire _cast_to_bits_expr: UInt<3> |  | ||||||
|         connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) |  | ||||||
|         connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] |  | ||||||
|         wire _array_literal_expr_1: UInt<1>[3] |  | ||||||
|         connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) |  | ||||||
|         connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) |  | ||||||
|         connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) |  | ||||||
|         wire _cast_array_to_bits_expr_1: UInt<1>[3] |  | ||||||
|         connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] |  | ||||||
|         connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] |  | ||||||
|         connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] |  | ||||||
|         wire _cast_to_bits_expr_1: UInt<3> |  | ||||||
|         connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) |  | ||||||
|         connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] |  | ||||||
|         connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1] |  | ||||||
|         connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1] |  | ||||||
|         connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] |  | ||||||
|         connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1] |  | ||||||
|         connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1] |  | ||||||
|         connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1] |  | ||||||
| ",
 |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -49,8 +49,7 @@ | ||||||
|                 "AsyncReset": "Visible", |                 "AsyncReset": "Visible", | ||||||
|                 "SyncReset": "Visible", |                 "SyncReset": "Visible", | ||||||
|                 "Reset": "Visible", |                 "Reset": "Visible", | ||||||
|                 "Clock": "Visible", |                 "Clock": "Visible" | ||||||
|                 "PhantomConst": "Visible" |  | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "Bundle": { |         "Bundle": { | ||||||
|  | @ -1263,12 +1262,6 @@ | ||||||
|                 "ArrayElement": "Visible", |                 "ArrayElement": "Visible", | ||||||
|                 "DynArrayElement": "Visible" |                 "DynArrayElement": "Visible" | ||||||
|             } |             } | ||||||
|         }, |  | ||||||
|         "PhantomConst": { |  | ||||||
|             "data": { |  | ||||||
|                 "$kind": "Opaque" |  | ||||||
|             }, |  | ||||||
|             "generics": "<T: ?Sized + crate::phantom_const::PhantomConstValue>" |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue