diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f952f42..97fa3ff 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -396,15 +396,29 @@ impl ToTokens for Builder { quote_spanned! {self.ident.span()=> #[automatically_derived] #[allow(non_camel_case_types, dead_code, private_interfaces)] - impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty + impl #filled_impl_generics ::fayalite::expr::ValueType for #filled_ty #filled_where_clause { type Type = #target #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategoryExpr; + + fn ty(&self) -> ::Type { + #target { + #(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)* + } + } + } + + #[automatically_derived] + #[allow(non_camel_case_types, dead_code, private_interfaces)] + impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty + #filled_where_clause + { fn to_expr( &self, - ) -> ::fayalite::expr::Expr<::Type> { + ) -> ::fayalite::expr::Expr<::Type> { let __ty = #target { - #(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)* + #(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)* }; let __field_values = [ #(::fayalite::expr::Expr::canonical(self.#field_idents),)* @@ -695,10 +709,10 @@ impl ToTokens for ParsedBundle { v.field(&value.#ident); } })); - let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let value_type_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); quote_spanned! {span=> - #ident: ::fayalite::sim::value::SimValue::ty(&self.#ident), + #ident: ::fayalite::expr::ValueType::ty(&self.#ident), } })); let fields_len = fields.named().into_iter().len(); @@ -806,28 +820,39 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] - impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics #where_clause { type Type = #mask_type_ident #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + #mask_type_ident { + #(#value_type_fields)* + } + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + #where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #mask_type_ident { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) } fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #mask_type_ident { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, self) } @@ -955,28 +980,39 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] - impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics #where_clause { type Type = #target #type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + #target { + #(#value_type_fields)* + } + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + #where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #target { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) } fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { let ty = #target { - #(#to_sim_value_fields)* + #(#value_type_fields)* }; ::fayalite::sim::value::SimValue::from_value(ty, self) } @@ -1002,91 +1038,172 @@ impl ToTokens for ParsedBundle { } .to_tokens(tokens); if let Some((cmp_eq,)) = cmp_eq { - let mut expr_where_clause = + let mut cmp_eq_where_clause = Generics::from(generics) .where_clause .unwrap_or_else(|| syn::WhereClause { where_token: Token![where](span), predicates: Punctuated::new(), }); - let mut sim_value_where_clause = expr_where_clause.clone(); - let mut fields_sim_value_eq = vec![]; - let mut fields_cmp_eq = vec![]; - let mut fields_cmp_ne = vec![]; + let mut fields_value_eq = vec![]; + let mut fields_value_ne = vec![]; + let mut fields_expr_eq = vec![]; + let mut fields_expr_ne = vec![]; + let mut fields_valueless_eq = vec![]; + let mut fields_valueless_ne = vec![]; for field in fields.named() { let field_ident = field.ident(); let field_ty = field.ty(); - expr_where_clause + cmp_eq_where_clause .predicates .push(parse_quote_spanned! {cmp_eq.span=> - #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> + #field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty> }); - sim_value_where_clause - .predicates - .push(parse_quote_spanned! {cmp_eq.span=> - #field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty> - }); - fields_sim_value_eq.push(quote_spanned! {span=> - ::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident) + fields_value_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_value_eq( + __lhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident), + __rhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident), + ) }); - fields_cmp_eq.push(quote_spanned! {span=> - ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) + fields_value_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_value_ne( + __lhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident), + __rhs.#field_ident, + ::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident), + ) }); - fields_cmp_ne.push(quote_spanned! {span=> - ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) + fields_expr_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq( + __lhs.#field_ident, + __rhs.#field_ident, + ) + }); + fields_expr_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne( + __lhs.#field_ident, + __rhs.#field_ident, + ) + }); + fields_valueless_eq.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq( + ::fayalite::expr::Valueless::new(__lhs.#field_ident), + ::fayalite::expr::Valueless::new(__rhs.#field_ident), + ) + }); + fields_valueless_ne.push(quote_spanned! {span=> + ::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne( + ::fayalite::expr::Valueless::new(__lhs.#field_ident), + ::fayalite::expr::Valueless::new(__rhs.#field_ident), + ) }); } - let sim_value_eq_body; - let cmp_eq_body; - let cmp_ne_body; + let value_eq_body; + let value_ne_body; + let expr_eq_body; + let expr_ne_body; + let valueless_eq_body; + let valueless_ne_body; if fields_len == 0 { - sim_value_eq_body = quote_spanned! {span=> + value_eq_body = quote_spanned! {span=> true }; - cmp_eq_body = quote_spanned! {span=> + value_ne_body = quote_spanned! {span=> + false + }; + expr_eq_body = quote_spanned! {span=> ::fayalite::expr::ToExpr::to_expr(&true) }; - cmp_ne_body = quote_spanned! {span=> + expr_ne_body = quote_spanned! {span=> ::fayalite::expr::ToExpr::to_expr(&false) }; + valueless_eq_body = quote_spanned! {span=> + ::fayalite::expr::Valueless::new(::fayalite::int::Bool) + }; + valueless_ne_body = quote_spanned! {span=> + ::fayalite::expr::Valueless::new(::fayalite::int::Bool) + }; } else { - sim_value_eq_body = quote_spanned! {span=> - #(#fields_sim_value_eq)&&* + value_eq_body = quote_spanned! {span=> + #(#fields_value_eq)&* }; - cmp_eq_body = quote_spanned! {span=> - #(#fields_cmp_eq)&* + value_ne_body = quote_spanned! {span=> + #(#fields_value_ne)|* }; - cmp_ne_body = quote_spanned! {span=> - #(#fields_cmp_ne)|* + expr_eq_body = quote_spanned! {span=> + #(#fields_expr_eq)&* + }; + expr_ne_body = quote_spanned! {span=> + #(#fields_expr_ne)|* + }; + valueless_eq_body = quote_spanned! {span=> + let __lhs = ::fayalite::expr::ValueType::ty(&__lhs); + let __rhs = ::fayalite::expr::ValueType::ty(&__rhs); + #(#fields_valueless_eq)|* + }; + valueless_ne_body = quote_spanned! {span=> + let __lhs = ::fayalite::expr::ValueType::ty(&__lhs); + let __rhs = ::fayalite::expr::ValueType::ty(&__rhs); + #(#fields_valueless_ne)|* }; }; quote_spanned! {span=> #[automatically_derived] - impl #impl_generics ::fayalite::expr::ops::ExprPartialEq for #target #type_generics - #expr_where_clause + impl #impl_generics ::fayalite::expr::HdlPartialEqImpl for #target #type_generics + #cmp_eq_where_clause { - fn cmp_eq( + #[track_caller] + fn cmp_value_eq( + __lhs: Self, + __lhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __rhs: Self, + __rhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + ) -> ::fayalite::__std::primitive::bool { + #value_eq_body + } + + #[track_caller] + fn cmp_value_ne( + __lhs: Self, + __lhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __rhs: Self, + __rhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + ) -> ::fayalite::__std::primitive::bool { + #value_ne_body + } + + #[track_caller] + fn cmp_expr_eq( __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { - #cmp_eq_body + #expr_eq_body } - fn cmp_ne( + + #[track_caller] + fn cmp_expr_ne( __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { - #cmp_ne_body + #expr_ne_body } - } - #[automatically_derived] - impl #impl_generics ::fayalite::sim::value::SimValuePartialEq for #target #type_generics - #sim_value_where_clause - { - fn sim_value_eq( - __lhs: &::fayalite::sim::value::SimValue, - __rhs: &::fayalite::sim::value::SimValue, - ) -> bool { - #sim_value_eq_body + + #[track_caller] + fn cmp_valueless_eq( + __lhs: ::fayalite::expr::Valueless, + __rhs: ::fayalite::expr::Valueless, + ) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> { + #valueless_eq_body + } + + #[track_caller] + fn cmp_valueless_ne( + __lhs: ::fayalite::expr::Valueless, + __rhs: ::fayalite::expr::Valueless, + ) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> { + #valueless_ne_body } } } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 885cf87..90838f0 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -1024,16 +1024,26 @@ impl ToTokens for ParsedEnum { <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; } #[automatically_derived] - impl #static_impl_generics ::fayalite::sim::value::ToSimValue + impl #static_impl_generics ::fayalite::expr::ValueType for #sim_value_ident #static_type_generics #static_where_clause { type Type = #target #static_type_generics; + type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue; + fn ty(&self) -> ::Type { + ::fayalite::ty::StaticType::TYPE + } + } + #[automatically_derived] + impl #static_impl_generics ::fayalite::sim::value::ToSimValue + for #sim_value_ident #static_type_generics + #static_where_clause + { fn to_sim_value( &self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { ::fayalite::sim::value::SimValue::from_value( ::fayalite::ty::StaticType::TYPE, @@ -1043,7 +1053,7 @@ impl ToTokens for ParsedEnum { fn into_sim_value( self, ) -> ::fayalite::sim::value::SimValue< - ::Type, + ::Type, > { ::fayalite::sim::value::SimValue::from_value( ::fayalite::ty::StaticType::TYPE, diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 13ec7a2..152053c 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -887,7 +887,13 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr } } let _print_on_panic = PrintOnPanic(&contents); - let contents = prettyplease::unparse(&parse_quote! { #contents }); + let mut parse_err = None; + let (Ok(contents) | Err(contents)) = syn::parse2(contents.clone()) + .map(|file| prettyplease::unparse(&file)) + .map_err(|e| { + parse_err = Some(e); + contents.to_string() + }); let hash = ::digest(&contents); let hash = base16ct::HexDisplay(&hash[..5]); file.write_all(contents.as_bytes()).unwrap(); @@ -899,9 +905,26 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr e.unwrap(); } } - eprintln!("generated {}", dest_file.display()); + let log_msg = if let Some(parse_err) = parse_err { + format!( + "fayalite-proc-macros-impl internal error:\nfailed to parse generated output: {parse_err}\nunformatted output is in: {}\n", + dest_file.display() + ) + } else { + format!("generated {}\n", dest_file.display()) + }; + // write message atomically if possible + let mut stderr = std::io::stderr().lock(); + let write_result = stderr.write_all(log_msg.as_bytes()); + let flush_result = stderr.flush(); + drop(stderr); // unlock before we try to panic + write_result.unwrap(); + flush_result.unwrap(); + std::io::stderr() + .lock() + .write_all(log_msg.as_bytes()) + .unwrap(); let dest_file = dest_file.to_str().unwrap(); - quote! { include!(#dest_file); } diff --git a/crates/fayalite/examples/blinky.rs b/crates/fayalite/examples/blinky.rs index 75799fd..d2cdb33 100644 --- a/crates/fayalite/examples/blinky.rs +++ b/crates/fayalite/examples/blinky.rs @@ -12,7 +12,7 @@ fn blinky(platform_io_builder: PlatformIOBuilder<'_>) { clk: clk_input.clk, rst, }; - let max_value = (Expr::ty(clk_input).frequency() / 2.0).round_ties_even() as u64 - 1; + let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1; let int_ty = UInt::range_inclusive(0..=max_value); #[hdl] let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); diff --git a/crates/fayalite/examples/tx_only_uart.rs b/crates/fayalite/examples/tx_only_uart.rs index 5c20b39..59e2968 100644 --- a/crates/fayalite/examples/tx_only_uart.rs +++ b/crates/fayalite/examples/tx_only_uart.rs @@ -105,7 +105,7 @@ fn tx_only_uart( connect(tx, tx_bits[uart_state_reg]); #[hdl] - if uart_state_reg.cmp_eq(Expr::ty(tx_bits).len() - 1) { + if uart_state_reg.cmp_eq(tx_bits.ty().len() - 1) { connect(next_uart_state, 0_hdl_u4); let next_addr_val = addr_reg + 1u8; #[hdl] diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 569f2e2..4e2b223 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -3,13 +3,13 @@ use crate::{ expr::{ - CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, - ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, + CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless, + ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator}, }, int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, - sim::value::{SimValue, SimValuePartialEq}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, @@ -19,7 +19,7 @@ use crate::{ util::ConstUsize, }; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; -use std::{iter::FusedIterator, ops::Index}; +use std::{borrow::Cow, iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ArrayType { @@ -221,7 +221,7 @@ impl Type for ArrayType { let value = AsMut::<[SimValue]>::as_mut(value); assert_eq!(self.len(), value.len()); for element_value in value { - assert_eq!(SimValue::ty(element_value), element_ty); + assert_eq!(element_value.ty(), element_ty); let (element_opaque, rest) = opaque.split_at(element_size); SimValue::opaque_mut(element_value).clone_from_slice(element_opaque); opaque = rest; @@ -238,7 +238,7 @@ impl Type for ArrayType { let value = AsRef::<[SimValue]>::as_ref(value); assert_eq!(self.len(), value.len()); for element_value in value { - assert_eq!(SimValue::ty(element_value), element_ty); + assert_eq!(element_value.ty(), element_ty); writer.fill_prefix_with(element_size, |writer| { writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) }); @@ -326,14 +326,30 @@ impl Index for ArrayWithoutLen { } } -impl ExprPartialEq> for ArrayType +impl HdlPartialEqImpl> for ArrayType where - Lhs: ExprPartialEq, + Lhs: HdlPartialEqImpl, { - fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { - let lhs_ty = Expr::ty(lhs); - let rhs_ty = Expr::ty(rhs); - assert_eq!(lhs_ty.len(), rhs_ty.len()); + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: ArrayType, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + assert_eq!(lhs.len(), rhs.len()); + let lhs = lhs.element(); + let rhs = rhs.element(); + let lhs_value: &[_] = (*lhs_value).as_ref(); + let rhs_value: &[_] = (*rhs_value).as_ref(); + for (lhs_value, rhs_value) in lhs_value.iter().zip(rhs_value) { + if !Lhs::cmp_value_eq(lhs, Cow::Borrowed(lhs_value), rhs, Cow::Borrowed(rhs_value)) { + return false; + } + } + true + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_eq(r)) @@ -341,11 +357,8 @@ where .cast_to_bits() .all_one_bits() } - - fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { - let lhs_ty = Expr::ty(lhs); - let rhs_ty = Expr::ty(rhs); - assert_eq!(lhs_ty.len(), rhs_ty.len()); + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_ne(r)) @@ -353,17 +366,19 @@ where .cast_to_bits() .any_one_bits() } -} - -impl SimValuePartialEq> for ArrayType -where - Lhs: SimValuePartialEq, -{ - fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { - AsRef::<[_]>::as_ref(&**this) - .iter() - .zip(AsRef::<[_]>::as_ref(&**other)) - .all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r)) + fn cmp_valueless_eq( + lhs: Valueless, + rhs: Valueless>, + ) -> Valueless { + assert_eq!(lhs.ty().len(), rhs.ty().len()); + Valueless::new(Bool) + } + fn cmp_valueless_ne( + lhs: Valueless, + rhs: Valueless>, + ) -> Valueless { + assert_eq!(lhs.ty().len(), rhs.ty().len()); + Valueless::new(Bool) } } @@ -374,7 +389,7 @@ impl ExprIntoIterator for ArrayType { fn expr_into_iter(e: Expr) -> Self::ExprIntoIter { ExprArrayIter { base: e, - indexes: 0..Expr::ty(e).len(), + indexes: 0..e.ty().len(), } } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 0edf192..1471f3a 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -3,12 +3,14 @@ use crate::{ expr::{ - CastToBits, Expr, ReduceBits, ToExpr, - ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType, + Valueless, + ops::{ArrayLiteral, BundleLiteral}, + value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue}, }, int::{Bool, DynSize}, intern::{Intern, InternSlice, Interned}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, @@ -18,7 +20,7 @@ use crate::{ util::HashMap, }; use serde::{Deserialize, Serialize}; -use std::{fmt, marker::PhantomData}; +use std::{borrow::Cow, fmt, marker::PhantomData}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct BundleField { @@ -317,7 +319,7 @@ impl<'a> BundleSimValueFromOpaque<'a> { #[track_caller] pub fn field_clone_from_opaque(&mut self, field_value: &mut SimValue) { let (field_ty, field_opaque) = self.field_ty_and_opaque::(); - assert_eq!(field_ty, SimValue::ty(field_value)); + assert_eq!(field_ty, field_value.ty()); SimValue::opaque_mut(field_value).clone_from_slice(field_opaque); } } @@ -353,7 +355,7 @@ impl<'a> BundleSimValueToOpaque<'a> { else { panic!("tried to write too many fields with BundleSimValueToOpaque"); }; - assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); + assert_eq!(T::from_canonical(ty), field_value.ty()); self.writer.fill_prefix_with(ty.size(), |writer| { writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice()) }); @@ -566,23 +568,54 @@ macro_rules! impl_tuples { builder.finish() }; } - impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { + impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*) + where + Self: ValueType, + { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + let ($($var,)*) = this; + Cow::Owned(($($var.to_sim_value(),)*)) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + let ($($var,)*) = this; + Cow::Owned(($($var.into_sim_value(),)*)) + } + } + impl<$($T: ValueType,)*> ValueType for ($($T,)*) + where + ValueCategoryValue: ValueCategoryCommon<($($T::ValueCategory,)*)>, + { type Type = ($($T::Type,)*); - + type ValueCategory = >::Common; + fn ty(&self) -> Self::Type { + let ($($var,)*) = self; + ($($var.ty(),)*) + } + } + impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) + where + Self: ValueType, + { fn to_expr(&self) -> Expr { let ($($var,)*) = self; $(let $var = $var.to_expr();)* - let ty = ($(Expr::ty($var),)*); + let ty = ($($var.ty(),)*); let field_values = [$(Expr::canonical($var)),*]; BundleLiteral::new(ty, field_values.intern_slice()).to_expr() } } - impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> { + impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> { type Type = ($($T,)*); - + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + let ($($var,)*) = self.0; + ($($var.ty(),)*) + } + } + impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> { fn to_expr(&self) -> Expr { let ($($var,)*) = self.0; - let ty = ($(Expr::ty($var),)*); + let ty = ($($var.ty(),)*); let field_values = [$(Expr::canonical($var)),*]; BundleLiteral::new(ty, field_values.intern_slice()).to_expr() } @@ -618,7 +651,7 @@ macro_rules! impl_tuples { }; let mut opaque = OpaqueSimValue::empty(); $(let $var = $var.into_sim_value_with_type($ty_var.ty); - assert_eq!(SimValue::ty(&$var), $ty_var.ty); + assert_eq!($var.ty(), $ty_var.ty); opaque.extend_from_slice(SimValue::opaque(&$var).as_slice()); )* SimValue::from_opaque(ty, opaque) @@ -640,53 +673,82 @@ macro_rules! impl_tuples { SimValue::from_value(ty, ($($var,)*)) } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { - type Type = ($($T::Type,)*); + impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) + where + Self: ValueType, + { #[track_caller] fn to_sim_value(&self) -> SimValue { let ($($var,)*) = self; $(let $var = $var.to_sim_value();)* - SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + SimValue::from_value(($($var.ty(),)*), ($($var,)*)) } #[track_caller] fn into_sim_value(self) -> SimValue { let ($($var,)*) = self; $(let $var = $var.to_sim_value();)* - SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + SimValue::from_value(($($var.ty(),)*), ($($var,)*)) } } - impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { - fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) { + #[track_caller] + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: ($($Rhs,)*), + rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>, + ) -> bool { + #![allow(unused_variables)] + let ($($lhs_var,)*) = &*lhs_value; + let ($($rhs_var,)*) = &*rhs_value; + let retval = true; + $(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)* + retval + } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { let ($($lhs_var,)*) = *lhs; let ($($rhs_var,)*) = *rhs; ArrayLiteral::::new( Bool, - FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), + FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]), ) .cast_to_bits() .all_one_bits() } - fn cmp_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { let ($($lhs_var,)*) = *lhs; let ($($rhs_var,)*) = *rhs; ArrayLiteral::::new( Bool, - FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), + FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]), ) .cast_to_bits() .any_one_bits() } - } - impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) { - fn sim_value_eq(lhs: &SimValue, rhs: &SimValue<($($Rhs,)*)>) -> bool { - let ($($lhs_var,)*) = &**lhs; - let ($($rhs_var,)*) = &**rhs; - let retval = true; - $(let retval = retval && $lhs_var == $rhs_var;)* - retval + + #[track_caller] + fn cmp_valueless_eq(lhs: Valueless, rhs: Valueless<($($Rhs,)*)>) -> Valueless { + let ($($lhs_var,)*) = lhs.ty(); + let ($($rhs_var,)*) = rhs.ty(); + // let them check that the types can be compared + $($Lhs::cmp_valueless_eq(Valueless::new($lhs_var), Valueless::new($rhs_var));)* + Valueless::new(Bool) + } + + #[track_caller] + fn cmp_valueless_ne(lhs: Valueless, rhs: Valueless<($($Rhs,)*)>) -> Valueless { + let ($($lhs_var,)*) = lhs.ty(); + let ($($rhs_var,)*) = rhs.ty(); + // let them check that the types can be compared + $($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)* + Valueless::new(Bool) } } + impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {} }; ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { impl_tuples!([$($lhs)*] []); @@ -775,9 +837,16 @@ impl Default for PhantomDataBuilder { } } -impl ToExpr for PhantomDataBuilder { +impl ValueType for PhantomDataBuilder { type Type = PhantomData; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + PhantomData + } +} + +impl ToExpr for PhantomDataBuilder { fn to_expr(&self) -> Expr { PhantomData.to_expr() } @@ -803,17 +872,22 @@ impl StaticType for PhantomData { const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; } -impl ToExpr for PhantomData { +impl ValueType for PhantomData { type Type = PhantomData; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + PhantomData + } +} + +impl ToExpr for PhantomData { fn to_expr(&self) -> Expr { BundleLiteral::new(PhantomData, Interned::default()).to_expr() } } impl ToSimValue for PhantomData { - type Type = PhantomData; - #[track_caller] fn to_sim_value(&self) -> SimValue { SimValue::from_value(*self, *self) diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 909edbd..168142b 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -1,10 +1,11 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::{Expr, ToExpr}, + expr::{Expr, ValueType}, hdl, int::Bool, reset::{Reset, ResetType}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, @@ -91,29 +92,34 @@ impl StaticType for Clock { } pub trait ToClock { - fn to_clock(&self) -> Expr; + type Output: ValueType; + fn to_clock(&self) -> Self::Output; } impl ToClock for &'_ T { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for &'_ mut T { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for Box { - fn to_clock(&self) -> Expr { + type Output = T::Output; + fn to_clock(&self) -> Self::Output { (**self).to_clock() } } impl ToClock for Expr { - fn to_clock(&self) -> Expr { + type Output = Expr; + fn to_clock(&self) -> Self::Output { *self } } @@ -125,7 +131,25 @@ pub struct ClockDomain { } impl ToClock for bool { - fn to_clock(&self) -> Expr { - self.to_expr().to_clock() + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + SimValue::from_value(Clock, *self) + } +} + +impl ToClock for SimValue { + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + SimValue::from_value(Clock, **self) + } +} + +impl ToClock for SimValue { + type Output = SimValue; + + fn to_clock(&self) -> Self::Output { + self.clone() } } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 083072b..a04f67a 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -2,10 +2,7 @@ // See Notices.txt for copyright information use crate::{ - expr::{ - Expr, ToExpr, - ops::{ExprPartialEq, VariantAccess}, - }, + expr::{Expr, HdlPartialEq, HdlPartialEqImpl, ToExpr, ValueType, ops::VariantAccess}, hdl, int::{Bool, UIntValue}, intern::{Intern, Interned}, @@ -13,7 +10,7 @@ use crate::{ EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect, enum_match_variants_helper, incomplete_wire, wire, }, - sim::value::{SimValue, SimValuePartialEq}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, @@ -24,7 +21,7 @@ use crate::{ }; use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; use serde::{Deserialize, Serialize}; -use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; +use std::{borrow::Cow, convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct EnumVariant { @@ -599,7 +596,7 @@ impl<'a> EnumSimValueFromOpaque<'a> { let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else { self.usage_error(true); }; - assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + assert_eq!(value.ty(), T::from_canonical(variant_ty)); SimValue::bits_mut(value) .bits_mut() .copy_from_bitslice(variant_bits); @@ -711,7 +708,7 @@ impl<'a> EnumSimValueToOpaque<'a> { let Some(variant_ty) = self.variants[discriminant].ty else { panic!("expected variant to have no field"); }; - assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + assert_eq!(value.ty(), T::from_canonical(variant_ty)); self.known_variant(discriminant, Some(SimValue::opaque(value)), padding) } } @@ -732,9 +729,38 @@ pub enum HdlOption { HdlSome(T), } -impl, Rhs: Type> ExprPartialEq> for HdlOption { +impl, Rhs: Type> HdlPartialEqImpl> + for HdlOption +{ + fn cmp_value_eq( + lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + rhs: HdlOption, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + type SimValueMatch = ::SimValue; + match (&*lhs_value, &*rhs_value) { + (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlNone(_)) => { + true + } + (SimValueMatch::::HdlSome(..), SimValueMatch::>::HdlNone(_)) + | (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlSome(..)) => { + false + } + ( + SimValueMatch::::HdlSome(l, _), + SimValueMatch::>::HdlSome(r, _), + ) => HdlPartialEqImpl::cmp_value_eq( + lhs.HdlSome, + Cow::Borrowed(&**l), + rhs.HdlSome, + Cow::Borrowed(&**r), + ), + } + } + #[hdl] - fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { #[hdl] let cmp_eq = wire(); #[hdl] @@ -743,7 +769,7 @@ impl, Rhs: Type> ExprPartialEq> fo { #[hdl] match rhs { - HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), + HdlSome(rhs) => connect(cmp_eq, lhs.cmp_eq(rhs)), HdlNone => connect(cmp_eq, false), } } @@ -760,7 +786,7 @@ impl, Rhs: Type> ExprPartialEq> fo } #[hdl] - fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { #[hdl] let cmp_ne = wire(); #[hdl] @@ -769,7 +795,7 @@ impl, Rhs: Type> ExprPartialEq> fo { #[hdl] match rhs { - HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), + HdlSome(rhs) => connect(cmp_ne, lhs.cmp_ne(rhs)), HdlNone => connect(cmp_ne, true), } } @@ -786,25 +812,6 @@ impl, Rhs: Type> ExprPartialEq> fo } } -impl, Rhs: Type> SimValuePartialEq> for HdlOption { - fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { - type SimValueMatch = ::SimValue; - match (&**this, &**other) { - (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlNone(_)) => { - true - } - (SimValueMatch::::HdlSome(..), SimValueMatch::>::HdlNone(_)) - | (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlSome(..)) => { - false - } - ( - SimValueMatch::::HdlSome(l, _), - SimValueMatch::>::HdlSome(r, _), - ) => l == r, - } - } -} - #[allow(non_snake_case)] pub fn HdlNone() -> Expr> { HdlOption[T::TYPE].HdlNone() @@ -813,7 +820,7 @@ pub fn HdlNone() -> Expr> { #[allow(non_snake_case)] pub fn HdlSome(value: impl ToExpr) -> Expr> { let value = value.to_expr(); - HdlOption[Expr::ty(value)].HdlSome(value) + HdlOption[value.ty()].HdlSome(value) } impl HdlOption { @@ -853,7 +860,7 @@ impl HdlOption { let value = f(value).inspect_err(|_| { and_then_out.complete(()); // avoid error })?; - let and_then_out = and_then_out.complete(Expr::ty(value)); + let and_then_out = and_then_out.complete(value.ty()); connect(and_then_out, value); drop(some_scope); let (Wrap::<::MatchVariant>::HdlNone, none_scope) = @@ -861,7 +868,7 @@ impl HdlOption { else { unreachable!(); }; - connect(and_then_out, Expr::ty(and_then_out).HdlNone()); + connect(and_then_out, and_then_out.ty().HdlNone()); drop(none_scope); Ok(and_then_out) } @@ -879,8 +886,8 @@ impl HdlOption { #[track_caller] pub fn and(expr: Expr, opt_b: Expr>) -> Expr> { #[hdl] - let and_out = wire(Expr::ty(opt_b)); - connect(and_out, Expr::ty(opt_b).HdlNone()); + let and_out = wire(opt_b.ty()); + connect(and_out, opt_b.ty().HdlNone()); #[hdl] if let HdlSome(_) = expr { connect(and_out, opt_b); @@ -894,8 +901,8 @@ impl HdlOption { f: impl FnOnce(Expr) -> Result, E>, ) -> Result, E> { #[hdl] - let filtered = wire(Expr::ty(expr)); - connect(filtered, Expr::ty(expr).HdlNone()); + let filtered = wire(expr.ty()); + connect(filtered, expr.ty().HdlNone()); let mut f = Some(f); #[hdl] if let HdlSome(v) = expr { @@ -969,7 +976,7 @@ impl HdlOption { f: impl FnOnce(Expr) -> Expr, ) -> Expr { #[hdl] - let mapped = wire(Expr::ty(default)); + let mapped = wire(default.ty()); let mut f = Some(f); #[hdl] match expr { @@ -994,12 +1001,12 @@ impl HdlOption { match expr { HdlSome(v) => { let v = f.take().unwrap()(v); - let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); connect(mapped, v); } HdlNone => { let v = default.take().unwrap()(); - let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty())); connect(mapped, v); } } @@ -1009,7 +1016,7 @@ impl HdlOption { #[track_caller] pub fn or(expr: Expr, opt_b: Expr) -> Expr { #[hdl] - let or_out = wire(Expr::ty(expr)); + let or_out = wire(expr.ty()); connect(or_out, opt_b); #[hdl] if let HdlSome(_) = expr { @@ -1021,7 +1028,7 @@ impl HdlOption { #[track_caller] pub fn or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { #[hdl] - let or_else_out = wire(Expr::ty(expr)); + let or_else_out = wire(expr.ty()); connect(or_else_out, f()); #[hdl] if let HdlSome(_) = expr { @@ -1033,7 +1040,7 @@ impl HdlOption { #[track_caller] pub fn unwrap_or(expr: Expr, default: Expr) -> Expr { #[hdl] - let unwrap_or_else_out = wire(Expr::ty(default)); + let unwrap_or_else_out = wire(default.ty()); connect(unwrap_or_else_out, default); #[hdl] if let HdlSome(v) = expr { @@ -1045,7 +1052,7 @@ impl HdlOption { #[track_caller] pub fn unwrap_or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { #[hdl] - let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome); + let unwrap_or_else_out = wire(expr.ty().HdlSome); connect(unwrap_or_else_out, f()); #[hdl] if let HdlSome(v) = expr { @@ -1057,14 +1064,14 @@ impl HdlOption { #[track_caller] pub fn xor(expr: Expr, opt_b: Expr) -> Expr { #[hdl] - let xor_out = wire(Expr::ty(expr)); + let xor_out = wire(expr.ty()); #[hdl] if let HdlSome(_) = expr { #[hdl] if let HdlNone = opt_b { connect(xor_out, expr); } else { - connect(xor_out, Expr::ty(expr).HdlNone()); + connect(xor_out, expr.ty().HdlNone()); } } else { connect(xor_out, opt_b); @@ -1075,8 +1082,8 @@ impl HdlOption { #[track_caller] pub fn zip(expr: Expr, other: Expr>) -> Expr> { #[hdl] - let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]); - connect(zip_out, Expr::ty(zip_out).HdlNone()); + let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]); + connect(zip_out, zip_out.ty().HdlNone()); #[hdl] if let HdlSome(l) = expr { #[hdl] @@ -1093,11 +1100,11 @@ impl HdlOption> { #[track_caller] pub fn flatten(expr: Expr) -> Expr> { #[hdl] - let flattened = wire(Expr::ty(expr).HdlSome); + let flattened = wire(expr.ty().HdlSome); #[hdl] match expr { HdlSome(v) => connect(flattened, v), - HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()), + HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()), } flattened } @@ -1107,7 +1114,7 @@ impl HdlOption<(T, U)> { #[hdl] #[track_caller] pub fn unzip(expr: Expr) -> Expr<(HdlOption, HdlOption)> { - let (t, u) = Expr::ty(expr).HdlSome; + let (t, u) = expr.ty().HdlSome; #[hdl] let unzipped = wire((HdlOption[t], HdlOption[u])); connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone())); diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 89e60cd..00a0cee 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -5,11 +5,8 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, enum_::{Enum, EnumType}, - expr::{ - ops::ExprCastTo, - target::{GetTarget, Target}, - }, - int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, + expr::target::{GetTarget, Target}, + int::{Bool, DynSize, IntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, module::{ @@ -19,14 +16,17 @@ use crate::{ phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, - ty::{CanonicalType, StaticType, Type, TypeWithDeref}, + sim::value::{SimValue, ToSimValue, ToSimValueWithType}, + ty::{CanonicalType, OpaqueSimValue, StaticType, Type, TypeWithDeref}, + util::{ConstBool, ConstUsize}, wire::Wire, }; use bitvec::slice::BitSlice; -use std::{convert::Infallible, fmt, ops::Deref}; +use std::{borrow::Cow, convert::Infallible, fmt, ops::Deref}; pub mod ops; pub mod target; +pub mod value_category; macro_rules! expr_enum { ( @@ -77,9 +77,18 @@ macro_rules! expr_enum { } } - impl ToExpr for $ExprEnum { + impl ValueType for $ExprEnum { type Type = CanonicalType; + type ValueCategory = value_category::ValueCategoryExpr; + fn ty(&self) -> Self::Type { + match self { + $(Self::$Variant(v) => v.ty().canonical(),)* + } + } + } + + impl ToExpr for $ExprEnum { fn to_expr(&self) -> Expr { match self { $(Self::$Variant(v) => Expr::canonical(v.to_expr()),)* @@ -282,7 +291,7 @@ impl fmt::Debug for Expr { __flow, } = self; let expr_ty = __ty.canonical(); - let enum_ty = __enum.to_expr().__ty; + let enum_ty = __enum.to_expr().ty(); assert_eq!( expr_ty, enum_ty, "expr ty mismatch:\nExpr {{\n__enum: {__enum:?},\n__ty: {__ty:?},\n__flow: {__flow:?}\n}}" @@ -296,23 +305,20 @@ impl Expr { pub fn expr_enum(this: Self) -> Interned { this.__enum } - pub fn ty(this: Self) -> T { - this.__ty - } pub fn flow(this: Self) -> Flow { this.__flow } pub fn canonical(this: Self) -> Expr { Expr { __enum: this.__enum, - __ty: this.__ty.canonical(), + __ty: this.ty().canonical(), __flow: this.__flow, } } pub fn from_canonical(this: Expr) -> Self { Expr { __enum: this.__enum, - __ty: T::from_canonical(this.__ty), + __ty: T::from_canonical(this.ty()), __flow: this.__flow, } } @@ -322,7 +328,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_dyn_int(this.__ty), + __ty: T::from_dyn_int(this.ty()), __flow: this.__flow, } } @@ -332,7 +338,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: this.__ty.as_dyn_int(), + __ty: this.ty().as_dyn_int(), __flow: this.__flow, } } @@ -342,7 +348,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Bundle::new(this.__ty.fields()), + __ty: Bundle::new(this.ty().fields()), __flow: this.__flow, } } @@ -352,7 +358,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_canonical(CanonicalType::Bundle(this.__ty)), + __ty: T::from_canonical(CanonicalType::Bundle(this.ty())), __flow: this.__flow, } } @@ -369,7 +375,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Enum::new(this.__ty.variants()), + __ty: Enum::new(this.ty().variants()), __flow: this.__flow, } } @@ -379,7 +385,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: T::from_canonical(CanonicalType::Enum(this.__ty)), + __ty: T::from_canonical(CanonicalType::Enum(this.ty())), __flow: this.__flow, } } @@ -409,7 +415,7 @@ impl Expr> { pub fn as_dyn_array(this: Self) -> Expr { Expr { __enum: this.__enum, - __ty: this.__ty.as_dyn_array(), + __ty: this.ty().as_dyn_array(), __flow: this.__flow, } } @@ -435,54 +441,41 @@ impl Visit for Expr { } } -pub trait ToExpr { - type Type: Type; +pub trait ToExpr: ValueType + ToValueless { fn to_expr(&self) -> Expr; } impl ToExpr for Expr { - type Type = T; - fn to_expr(&self) -> Expr { *self } } impl ToExpr for &'_ T { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for &'_ mut T { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for Box { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for Interned { - type Type = T::Type; - fn to_expr(&self) -> Expr { T::to_expr(self) } } impl ToExpr for UIntValue { - type Type = UIntType; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::UIntLiteral(self.clone().as_dyn_int().intern()).intern(), @@ -493,8 +486,6 @@ impl ToExpr for UIntValue { } impl ToExpr for SIntValue { - type Type = SIntType; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::SIntLiteral(self.clone().as_dyn_int().intern()).intern(), @@ -504,9 +495,16 @@ impl ToExpr for SIntValue { } } -impl ToExpr for bool { +impl ValueType for bool { type Type = Bool; + type ValueCategory = value_category::ValueCategoryValue; + fn ty(&self) -> Self::Type { + Bool + } +} + +impl ToExpr for bool { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::BoolLiteral(*self).intern(), @@ -537,8 +535,6 @@ impl Flow { } impl ToExpr for ModuleIO { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ModuleIO(self.canonical()).intern_sized(), @@ -561,8 +557,6 @@ impl GetTarget for ModuleIO { } impl ToExpr for Instance { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Instance(self.canonical()).intern_sized(), @@ -585,8 +579,6 @@ impl GetTarget for Instance { } impl ToExpr for Wire { - type Type = T; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Wire(self.canonical()).intern_sized(), @@ -609,8 +601,6 @@ impl GetTarget for Wire { } impl ToExpr for Reg { - type Type = T; - fn to_expr(&self) -> Expr { struct Dispatch; impl ResetTypeDispatch for Dispatch { @@ -650,8 +640,6 @@ impl GetTarget for Reg { } impl ToExpr for MemPort { - type Type = T::Port; - fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::MemPort(self.canonical()).intern_sized(), @@ -673,69 +661,840 @@ impl GetTarget for MemPort { } } -pub trait HdlPartialEq { - fn cmp_eq(self, rhs: Rhs) -> Expr; - fn cmp_ne(self, rhs: Rhs) -> Expr; +macro_rules! impl_hdl_cmp { + ( + #[ + impl_helper = $HdlCmpImplHelper:ident, + $(impl_helper_base = $HdlCmpImplHelperBase:ident,)? + impl_helper_sealed = $HdlCmpImplHelperSealed:ident, + ] + $vis:vis trait $HdlCmp:ident<$Rhs:ident: ValueType>: + ValueType $(+ $HdlCmpImplBase:ident)?> $(+ $HdlCmpBase:ident)? + { + $(type $Output:ident: ValueType;)? + $(#[ + helper = $cmp_fn_helper:ident; + value = $cmp_value_fn:ident( + $cmp_value_lhs:ident, + $cmp_value_lhs_value:ident, + $cmp_value_rhs:ident, + $cmp_value_rhs_value:ident $(,)? + ) $cmp_value_body:tt + sim_value = $cmp_sim_value_fn:ident; + expr = $cmp_expr_fn:ident($cmp_expr_lhs:ident, $cmp_expr_rhs:ident) $cmp_expr_body:tt + valueless = $cmp_valueless_fn:ident; + ] + fn $cmp_fn:ident(&self, rhs: Rhs) -> Self::Output;)+ + } + ) => { + $vis trait $HdlCmp<$Rhs: ValueType>: + ValueType> $(+ $HdlCmpBase<$Rhs>)? + { + $(type $Output: ValueType;)? + $(fn $cmp_fn(&self, rhs: $Rhs) -> Self::Output;)+ + } + + $vis trait $HdlCmpImpl<$Rhs: Type>: Type $(+ $HdlCmpImplBase<$Rhs>)? { + $(#[track_caller] + fn $cmp_value_fn( + $cmp_value_lhs: Self, + $cmp_value_lhs_value: Cow<'_, Self::SimValue>, + $cmp_value_rhs: $Rhs, + $cmp_value_rhs_value: Cow<'_, $Rhs::SimValue>, + ) -> bool + $cmp_value_body)+ + + $(#[track_caller] + fn $cmp_sim_value_fn(lhs: Cow<'_, SimValue>, rhs: Cow<'_, SimValue<$Rhs>>) -> SimValue { + let lhs_ty = lhs.ty(); + let rhs_ty = rhs.ty(); + let lhs = match lhs { + Cow::Borrowed(v) => Cow::Borrowed(&**v), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + let rhs = match rhs { + Cow::Borrowed(v) => Cow::Borrowed(&**v), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + Self::$cmp_value_fn(lhs_ty, lhs, rhs_ty, rhs).to_sim_value() + })+ + + $(#[track_caller] + fn $cmp_expr_fn($cmp_expr_lhs: Expr, $cmp_expr_rhs: Expr<$Rhs>) -> Expr + $cmp_expr_body)+ + + $(#[track_caller] + fn $cmp_valueless_fn(lhs: Valueless, rhs: Valueless<$Rhs>) -> Valueless { + let _ = lhs; + let _ = rhs; + Valueless::new(Bool) + })+ + } + + trait $HdlCmpImplHelperSealed<$Rhs, Common> {} + + #[expect(private_bounds)] + $vis trait $HdlCmpImplHelper<$Rhs: ValueType, Common: value_category::ValueCategory>: + $HdlCmpImplHelperSealed<$Rhs, Common> + + ValueType> + $(+ $HdlCmpImplHelperBase<$Rhs, Common>)? + { + $(type $Output: ValueType;)? + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: $Rhs) -> Self::Output;)+ + } + + impl $HdlCmp<$Rhs> for Lhs + where + Lhs: ?Sized + ValueType $(+ $HdlCmpBase<$Rhs>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + LC: value_category::ValueCategory + value_category::ValueCategoryCommon<(RC,), Common = Common>, + RC: value_category::ValueCategory, + Common: value_category::ValueCategory, + Lhs: $HdlCmpImplHelper<$Rhs, Common> $(+ $HdlCmpImplHelperBase<$Rhs, Common, Output = Self::Output>)?, + { + $(type $Output = >::Output;)? + + $(#[track_caller] + fn $cmp_fn(&self, rhs: $Rhs) -> Self::Output { + >::$cmp_fn_helper(self, rhs) + })+ + } + + impl<'l, 'r, Lhs, $Rhs, LTy, RTy> $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValueInner<'l> + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValue, Output = bool>)?, + $Rhs: ValueType + ToSimValueInner<'r>, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl<'l, 'r, Lhs, $Rhs, LTy, RTy> $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValueInner<'l> + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValue, Output = bool>)?, + $Rhs: ValueType + ToSimValueInner<'r>, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = bool;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_value_fn( + self.ty(), + Lhs::to_sim_value_inner(self), + rhs.ty(), + $Rhs::into_sim_value_inner(rhs), + ) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategorySimValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValue + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategorySimValue, Output = SimValue>)?, + $Rhs: ValueType + ToSimValue, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategorySimValue> for Lhs + where + Lhs: ?Sized + ValueType + ToSimValue + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategorySimValue, Output = SimValue>)?, + $Rhs: ValueType + ToSimValue, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = SimValue;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_sim_value_fn(Cow::Owned(Lhs::to_sim_value(self)), Cow::Owned($Rhs::into_sim_value(rhs))) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryExpr> for Lhs + where + Lhs: ?Sized + ValueType + ToExpr + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryExpr, Output = Expr>)?, + $Rhs: ValueType + ToExpr, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryExpr> for Lhs + where + Lhs: ?Sized + ValueType + ToExpr + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryExpr, Output = Expr>)?, + $Rhs: ValueType + ToExpr, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = Expr;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_expr_fn(Lhs::to_expr(self), $Rhs::to_expr(&rhs)) + })+ + } + + impl $HdlCmpImplHelperSealed<$Rhs, value_category::ValueCategoryValueless> for Lhs + where + Lhs: ?Sized + ValueType + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValueless, Output = Valueless>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + } + + impl $HdlCmpImplHelper<$Rhs, value_category::ValueCategoryValueless> for Lhs + where + Lhs: ?Sized + ValueType + $(+ $HdlCmpImplHelperBase<$Rhs, value_category::ValueCategoryValueless, Output = Valueless>)?, + $Rhs: ValueType, + LTy: Type + $HdlCmpImpl, + RTy: Type, + { + $(type $Output = Valueless;)? + + $(#[track_caller] + fn $cmp_fn_helper(&self, rhs: Rhs) -> Self::Output { + $HdlCmpImpl::$cmp_valueless_fn(Lhs::to_valueless(self), $Rhs::to_valueless(&rhs)) + })+ + } + }; } -pub trait HdlPartialOrd: HdlPartialEq { - fn cmp_lt(self, rhs: Rhs) -> Expr; - fn cmp_le(self, rhs: Rhs) -> Expr; - fn cmp_gt(self, rhs: Rhs) -> Expr; - fn cmp_ge(self, rhs: Rhs) -> Expr; -} - -pub trait ReduceBits { - type UIntOutput; - type BoolOutput; - fn reduce_bitand(self) -> Self::UIntOutput; - fn reduce_bitor(self) -> Self::UIntOutput; - fn reduce_bitxor(self) -> Self::UIntOutput; - fn any_one_bits(self) -> Self::BoolOutput; - fn any_zero_bits(self) -> Self::BoolOutput; - fn all_one_bits(self) -> Self::BoolOutput; - fn all_zero_bits(self) -> Self::BoolOutput; - fn parity_odd(self) -> Self::BoolOutput; - fn parity_even(self) -> Self::BoolOutput; -} - -pub trait CastToBits { - fn cast_to_bits(&self) -> Expr; -} - -impl CastToBits for T { - fn cast_to_bits(&self) -> Expr { - ops::CastToBits::new(Expr::canonical(self.to_expr())).to_expr() +impl_hdl_cmp! { + #[ + impl_helper = HdlPartialEqImplHelper, + impl_helper_sealed = HdlPartialEqImplHelperSealed, + ] + pub trait HdlPartialEq: + ValueType > + { + type Output: ValueType; + #[ + helper = cmp_eq_helper; + value = cmp_value_eq(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_eq; + expr = cmp_expr_eq(lhs, rhs); + valueless = cmp_valueless_eq; + ] + fn cmp_eq(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_ne_helper; + value = cmp_value_ne(lhs, lhs_value, rhs, rhs_value) { + !Self::cmp_value_eq(lhs, lhs_value, rhs, rhs_value) + } + sim_value = cmp_sim_value_ne; + expr = cmp_expr_ne(lhs, rhs) { + !Self::cmp_expr_eq(lhs, rhs) + } + valueless = cmp_valueless_ne; + ] + fn cmp_ne(&self, rhs: Rhs) -> Self::Output; } } -pub trait CastBitsTo { +impl_hdl_cmp! { + #[ + impl_helper = HdlPartialOrdImplHelper, + impl_helper_base = HdlPartialEqImplHelper, + impl_helper_sealed = HdlPartialOrdImplHelperSealed, + ] + pub trait HdlPartialOrd: + ValueType + HdlPartialEqImpl > + + HdlPartialEq + { + #[ + helper = cmp_lt_helper; + value = cmp_value_lt(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_lt; + expr = cmp_expr_lt(lhs, rhs); + valueless = cmp_valueless_lt; + ] + fn cmp_lt(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_le_helper; + value = cmp_value_le(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_le; + expr = cmp_expr_le(lhs, rhs); + valueless = cmp_valueless_le; + ] + fn cmp_le(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_gt_helper; + value = cmp_value_gt(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_gt; + expr = cmp_expr_gt(lhs, rhs); + valueless = cmp_valueless_gt; + ] + fn cmp_gt(&self, rhs: Rhs) -> Self::Output; + #[ + helper = cmp_ge_helper; + value = cmp_value_ge(lhs, lhs_value, rhs, rhs_value); + sim_value = cmp_sim_value_ge; + expr = cmp_expr_ge(lhs, rhs); + valueless = cmp_valueless_ge; + ] + fn cmp_ge(&self, rhs: Rhs) -> Self::Output; + } +} + +macro_rules! make_reduce_bits_traits { + ( + $vis:vis trait $ReduceBits:ident { + $(type $Output:ident: ValueType;)* + $(#[ + ty = $ty:ty, + value_ty = $value_ty:ty, + value = $value_f:ident($value_this:ident, $value_value:ident) $value_body:tt + sim_value = $sim_value_f:ident, + expr = $expr_f:ident($expr_this:ident) $expr_body:tt + valueless = $valueless_f:ident, + ] + fn $f:ident(&self) -> Self::$FnOutput:ident;)* + } + ) => { + $vis trait $ReduceBits { + $(type $Output: ValueType;)* + $(fn $f(&self) -> Self::$FnOutput;)* + } + + trait ReduceBitsImplHelperSealed {} + + #[expect(private_bounds)] + $vis trait ReduceBitsImplHelper: ReduceBitsImplHelperSealed { + $(type $Output: ValueType;)* + $(fn $f(this: &Self) -> Self::$FnOutput;)* + } + + impl::ValueCategory>> $ReduceBits for T { + $(type $Output = ::ValueCategory>>::$Output;)* + $(fn $f(&self) -> Self::$FnOutput { + ::ValueCategory>>::$f(self) + })* + } + + $vis trait ReduceBitsImpl: Type { + $(fn $value_f($value_this: Self, $value_value: Cow<'_, Self::SimValue>) -> $value_ty $value_body)* + + $(fn $sim_value_f(this: Cow<'_, SimValue>) -> SimValue<$ty> { + let ty = this.ty(); + let value = match this { + Cow::Borrowed(v) => Cow::Borrowed(SimValue::value(v)), + Cow::Owned(v) => Cow::Owned(SimValue::into_value(v)), + }; + Self::$value_f(ty, value).into_sim_value() + })* + + $(fn $expr_f($expr_this: Expr) -> Expr<$ty> $expr_body)* + + $(fn $valueless_f(_this: Valueless) -> Valueless<$ty> { + Valueless::new(StaticType::TYPE) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToSimValueInner<'a>, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToSimValueInner<'a>, + { + type UIntOutput = UIntValue>; + type BoolOutput = bool; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$value_f(this.ty(), Self::to_sim_value_inner(this)) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToSimValue, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToSimValue, + { + type UIntOutput = SimValue>; + type BoolOutput = SimValue; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$sim_value_f(Cow::Owned(this.to_sim_value())) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType + ToExpr, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType + ToExpr, + { + type UIntOutput = Expr>; + type BoolOutput = Expr; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$expr_f(this.to_expr()) + })* + } + + impl<'a, T> ReduceBitsImplHelperSealed for T + where + T: ?Sized + ValueType, + { + } + + impl<'a, T> ReduceBitsImplHelper for T + where + T: ?Sized + ValueType, + { + type UIntOutput = Valueless>; + type BoolOutput = Valueless; + + $(fn $f(this: &Self) -> Self::$FnOutput { + T::Type::$valueless_f(this.to_valueless()) + })* + } + }; +} + +make_reduce_bits_traits! { + pub trait ReduceBits { + type UIntOutput: ValueType>; + type BoolOutput: ValueType; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitand(this, value); + sim_value = sim_value_reduce_bitand, + expr = expr_reduce_bitand(this); + valueless = valueless_reduce_bitand, + ] + fn reduce_bitand(&self) -> Self::UIntOutput; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitor(this, value); + sim_value = sim_value_reduce_bitor, + expr = expr_reduce_bitor(this); + valueless = valueless_reduce_bitor, + ] + fn reduce_bitor(&self) -> Self::UIntOutput; + + #[ + ty = UInt<1>, + value_ty = UIntValue>, + value = value_reduce_bitxor(this, value); + sim_value = sim_value_reduce_bitxor, + expr = expr_reduce_bitxor(this); + valueless = valueless_reduce_bitxor, + ] + fn reduce_bitxor(&self) -> Self::UIntOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_any_one_bits(this, value) { + Self::value_reduce_bitor(this, value).bits()[0] + } + sim_value = sim_value_any_one_bits, + expr = expr_any_one_bits(this) { + this.reduce_bitor().cast_to_static() + } + valueless = valueless_any_one_bits, + ] + fn any_one_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_any_zero_bits(this, value) { + !Self::value_reduce_bitand(this, value).bits()[0] + } + sim_value = sim_value_any_zero_bits, + expr = expr_any_zero_bits(this) { + (!this.reduce_bitand()).cast_to_static() + } + valueless = valueless_any_zero_bits, + ] + fn any_zero_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_all_one_bits(this, value) { + Self::value_reduce_bitand(this, value).bits()[0] + } + sim_value = sim_value_all_one_bits, + expr = expr_all_one_bits(this) { + this.reduce_bitand().cast_to_static() + } + valueless = valueless_all_one_bits, + ] + fn all_one_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_all_zero_bits(this, value) { + !Self::value_reduce_bitor(this, value).bits()[0] + } + sim_value = sim_value_all_zero_bits, + expr = expr_all_zero_bits(this) { + (!this.reduce_bitor()).cast_to_static() + } + valueless = valueless_all_zero_bits, + ] + fn all_zero_bits(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_parity_odd(this, value) { + Self::value_reduce_bitxor(this, value).bits()[0] + } + sim_value = sim_value_parity_odd, + expr = expr_parity_odd(this) { + this.reduce_bitxor().cast_to_static() + } + valueless = valueless_parity_odd, + ] + fn parity_odd(&self) -> Self::BoolOutput; + + #[ + ty = Bool, + value_ty = bool, + value = value_parity_even(this, value) { + !Self::value_reduce_bitxor(this, value).bits()[0] + } + sim_value = sim_value_parity_even, + expr = expr_parity_even(this) { + (!this.reduce_bitxor()).cast_to_static() + } + valueless = valueless_parity_even, + ] + fn parity_even(&self) -> Self::BoolOutput; + } +} + +pub trait CastToBits: ValueType { + type Output: ValueType; + fn cast_to_bits(&self) -> Self::Output; +} + +impl> CastToBits for T { + type Output = T::ImplOutput; + fn cast_to_bits(&self) -> Self::Output { + Self::cast_to_bits_impl(self) + } +} + +pub trait CastToBitsImpl { + type ImplOutput: ValueType; + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput; +} + +impl + CastToBitsImpl for T +{ + type ImplOutput = UIntValue; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + crate::sim::value::SimValue::bits(&this.to_sim_value()).clone() + } +} + +impl + CastToBitsImpl for T +{ + type ImplOutput = SimValue; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + crate::sim::value::SimValue::bits(&this.to_sim_value()).to_sim_value() + } +} + +impl CastToBitsImpl for T { + type ImplOutput = Expr; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + ops::CastToBits::new(Expr::canonical(this.to_expr())).to_expr() + } +} + +impl CastToBitsImpl for T { + type ImplOutput = Valueless; + + fn cast_to_bits_impl(this: &Self) -> Self::ImplOutput { + Valueless::new(UInt::new_dyn(this.ty().canonical().bit_width())) + } +} + +pub trait CastBitsTo: ValueType>> { + type Output: ValueType; + #[track_caller] - fn cast_bits_to(&self, ty: T) -> Expr; + fn cast_bits_to(&self, ty: T) -> Self::Output; } -impl> + ?Sized, Width: Size> CastBitsTo for T { - fn cast_bits_to(&self, ty: ToType) -> Expr { - ops::CastBitsTo::new(Expr::as_dyn_int(self.to_expr()), ty).to_expr() +impl< + This: ?Sized + ValueType> + CastBitsToImpl, + Width: Size, +> CastBitsTo for This +{ + type Output = This::ImplOutput; + + #[track_caller] + fn cast_bits_to(&self, ty: T) -> Self::Output { + Self::cast_bits_to_impl(self, ty) } } -pub trait CastTo: ToExpr { - fn cast_to(&self, to_type: ToType) -> Expr +pub trait CastBitsToImpl { + type ImplOutput: ValueType; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput; +} + +impl< + 'a, + This: ?Sized + ValueType> + ToSimValueInner<'a>, + Width: Size, + C: value_category::ValueCategoryIsValueOrSimValue, +> CastBitsToImpl for This +{ + type ImplOutput = SimValue; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + let ty_props = ty.canonical().type_properties(); + assert!(ty_props.is_castable_from_bits); + assert_eq!(this.ty().width(), ty_props.bit_width); + crate::sim::value::SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(Self::to_sim_value_inner(this).into_owned().as_dyn_int()), + ) + } +} + +impl> + ToExpr, Width: Size> + CastBitsToImpl for This +{ + type ImplOutput = Expr; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + ops::CastBitsTo::new(Expr::as_dyn_int(this.to_expr()), ty).to_expr() + } +} + +impl>, Width: Size> + CastBitsToImpl for This +{ + type ImplOutput = Valueless; + + #[track_caller] + fn cast_bits_to_impl(this: &Self, ty: T) -> Self::ImplOutput { + let ty_props = ty.canonical().type_properties(); + assert!(ty_props.is_castable_from_bits); + assert_eq!(this.ty().width(), ty_props.bit_width); + Valueless::new(ty) + } +} + +pub trait CastToImpl: Type { + type ValueOutput: ValueType + + ToSimValueWithType; + #[track_caller] + fn cast_value_to( + this: Self, + value: Cow<'_, Self::SimValue>, + to_type: ToType, + ) -> Self::ValueOutput; + #[track_caller] + fn cast_sim_value_to(value: Cow<'_, SimValue>, to_type: ToType) -> SimValue { + let ty = value.ty(); + let value = match value { + Cow::Borrowed(value) => Cow::Borrowed(&**value), + Cow::Owned(value) => Cow::Owned(SimValue::into_value(value)), + }; + Self::cast_value_to(ty, value, to_type).into_sim_value_with_type(to_type) + } + #[track_caller] + fn cast_expr_to(value: Expr, to_type: ToType) -> Expr; + #[track_caller] + fn cast_valueless_to(value: Valueless, to_type: ToType) -> Valueless { + let _ = value; + Valueless::new(to_type) + } +} + +trait CastToImplHelperSealed {} + +#[expect(private_bounds)] +pub trait CastToImplHelper: + ValueType + CastToImplHelperSealed +{ + type Output: ValueType where - Self::Type: ExprCastTo, - { - ExprCastTo::cast_to(self.to_expr(), to_type) - } - fn cast_to_static(&self) -> Expr + Self::Type: CastToImpl; + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output where - Self::Type: ExprCastTo, + Self::Type: CastToImpl; +} + +impl< + 'a, + This: ?Sized + ValueType + ToSimValueInner<'a>, +> CastToImplHelperSealed for This +{ +} + +impl< + 'a, + This: ?Sized + ValueType + ToSimValueInner<'a>, +> CastToImplHelper for This +{ + type Output + = >::ValueOutput + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, { - ExprCastTo::cast_to(self.to_expr(), ToType::TYPE) + Self::Type::cast_value_to(self.ty(), Self::to_sim_value_inner(self), to_type) } } -impl CastTo for T {} +impl + ToSimValue> + CastToImplHelperSealed for This +{ +} + +impl + ToSimValue> + CastToImplHelper for This +{ + type Output + = SimValue + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_sim_value_to(Cow::Owned(self.to_sim_value()), to_type) + } +} + +impl + ToExpr> + CastToImplHelperSealed for This +{ +} + +impl + ToExpr> + CastToImplHelper for This +{ + type Output + = Expr + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_expr_to(self.to_expr(), to_type) + } +} + +impl> + CastToImplHelperSealed for This +{ +} + +impl> + CastToImplHelper for This +{ + type Output + = Valueless + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_impl_helper(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::Type::cast_valueless_to(self.to_valueless(), to_type) + } +} + +pub trait CastTo: ValueType { + type Output: ValueType + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl; + + #[track_caller] + fn cast_to_static(&self) -> Self::Output + where + Self::Type: CastToImpl, + { + self.cast_to(ToType::TYPE) + } +} + +impl< + T: ValueType + CastToImplHelper + ?Sized, + C: value_category::ValueCategory, +> CastTo for T +{ + type Output + = >::Output + where + Self::Type: CastToImpl; + fn cast_to(&self, to_type: ToType) -> Self::Output + where + Self::Type: CastToImpl, + { + Self::cast_to_impl_helper(self, to_type) + } +} #[doc(hidden)] pub fn check_match_expr( @@ -761,7 +1520,7 @@ pub fn repeat( let element = element.to_expr(); let canonical_element = Expr::canonical(element); ops::ArrayLiteral::new( - Expr::ty(element), + element.ty(), std::iter::repeat(canonical_element) .take(L::Size::as_usize(len)) .collect(), @@ -769,9 +1528,16 @@ pub fn repeat( .to_expr() } -impl ToExpr for PhantomConst { +impl ValueType for PhantomConst { type Type = Self; + type ValueCategory = value_category::ValueCategoryValue; + fn ty(&self) -> Self::Type { + *self + } +} + +impl ToExpr for PhantomConst { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), @@ -792,3 +1558,137 @@ impl ToLiteralBits for Phan Ok(Interned::default()) } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Valueless(T); + +impl Valueless { + pub const fn new(ty: T) -> Self { + Self(ty) + } + pub const fn ty(self) -> T { + self.0 + } +} + +impl Default for Valueless { + fn default() -> Self { + Self(T::TYPE) + } +} + +pub trait ValueType { + type Type: Type; + type ValueCategory: value_category::ValueCategory; + fn ty(&self) -> Self::Type; +} + +trait ToValuelessSealed {} + +#[expect(private_bounds)] +pub trait ToValueless: ValueType + ToValuelessSealed { + fn to_valueless(&self) -> Valueless { + Valueless::new(self.ty()) + } +} + +impl ToValuelessSealed for T {} + +impl ToValueless for T {} + +impl ValueType for Valueless { + type Type = T; + type ValueCategory = value_category::ValueCategoryValueless; + + fn ty(&self) -> Self::Type { + self.0 + } +} + +impl<'a, T: ?Sized + ValueType> ValueType for &'a T { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl<'a, T: ?Sized + ValueType> ValueType for &'a mut T { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Box { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for std::sync::Arc { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for std::rc::Rc { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Interned { + type Type = T::Type; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + T::ty(self) + } +} + +impl ValueType for Expr { + type Type = T; + type ValueCategory = value_category::ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.__ty + } +} + +pub trait ToSimValueInner<'a>: ValueType { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue>; + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> + where + Self: Sized; +} + +impl<'a, T: Type> ToSimValueInner<'a> for SimValue { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&**this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(SimValue::into_value(this)) + } +} + +impl<'a, T: Type> ToSimValueInner<'a> for &'a SimValue { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&***this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Borrowed(&**this) + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index b10e3ae..b8ef4f7 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -7,12 +7,14 @@ use crate::{ clock::{Clock, ToClock}, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, - HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, + CastBitsTo as _, CastTo, CastToBits as _, CastToImpl, Expr, ExprEnum, Flow, HdlPartialEq, + HdlPartialEqImpl, HdlPartialOrd, HdlPartialOrdImpl, NotALiteralExpr, ReduceBitsImpl, + ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, Valueless, target::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, + value_category::ValueCategoryExpr, }, int::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, @@ -24,6 +26,7 @@ use crate::{ AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, ToSyncReset, }, + sim::value::{SimValue, ToSimValue}, ty::{CanonicalType, StaticType, Type}, util::ConstUsize, }; @@ -31,50 +34,1759 @@ use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use num_bigint::BigInt; use num_traits::{ToPrimitive, Zero}; use std::{ + borrow::Cow, fmt, marker::PhantomData, + num::NonZero, ops::{ Add, BitAnd, BitOr, BitXor, Div, Index, Mul, Neg as StdNeg, Not, Range, RangeBounds, Rem, Shl, Shr, Sub, }, }; -macro_rules! forward_value_to_expr_unary_op_trait { +macro_rules! make_impls { ( - #[generics($($generics:tt)*)] - #[value($Value:ty)] - $Trait:ident::$method:ident + $([$($args:tt)*])? + $m:ident! {$($rest:tt)*} ) => { - impl<$($generics)*> $Trait for $Value - where - Expr<<$Value as ToExpr>::Type>: $Trait, - { - type Output = ::Type> as $Trait>::Output; + $m! {$($($args)*)? $($rest)*} + }; + ( + #[kinds()] + $($rest:tt)* + ) => {}; + ( + #[kinds($first_kind:tt $(, $kinds:tt)* $(,)?)] + $($rest:tt)* + ) => { + make_impls! { + #[kind($first_kind)] + $($rest)* + } + make_impls! { + #[kinds($($kinds),*)] + $($rest)* + } + }; + ( + #[type($($ty:tt)*)] + $(#[$($meta:tt)*])* + $([$($args:tt)*])? + $m:ident $($rest:tt)* + ) => { + make_impls! { + $(#[$($meta)*])* + [$($($args)*)? $($ty)*,] + $m $($rest)* + } + }; + ( + #[kind((int_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_no_nz<$lt, $Width>), (sint_no_nz<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_local<$lt, $Width>), (sint_local<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_value<$lt, $Width>), (sint_value<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((int_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((uint_sim_value<$lt, $Width>), (sint_sim_value<$lt, $Width>))] + $($rest)* + } + }; + ( + #[kind((uint<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroU_N, + (uint_no_nz<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroI_N, + (sint_no_nz<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + usize, + u_N, + (uint_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((sint_no_nz<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + isize, + i_N, + (sint_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_at_most_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_at_most_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_local_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local_at_most_expr<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_local_at_most_sim_value<$lt, $Width>), + (Expr>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_value<$lt, $Width>), + (uint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local_at_most_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_value<$lt, $Width>), + (sint_sim_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((uint_at_most_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + prim_uint, + (uint_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind((sint_at_most_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + prim_sint, + (sint_value<$lt, $Width>), + )] + $($rest)* + } + }; + ( + #[kind(prim_uint)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroU_N, + usize, + u_N, + )] + $($rest)* + } + }; + ( + #[kind(prim_sint)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (NonZero), + NonZeroI_N, + isize, + i_N, + )] + $($rest)* + } + }; + ( + #[kind((bool_local_at_most_expr<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_sim_value<$lt>), + (Expr), + )] + $($rest)* + } + }; + ( + #[kind((bool_at_most_expr<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_at_most_sim_value<$lt>), + (Expr), + )] + $($rest)* + } + }; + ( + #[kind((bool_at_most_sim_value<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + bool, + (bool_sim_value<$lt>), + )] + $($rest)* + } + }; + ( + #[kind((uint_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (uint_value<$lt, $Width>), + (SimValue>), + (&$lt SimValue>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((sint_local<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (sint_value<$lt, $Width>), + (SimValue>), + (&$lt SimValue>), + (Expr>), + (Valueless>), + )] + $($rest)* + } + }; + ( + #[kind((uint_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (UIntValue<$Width: Size>), + (&$lt UIntValue<$Width: Size>), + )] + $($rest)* + } + }; + ( + #[kind((sint_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SIntValue<$Width: Size>), + (&$lt SIntValue<$Width: Size>), + )] + $($rest)* + } + }; + ( + #[kind((uint_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue>), + (&$lt SimValue>), + )] + $($rest)* + } + }; + ( + #[kind((sint_sim_value<$lt:lifetime, $Width:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue>), + (&$lt SimValue>), + )] + $($rest)* + } + }; + ( + #[kind((bool_sim_value<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (SimValue), + (&$lt SimValue), + )] + $($rest)* + } + }; + ( + #[kind((bool<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + bool, + (bool_sim_value<$lt>), + (Expr), + (Valueless), + )] + $($rest)* + } + }; + ( + #[kind((bool_local<$lt:lifetime>))] + $($rest:tt)* + ) => { + make_impls! { + #[kinds( + (bool_sim_value<$lt>), + (Expr), + (Valueless), + )] + $($rest)* + } + }; + ( + #[kind(u_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds(u8, u16, u32, u64, u128)] + $($rest)* + } + }; + ( + #[kind(i_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds(i8, i16, i32, i64, i128)] + $($rest)* + } + }; + ( + #[kind(NonZeroU_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((NonZero), (NonZero), (NonZero), (NonZero), (NonZero))] + $($rest)* + } + }; + ( + #[kind(NonZeroI_N)] + $($rest:tt)* + ) => { + make_impls! { + #[kinds((NonZero), (NonZero), (NonZero), (NonZero), (NonZero))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$Ty:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][] ($wrapper<$Ty>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$Ty:ident>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][] (&$lt $wrapper<$Ty>))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$Width:ident: Size>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][$Width: Size,] ($wrapper<$Width>))] + $($rest)* + } + }; + ( + #[kind(($wrapper:ident<$ty:ident<$Width:ident: Size>>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([][$Width: Size,] ($wrapper<$ty<$Width>>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$Width:ident: Size>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][$Width: Size,] (&$lt $wrapper<$Width>))] + $($rest)* + } + }; + ( + #[kind((&$lt:lifetime $wrapper:ident<$ty:ident<$Width:ident: Size>>))] + $($rest:tt)* + ) => { + make_impls! { + #[type([$lt,][$Width: Size,] (&$lt $wrapper<$ty<$Width>>))] + $($rest)* + } + }; + (#[kind(usize)] $($rest:tt)*) => {make_impls! { #[type([][] (usize))] $($rest)* }}; + (#[kind(isize)] $($rest:tt)*) => {make_impls! { #[type([][] (isize))] $($rest)* }}; + (#[kind(bool)] $($rest:tt)*) => {make_impls! { #[type([][] (bool))] $($rest)* }}; + (#[kind(u8)] $($rest:tt)*) => {make_impls! { #[type([][] (u8))] $($rest)* }}; + (#[kind(u16)] $($rest:tt)*) => {make_impls! { #[type([][] (u16))] $($rest)* }}; + (#[kind(u32)] $($rest:tt)*) => {make_impls! { #[type([][] (u32))] $($rest)* }}; + (#[kind(u64)] $($rest:tt)*) => {make_impls! { #[type([][] (u64))] $($rest)* }}; + (#[kind(u128)] $($rest:tt)*) => {make_impls! { #[type([][] (u128))] $($rest)* }}; + (#[kind(i8)] $($rest:tt)*) => {make_impls! { #[type([][] (i8))] $($rest)* }}; + (#[kind(i16)] $($rest:tt)*) => {make_impls! { #[type([][] (i16))] $($rest)* }}; + (#[kind(i32)] $($rest:tt)*) => {make_impls! { #[type([][] (i32))] $($rest)* }}; + (#[kind(i64)] $($rest:tt)*) => {make_impls! { #[type([][] (i64))] $($rest)* }}; + (#[kind(i128)] $($rest:tt)*) => {make_impls! { #[type([][] (i128))] $($rest)* }}; +} +pub(crate) use make_impls; - fn $method(self) -> Self::Output { - $Trait::$method(self.to_expr()) +#[cfg(test)] +mod test_ops_impls; + +macro_rules! impl_simple_binary_op_trait { + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*> $Trait<$($R)*> for $($L)* { + type Output = $Out<<::Type> as $Trait::Type>>>::Output as ValueType>::Type>; + + fn $f($f_self, $f_rhs: $($R)*) -> Self::Output $body + } + }; + ( + $LLifetimes:tt $LBounds:tt ($($L:tt)*), + $RLifetimes:tt $RBounds:tt ($($R:tt)*), + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_simple_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $FirstTrait::$first_f($f_self, $f_rhs) -> $Out<_> $body + } + impl_simple_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_at_most_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivU::new(self.to_expr(), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((sint_at_most_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivU::new(self.to_expr(), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Expr<_> { + AddS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Sub::sub(self, rhs) -> Expr<_> { + SubS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Mul::mul(self, rhs) -> Expr<_> { + MulS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Div::div(self, rhs) -> Expr<_> { + DivS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Rem::rem(self, rhs) -> Expr<_> { + RemS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + #[kinds((uint_at_most_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + #[kinds((sint_at_most_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_sim_value<'l, L>))] + #[kinds((sint_sim_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())).into_sim_value() + }, + Sub::sub(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())).into_sim_value() + }, + Mul::mul(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())).into_sim_value() + }, + Div::div(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())).into_sim_value() + }, + Rem::rem(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())).into_sim_value() + }, + BitAnd::bitand(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_at_most_expr<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((sint_at_most_expr<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> Valueless<_> { + self.to_valueless().add(rhs.to_valueless()) + }, + Sub::sub(self, rhs) -> Valueless<_> { + self.to_valueless().sub(rhs.to_valueless()) + }, + Mul::mul(self, rhs) -> Valueless<_> { + self.to_valueless().mul(rhs.to_valueless()) + }, + Div::div(self, rhs) -> Valueless<_> { + self.to_valueless().div(rhs.to_valueless()) + }, + Rem::rem(self, rhs) -> Valueless<_> { + self.to_valueless().rem(rhs.to_valueless()) + }, + BitAnd::bitand(self, rhs) -> Valueless<_> { + self.to_valueless().bitand(rhs.to_valueless()) + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + self.to_valueless().bitor(rhs.to_valueless()) + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + self.to_valueless().bitxor(rhs.to_valueless()) + }, + } +} + +type SimValueInner = ::SimValue; + +trait ValueIntoBigInt { + fn value_to_bigint(&self) -> BigInt; +} + +macro_rules! impl_value_to_bigint { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] (NonZero<$T:ty>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for NonZero<$T> { + fn value_to_bigint(&self) -> BigInt { + self.get().into() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($(&$lt:lifetime)? $IntValue:ident<$Width:ident>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $(&$lt)? $IntValue<$Width> { + fn value_to_bigint(&self) -> BigInt { + self.to_bigint() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($(&$lt:lifetime)? SimValue<$IntType:ident<$Width:ident>>),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $(&$lt)? SimValue<$IntType<$Width>> { + fn value_to_bigint(&self) -> BigInt { + self.to_bigint() + } + } + }; + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($T:ty),) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $T { + fn value_to_bigint(&self) -> BigInt { + (*self).into() } } }; } -macro_rules! impl_unary_op_trait { - ( - #[generics($($generics:tt)*)] - fn $Trait:ident::$method:ident($arg:ident: $Arg:ty) -> $Output:ty { - $($body:tt)* - } - ) => { - impl<$($generics)*> $Trait for Expr<$Arg> - { - type Output = Expr<$Output>; +make_impls! { + #[kinds((sint_at_most_sim_value<'a, Width>), (uint_at_most_sim_value<'a, Width>))] + impl_value_to_bigint! {} +} - fn $method(self) -> Self::Output { - let $arg = self; - $($body)* - } +make_impls! { + #[kinds((uint_value<'l, L>))] + #[kinds(prim_uint)] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((sint_value<'l, L>))] + #[kinds(prim_sint)] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((uint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_value<'l, L>))] + #[kinds((sint_value<'r, R>))] + impl_simple_binary_op_trait! { + Add::add(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().add(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().add(rhs.value_to_bigint())) + }, + Sub::sub(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().sub(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().sub(rhs.value_to_bigint())) + }, + Mul::mul(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().mul(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().mul(rhs.value_to_bigint())) + }, + Div::div(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().div(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().div(rhs.value_to_bigint())) + }, + Rem::rem(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().rem(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().rem(rhs.value_to_bigint())) + }, + BitAnd::bitand(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitand(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitand(rhs.value_to_bigint())) + }, + BitOr::bitor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitor(rhs.value_to_bigint())) + }, + BitXor::bitxor(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().bitxor(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint().bitxor(rhs.value_to_bigint())) + }, + } +} + +make_impls! { + #[kinds((Expr))] + #[kinds((bool_sim_value<'r>))] + impl_simple_binary_op_trait! { + /// intentionally only implemented for expressions, not rust's bool type, + /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do + /// what is usually wanted. + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + } +} + +make_impls! { + #[kinds((bool_local_at_most_expr<'l>))] + #[kinds((Expr))] + impl_simple_binary_op_trait! { + /// intentionally only implemented for expressions, not rust's bool type, + /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do + /// what is usually wanted. + BitAnd::bitand(self, rhs) -> Expr<_> { + BitAndB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + BitOrB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + BitXorB::new(self.to_expr(), rhs.to_expr()).to_expr() + }, + } +} + +make_impls! { + #[kinds((bool_at_most_sim_value<'l>))] + #[kinds((bool_sim_value<'r>))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitand(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitor(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitxor(*ToSimValueInner::into_sim_value_inner(rhs)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((bool_sim_value<'l>))] + #[kinds(bool)] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitand(rhs).into_sim_value() + }, + BitOr::bitor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitor(rhs).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> SimValue<_> { + ToSimValueInner::into_sim_value_inner(self).bitxor(rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((bool_at_most_expr<'l>))] + #[kinds((Valueless))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> Valueless<_> { + rhs + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + rhs + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + rhs + }, + } +} + +make_impls! { + #[kinds((Valueless))] + #[kinds((bool_at_most_expr<'r>))] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + BitOr::bitor(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + BitXor::bitxor(self, rhs) -> Valueless<_> { + rhs.to_valueless() + }, + } +} + +macro_rules! impl_shift_binary_op_trait { + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [][] (usize), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($LBounds)*> $Trait for $($L)* { + type Output = $Out<<::Type> as $Trait>::Output as ValueType>::Type>; + + #[track_caller] + fn $f($f_self, $f_rhs: usize) -> Self::Output $body } }; + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), + [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*> $Trait<$($R)*> for $($L)* { + type Output = $Out<<::Type> as $Trait::Type>>>::Output as ValueType>::Type>; + + #[track_caller] + fn $f($f_self, $f_rhs: $($R)*) -> Self::Output $body + } + }; + ( + $LLifetimes:tt $LBounds:tt ($($L:tt)*), + $RLifetimes:tt $RBounds:tt ($($R:tt)*), + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident, $f_rhs:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_shift_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $FirstTrait::$first_f($f_self, $f_rhs) -> $Out<_> $body + } + impl_shift_binary_op_trait! { + $LLifetimes $LBounds ($($L)*), + $RLifetimes $RBounds ($($R)*), + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> Expr<_> { + FixedShlU::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + FixedShrU::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> Expr<_> { + FixedShlS::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + FixedShrS::new(Expr::as_dyn_int(self.to_expr()), rhs).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlU::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs)).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(self.to_expr(), Expr::as_dyn_int(rhs)).to_expr() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Expr>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlS::new(Expr::as_dyn_int(self.to_expr()), Expr::as_dyn_int(rhs)).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(self.to_expr(), Expr::as_dyn_int(rhs)).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_local_at_most_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlU::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(self, Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + #[kinds((uint_local_at_most_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Expr<_> { + DynShlS::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(self, Expr::as_dyn_int(rhs.to_expr())).to_expr() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_at_most_sim_value<'l, L>))] + #[kinds((uint_sim_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_sim_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((uint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_value<'l, L>))] + #[kinds((uint_value<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + let rhs: usize = rhs.value_to_bigint().try_into().expect("dynamic left-shift's amount is too big"); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs.to_valueless()).ty(); + let rhs = rhs.value_to_bigint().try_into().unwrap_or(usize::MAX); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((uint_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((sint_value<'l, L>))] + impl_shift_binary_op_trait! { + [][] (usize), + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shr(rhs).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((uint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.to_valueless().shl(rhs) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.to_valueless().shr(rhs) + }, + } +} + +make_impls! { + #[kinds((sint_at_most_expr<'l, L>))] + #[kinds((Valueless>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.to_valueless().shl(rhs) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.to_valueless().shr(rhs) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_local_at_most_expr<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.shl(rhs.to_valueless()) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.shr(rhs.to_valueless()) + }, + } +} + +make_impls! { + #[kinds((Valueless>))] + #[kinds((uint_local_at_most_expr<'r, R>))] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> Valueless<_> { + self.shl(rhs.to_valueless()) + }, + Shr::shr(self, rhs) -> Valueless<_> { + self.shr(rhs.to_valueless()) + }, + } +} + +macro_rules! impl_unary_op_trait { + ( + [$($Lifetimes:tt)*][$($Bounds:tt)*] ($($Ty:tt)*), + $(#[$meta:meta])* + $Trait:ident::$f:ident($f_self:ident) -> $Out:ident<_> $body:block $(,)? + ) => { + $(#[$meta])* + impl<$($Lifetimes)* $($Bounds)*> $Trait for $($Ty)* { + type Output = $Out<<::Type> as $Trait>::Output as ValueType>::Type>; + + fn $f($f_self) -> Self::Output $body + } + }; + ( + $Lifetimes:tt $Bounds:tt $Ty:tt, + $(#[$meta:meta])* + $FirstTrait:ident::$first_f:ident($f_self:ident) -> $Out:ident<_> $body:block, + $($rest:tt)* + ) => { + impl_unary_op_trait! { + $Lifetimes $Bounds $Ty, + $(#[$meta])* + $FirstTrait::$first_f($f_self) -> $Out<_> $body + } + impl_unary_op_trait! { + $Lifetimes $Bounds $Ty, + $(#[$meta])* + $($rest)* + } + }; +} + +make_impls! { + #[kinds((SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotU::new(self).to_expr() + }, + } +} + +make_impls! { + #[kinds((SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + StdNeg::neg(self) -> SimValue<_> { + StdNeg::neg(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue>))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::value(self)).into_sim_value() + }, + StdNeg::neg(self) -> SimValue<_> { + StdNeg::neg(SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr>))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotS::new(self).to_expr() + }, + StdNeg::neg(self) -> Expr<_> { + Neg::new(Expr::as_dyn_int(self)).to_expr() + }, + } +} + +make_impls! { + #[kinds((SimValue))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(SimValue::into_value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((&'a SimValue))] + impl_unary_op_trait! { + Not::not(self) -> SimValue<_> { + Not::not(*SimValue::value(self)).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((Expr))] + impl_unary_op_trait! { + Not::not(self) -> Expr<_> { + NotB::new(self).to_expr() + }, + } } macro_rules! impl_get_target_none { @@ -107,9 +1819,16 @@ impl NotU { } } -impl ToExpr for NotU { +impl ValueType for NotU { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().not().ty() + } +} + +impl ToExpr for NotU { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotU(NotU { @@ -117,7 +1836,7 @@ impl ToExpr for NotU { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.ty(), __flow: Flow::Source, } } @@ -131,19 +1850,6 @@ impl ToLiteralBits for NotU { impl_get_target_none!([Width: Size] NotU); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn Not::not(arg: UIntType) -> UIntType { - NotU::new(arg).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(UIntValue)] - Not::not -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct NotS { arg: Expr>, @@ -164,9 +1870,16 @@ impl NotS { } } -impl ToExpr for NotS { +impl ValueType for NotS { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().not().ty() + } +} + +impl ToExpr for NotS { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotS(NotS { @@ -174,7 +1887,7 @@ impl ToExpr for NotS { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty.as_same_width_uint(), + __ty: self.ty(), __flow: Flow::Source, } } @@ -188,19 +1901,6 @@ impl ToLiteralBits for NotS { impl_get_target_none!([Width: Size] NotS); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn Not::not(arg: SIntType) -> UIntType { - NotS::new(arg).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(SIntValue)] - Not::not -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct NotB { arg: Expr, @@ -221,9 +1921,16 @@ impl NotB { } } -impl ToExpr for NotB { +impl ValueType for NotB { type Type = Bool; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Bool + } +} + +impl ToExpr for NotB { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::NotB(NotB { @@ -231,7 +1938,7 @@ impl ToExpr for NotB { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.arg.ty(), __flow: Flow::Source, } } @@ -245,13 +1952,6 @@ impl ToLiteralBits for NotB { impl_get_target_none!([] NotB); -impl_unary_op_trait! { - #[generics()] - fn Not::not(arg: Bool) -> Bool { - NotB::new(arg).to_expr() - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Neg { arg: Expr, @@ -270,22 +1970,21 @@ impl Neg { }); retval } - pub fn ty(self) -> SInt { - SInt::new_dyn( - Expr::ty(self.arg) - .width() - .checked_add(1) - .expect("width too big"), - ) - } pub fn arg(self) -> Expr { self.arg } } -impl ToExpr for Neg { +impl ValueType for Neg { type Type = SInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.arg.to_valueless().neg().ty() + } +} + +impl ToExpr for Neg { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Neg(*self).intern(), @@ -303,64 +2002,6 @@ impl ToLiteralBits for Neg { impl_get_target_none!([] Neg); -impl_unary_op_trait! { - #[generics(Width: Size)] - fn StdNeg::neg(arg: SIntType) -> SInt { - Neg::new(Expr::as_dyn_int(arg)).to_expr() - } -} - -forward_value_to_expr_unary_op_trait! { - #[generics(Width: Size)] - #[value(SIntValue)] - StdNeg::neg -} - -macro_rules! impl_binary_op_trait { - ( - #[generics($($generics:tt)*)] - fn $Trait:ident::$method:ident($lhs:ident: $Lhs:ty, $rhs:ident: $Rhs:ty) -> $Output:ty { - $($body:tt)* - } - ) => { - impl< - Rhs: ToExpr, - $($generics)* - > $Trait for Expr<$Lhs> - { - type Output = Expr<$Output>; - - fn $method(self, rhs: Rhs) -> Self::Output { - let $lhs = self; - let $rhs = rhs.to_expr(); - $($body)* - } - } - }; -} - -macro_rules! forward_value_to_expr_binary_op_trait { - ( - #[generics($($generics:tt)*)] - #[lhs_value($LhsValue:ty)] - $Trait:ident::$method:ident - ) => { - impl< - Rhs, - $($generics)* - > $Trait for $LhsValue - where - Expr<<$LhsValue as ToExpr>::Type>: $Trait, - { - type Output = ::Type> as $Trait>::Output; - - fn $method(self, rhs: Rhs) -> Self::Output { - $Trait::$method(self.to_expr(), rhs) - } - } - }; -} - fn binary_op_literal_bits( result_ty: ResultTy, lhs: Expr, @@ -412,9 +2053,16 @@ macro_rules! binary_op_bitwise { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + $ty + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -423,17 +2071,6 @@ macro_rules! binary_op_bitwise { } } } - - /// intentionally only implemented for expressions, not rust's bool type, - /// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do - /// what is usually wanted. - impl $Trait for Expr { - type Output = Expr; - - fn $method(self, rhs: Expr) -> Expr { - $name::new(self, rhs).to_expr() - } - } }; ($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -461,9 +2098,6 @@ macro_rules! binary_op_bitwise { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> UInt { - UInt::new_dyn(Expr::ty(self.lhs).width().max(Expr::ty(self.rhs).width())) - } } impl ToLiteralBits for $name { @@ -474,9 +2108,16 @@ macro_rules! binary_op_bitwise { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().$method(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -485,19 +2126,6 @@ macro_rules! binary_op_bitwise { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn $Trait::$method(lhs: $ty, rhs: $ty) -> UInt { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - $Trait::$method - } }; } @@ -538,15 +2166,6 @@ macro_rules! binary_op_add_sub { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - Expr::ty(self.lhs) - .width() - .max(Expr::ty(self.rhs).width()) - .checked_add(1) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -557,9 +2176,16 @@ macro_rules! binary_op_add_sub { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + $Trait::$method(self.lhs.to_valueless(), self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -568,19 +2194,6 @@ macro_rules! binary_op_add_sub { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn $Trait::$method(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - $Trait::$method - } }; } @@ -616,14 +2229,6 @@ macro_rules! binary_op_mul { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - Expr::ty(self.lhs) - .width() - .checked_add(Expr::ty(self.rhs).width()) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -634,9 +2239,16 @@ macro_rules! binary_op_mul { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().mul(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -645,19 +2257,6 @@ macro_rules! binary_op_mul { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Mul::mul(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - Mul::mul - } }; } @@ -689,9 +2288,6 @@ impl DivU { pub fn rhs(self) -> Expr { self.rhs } - pub fn ty(self) -> UIntType { - Expr::ty(self.lhs) - } } impl ToLiteralBits for DivU { @@ -702,9 +2298,16 @@ impl ToLiteralBits for DivU { impl_get_target_none!([LhsWidth: Size] DivU); -impl ToExpr for DivU { +impl ValueType for DivU { type Type = UIntType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().div(self.rhs.to_valueless()).ty() + } +} + +impl ToExpr for DivU { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DivU(DivU { @@ -719,19 +2322,6 @@ impl ToExpr for DivU { } } -impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Div::div(lhs: UIntType, rhs: UIntType) -> UIntType { - DivU::new(lhs, Expr::as_dyn_int(rhs)).to_expr() - } -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Div::div -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct DivS { lhs: Expr, @@ -757,14 +2347,6 @@ impl DivS { pub fn rhs(self) -> Expr { self.rhs } - pub fn ty(self) -> SInt { - SInt::new_dyn( - Expr::ty(self.lhs) - .width() - .checked_add(1) - .expect("width too big"), - ) - } } impl ToLiteralBits for DivS { @@ -775,9 +2357,16 @@ impl ToLiteralBits for DivS { impl_get_target_none!([] DivS); -impl ToExpr for DivS { +impl ValueType for DivS { type Type = SInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().div(self.rhs.to_valueless()).ty() + } +} + +impl ToExpr for DivS { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DivS(*self).intern(), @@ -787,19 +2376,6 @@ impl ToExpr for DivS { } } -impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Div::div(lhs: SIntType, rhs: SIntType) -> SInt { - DivS::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Div::div -} - macro_rules! binary_op_rem { ($name:ident, $ty:ident, $value:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -831,9 +2407,6 @@ macro_rules! binary_op_rem { pub fn rhs(self) -> Expr<$ty> { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn(Expr::ty(self.lhs).width().min(Expr::ty(self.rhs).width())) - } } impl ToLiteralBits for $name { @@ -844,9 +2417,16 @@ macro_rules! binary_op_rem { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().rem(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -855,19 +2435,6 @@ macro_rules! binary_op_rem { } } } - - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Rem::rem(lhs: $ty, rhs: $ty) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() - } - } - - forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value($value)] - Rem::rem - } }; } @@ -894,7 +2461,7 @@ impl BundleLiteral { for (&field, &field_value) in fields.iter().zip(field_values.iter()) { assert_eq!( field.ty, - Expr::ty(field_value), + field_value.ty(), "field's type doesn't match value's type: field name {:?}", field.name ); @@ -914,9 +2481,6 @@ impl BundleLiteral { .ok_or(NotALiteralExpr), } } - pub fn ty(self) -> T { - self.ty - } pub fn field_values(self) -> Interned<[Expr]> { self.field_values } @@ -930,9 +2494,16 @@ impl ToLiteralBits for BundleLiteral { impl_get_target_none!([T: BundleType] BundleLiteral); -impl ToExpr for BundleLiteral { +impl ValueType for BundleLiteral { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for BundleLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::BundleLiteral(BundleLiteral { @@ -968,7 +2539,7 @@ impl ArrayLiteral { for &element_value in element_values.iter() { assert_eq!( canonical_element_type, - Expr::ty(element_value), + element_value.ty(), "array's element type doesn't match element value's type", ); if let (Some(literal_bits), Ok(element_bits)) = @@ -997,12 +2568,6 @@ impl ArrayLiteral { pub fn is_empty(self) -> bool { self.element_values.is_empty() } - pub fn ty(self) -> ArrayType { - ArrayType::new( - self.element_type, - Len::from_usize(self.element_values.len()), - ) - } pub fn element_values(self) -> Interned<[Expr]> { self.element_values } @@ -1016,9 +2581,19 @@ impl ToLiteralBits for ArrayLiteral { impl_get_target_none!([Element: Type, Len: Size] ArrayLiteral); -impl ToExpr for ArrayLiteral { +impl ValueType for ArrayLiteral { type Type = ArrayType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + ArrayType::new( + self.element_type, + Len::from_usize(self.element_values.len()), + ) + } +} + +impl ToExpr for ArrayLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ArrayLiteral(ArrayLiteral { @@ -1034,12 +2609,22 @@ impl ToExpr for ArrayLiteral { } } -impl, const N: usize> ToExpr for [T; N] +impl, const N: usize> ValueType for [T; N] where ConstUsize: KnownSize, { type Type = Array; + type ValueCategory = T::ValueCategory; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } +} + +impl, const N: usize> ToExpr for [T; N] +where + ConstUsize: KnownSize, +{ fn to_expr(&self) -> Expr { ArrayLiteral::new( T::Type::TYPE, @@ -1049,9 +2634,16 @@ where } } +impl> ValueType for [T] { + type Type = Array; + type ValueCategory = T::ValueCategory; + + fn ty(&self) -> Self::Type { + ArrayType::new_dyn(StaticType::TYPE, self.len()) + } +} + impl> ToExpr for [T] { - type Type = Array; - fn to_expr(&self) -> Expr { ArrayLiteral::new( T::Type::TYPE, @@ -1061,9 +2653,16 @@ impl> ToExpr for [T] { } } -impl> ToExpr for Vec { +impl> ValueType for Vec { type Type = Array; + type ValueCategory = T::ValueCategory; + fn ty(&self) -> Self::Type { + <[T]>::ty(self) + } +} + +impl> ToExpr for Vec { fn to_expr(&self) -> Expr { <[T]>::to_expr(self) } @@ -1090,7 +2689,7 @@ impl EnumLiteral { assert!(variant_index < variants.len()); assert_eq!( variants[variant_index].ty, - variant_value.map(Expr::ty), + variant_value.map(|v| v.ty()), "variant's type doesn't match value's type: variant name {:?}", variants[variant_index].name ); @@ -1130,9 +2729,6 @@ impl EnumLiteral { }; Self::new_by_index(ty, *variant_index, variant_value) } - pub fn ty(self) -> T { - self.ty - } pub fn variant_index(self) -> usize { self.variant_index } @@ -1152,9 +2748,16 @@ impl ToLiteralBits for EnumLiteral { impl_get_target_none!([T: EnumType] EnumLiteral); -impl ToExpr for EnumLiteral { +impl ValueType for EnumLiteral { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for EnumLiteral { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::EnumLiteral(EnumLiteral { @@ -1199,25 +2802,6 @@ macro_rules! impl_dyn_shl { pub fn rhs(self) -> Expr { self.rhs } - #[track_caller] - pub fn ty(self) -> $ty { - let Some(pow2_rhs_width) = Expr::ty(self.rhs) - .width() - .try_into() - .ok() - .and_then(|v| 2usize.checked_pow(v)) - else { - panic!( - "dynamic left-shift amount's bit-width is too big, try casting the shift \ - amount to a smaller bit-width before shifting" - ); - }; - $ty::new_dyn( - (pow2_rhs_width - 1) - .checked_add(Expr::ty(self.lhs).width()) - .expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -1228,9 +2812,17 @@ macro_rules! impl_dyn_shl { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + #[track_caller] + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().shl(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { #[track_caller] fn to_expr(&self) -> Expr { Expr { @@ -1240,14 +2832,6 @@ macro_rules! impl_dyn_shl { } } } - - impl Shl>> for Expr<$ty> { - type Output = Expr<$ty>; - - fn shl(self, rhs: Expr>) -> Self::Output { - $name::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs)).to_expr() - } - } }; } @@ -1272,7 +2856,7 @@ macro_rules! impl_dyn_shr { literal_bits: Err(NotALiteralExpr), }; retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| { - Ok(lhs << rhs.to_usize().ok_or(NotALiteralExpr)?) + Ok(lhs >> rhs.to_usize().ok_or(NotALiteralExpr)?) }); retval } @@ -1282,10 +2866,6 @@ macro_rules! impl_dyn_shr { pub fn rhs(self) -> Expr { self.rhs } - #[track_caller] - pub fn ty(self) -> $ty { - Expr::ty(self.lhs) - } } impl ToLiteralBits for $name { @@ -1296,9 +2876,17 @@ macro_rules! impl_dyn_shr { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + #[track_caller] + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().shr(self.rhs.to_valueless()).ty() + } + } + + impl ToExpr for $name { #[track_caller] fn to_expr(&self) -> Expr { Expr { @@ -1308,31 +2896,19 @@ macro_rules! impl_dyn_shr { literal_bits: self.literal_bits, }) .intern(), - __ty: Expr::ty(self.lhs), + __ty: self.ty(), __flow: Flow::Source, } } } - - impl Shr>> for Expr<$ty> { - type Output = Expr<$ty>; - - fn shr(self, rhs: Expr>) -> Self::Output { - $name::new(self, Expr::as_dyn_int(rhs)).to_expr() - } - } }; } impl_dyn_shr!(DynShrU, UIntType, UIntValue); impl_dyn_shr!(DynShrS, SIntType, SIntValue); -fn fixed_shr_width(lhs_width: usize, rhs: usize) -> Option { - Some(lhs_width.saturating_sub(rhs).max(1)) -} - macro_rules! binary_op_fixed_shift { - ($name:ident, $ty:ident, $value:ident, $width_fn:path, $Trait:ident::$method:ident) => { + ($name:ident, $ty:ident, $Trait:ident::$method:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { lhs: Expr<$ty>, @@ -1361,11 +2937,6 @@ macro_rules! binary_op_fixed_shift { pub fn rhs(self) -> usize { self.rhs } - pub fn ty(self) -> $ty { - $ty::new_dyn( - $width_fn(Expr::ty(self.lhs).width(), self.rhs).expect("width too big"), - ) - } } impl ToLiteralBits for $name { @@ -1376,9 +2947,16 @@ macro_rules! binary_op_fixed_shift { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $ty; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.lhs.to_valueless().$method(self.rhs).ty() + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1387,98 +2965,26 @@ macro_rules! binary_op_fixed_shift { } } } - - impl $Trait for Expr<$ty> { - type Output = Expr<$ty>; - - fn $method(self, rhs: usize) -> Self::Output { - $name::new(Expr::as_dyn_int(self), rhs).to_expr() - } - } }; } -binary_op_fixed_shift!(FixedShlU, UIntType, UIntValue, usize::checked_add, Shl::shl); -binary_op_fixed_shift!(FixedShlS, SIntType, SIntValue, usize::checked_add, Shl::shl); -binary_op_fixed_shift!(FixedShrU, UIntType, UIntValue, fixed_shr_width, Shr::shr); -binary_op_fixed_shift!(FixedShrS, SIntType, SIntValue, fixed_shr_width, Shr::shr); - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Shl::shl -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Shl::shl -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(UIntValue)] - Shr::shr -} - -forward_value_to_expr_binary_op_trait! { - #[generics(LhsWidth: Size)] - #[lhs_value(SIntValue)] - Shr::shr -} - -pub trait ExprPartialEq: Type { - fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr; -} - -pub trait ExprPartialOrd: ExprPartialEq { - fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_le(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr; - fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr; -} - -impl HdlPartialEq for Lhs -where - Lhs::Type: ExprPartialEq, -{ - fn cmp_eq(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr()) - } - fn cmp_ne(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr()) - } -} - -impl HdlPartialOrd for Lhs -where - Lhs::Type: ExprPartialOrd, -{ - fn cmp_lt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr()) - } - fn cmp_le(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr()) - } - fn cmp_gt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr()) - } - fn cmp_ge(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr()) - } -} +binary_op_fixed_shift!(FixedShlU, UInt, Shl::shl); +binary_op_fixed_shift!(FixedShlS, SInt, Shl::shl); +binary_op_fixed_shift!(FixedShrU, UInt, Shr::shr); +binary_op_fixed_shift!(FixedShrS, SInt, Shr::shr); macro_rules! impl_compare_op { ( $(#[width($LhsWidth:ident, $RhsWidth:ident)])? #[dyn_type($DynTy:ident)] #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] + #[to_cmp_value($lhs_compare_value:ident => $lhs_compare_value_expr:expr, $rhs_compare_value:ident => $rhs_compare_value_expr:expr)] #[type($Lhs:ty, $Rhs:ty)] #[trait($Trait:ident)] $( struct $name:ident; - fn $method:ident(); + fn $value_method:ident(); + fn $expr_method:ident(); $CmpTrait:ident::$cmp_method:ident(); )* ) => { @@ -1515,9 +3021,16 @@ macro_rules! impl_compare_op { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = Bool; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Bool + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1528,7 +3041,15 @@ macro_rules! impl_compare_op { })* impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs { - $(fn $method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { + $(fn $value_method( + _lhs: Self, + $lhs_compare_value: Cow<'_, ::SimValue>, + _rhs: $Rhs, + $rhs_compare_value: Cow<'_, <$Rhs as Type>::SimValue>, + ) -> bool { + $CmpTrait::$cmp_method($lhs_compare_value_expr, $rhs_compare_value_expr) + })* + $(fn $expr_method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { $name::new($dyn_lhs, $dyn_rhs).to_expr() })* } @@ -1538,79 +3059,192 @@ macro_rules! impl_compare_op { impl_compare_op! { #[dyn_type(Bool)] #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[to_cmp_value(lhs_value => &*lhs_value, rhs_value => &*rhs_value)] #[type(Bool, Bool)] - #[trait(ExprPartialEq)] - struct CmpEqB; fn cmp_eq(); PartialEq::eq(); - struct CmpNeB; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqB; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeB; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[dyn_type(Bool)] #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[to_cmp_value(lhs_value => &*lhs_value, rhs_value => &*rhs_value)] #[type(Bool, Bool)] - #[trait(ExprPartialOrd)] - struct CmpLtB; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeB; fn cmp_le(); PartialOrd::le(); - struct CmpGtB; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeB; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtB; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeB; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtB; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeB; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(UInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(UIntType, UIntType)] - #[trait(ExprPartialEq)] - struct CmpEqU; fn cmp_eq(); PartialEq::eq(); - struct CmpNeU; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqU; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeU; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(UInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(UIntType, UIntType)] - #[trait(ExprPartialOrd)] - struct CmpLtU; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeU; fn cmp_le(); PartialOrd::le(); - struct CmpGtU; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeU; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtU; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeU; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtU; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeU; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(SInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(SIntType, SIntType)] - #[trait(ExprPartialEq)] - struct CmpEqS; fn cmp_eq(); PartialEq::eq(); - struct CmpNeS; fn cmp_ne(); PartialEq::ne(); + #[trait(HdlPartialEqImpl)] + struct CmpEqS; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); + struct CmpNeS; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); } impl_compare_op! { #[width(LhsWidth, RhsWidth)] #[dyn_type(SInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(SIntType, SIntType)] - #[trait(ExprPartialOrd)] - struct CmpLtS; fn cmp_lt(); PartialOrd::lt(); - struct CmpLeS; fn cmp_le(); PartialOrd::le(); - struct CmpGtS; fn cmp_gt(); PartialOrd::gt(); - struct CmpGeS; fn cmp_ge(); PartialOrd::ge(); + #[trait(HdlPartialOrdImpl)] + struct CmpLtS; fn cmp_value_lt(); fn cmp_expr_lt(); PartialOrd::lt(); + struct CmpLeS; fn cmp_value_le(); fn cmp_expr_le(); PartialOrd::le(); + struct CmpGtS; fn cmp_value_gt(); fn cmp_expr_gt(); PartialOrd::gt(); + struct CmpGeS; fn cmp_value_ge(); fn cmp_expr_ge(); PartialOrd::ge(); } -pub trait ExprCastTo: Type { - fn cast_to(src: Expr, to_type: ToType) -> Expr; +macro_rules! impl_compare_forwards_to_bool { + ($ty:ident) => { + impl HdlPartialEqImpl for $ty { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_eq(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_ne(rhs.cast_to(Bool)) + } + } + + impl HdlPartialOrdImpl for $ty { + #[track_caller] + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::lt(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::le(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::gt(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + PartialOrd::ge(&*lhs_value, &*rhs_value) + } + + #[track_caller] + fn cmp_expr_lt(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_lt(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_le(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_le(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_gt(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_gt(rhs.cast_to(Bool)) + } + + #[track_caller] + fn cmp_expr_ge(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to(Bool).cmp_ge(rhs.cast_to(Bool)) + } + } + }; } -impl ExprCastTo for Bool { - fn cast_to(src: Expr, _to_type: Bool) -> Expr { - src +impl_compare_forwards_to_bool!(Clock); +impl_compare_forwards_to_bool!(Reset); +impl_compare_forwards_to_bool!(SyncReset); +impl_compare_forwards_to_bool!(AsyncReset); + +impl CastToImpl for Bool { + type ValueOutput = bool; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Bool, + ) -> Self::ValueOutput { + *value + } + + fn cast_sim_value_to(value: Cow<'_, SimValue>, _to_type: Bool) -> SimValue { + value.into_owned() + } + + fn cast_expr_to(value: Expr, _to_type: Bool) -> Expr { + value + } + + fn cast_valueless_to(value: Valueless, _to_type: Bool) -> Valueless { + value } } macro_rules! impl_cast_int_op { - ($name:ident, $from:ident, $to:ident) => { + ($name:ident, $from:ident, $to:ident, $to_value:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { arg: Expr<$from>, @@ -1633,9 +3267,6 @@ macro_rules! impl_cast_int_op { pub fn arg(self) -> Expr<$from> { self.arg } - pub fn ty(self) -> $to { - self.ty - } } impl ToLiteralBits for $name { @@ -1646,9 +3277,16 @@ macro_rules! impl_cast_int_op { impl_get_target_none!([ToWidth: Size] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $to; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name($name { @@ -1663,55 +3301,64 @@ macro_rules! impl_cast_int_op { } } - impl ExprCastTo<$to> for $from { - fn cast_to(src: Expr, to_type: $to) -> Expr<$to> { - $name::new(Expr::as_dyn_int(src), to_type).to_expr() + impl CastToImpl<$to> for $from { + type ValueOutput = $to_value; + + fn cast_value_to(_this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + to_type.from_int_wrapping(value.to_bigint()) + } + + fn cast_expr_to(value: Expr, to_type: $to) -> Expr<$to> { + $name::new(Expr::as_dyn_int(value), to_type).to_expr() } } }; } -impl_cast_int_op!(CastUIntToUInt, UIntType, UIntType); -impl_cast_int_op!(CastUIntToSInt, UIntType, SIntType); -impl_cast_int_op!(CastSIntToUInt, SIntType, UIntType); -impl_cast_int_op!(CastSIntToSInt, SIntType, SIntType); +impl_cast_int_op!(CastUIntToUInt, UIntType, UIntType, UIntValue); +impl_cast_int_op!(CastUIntToSInt, UIntType, SIntType, SIntValue); +impl_cast_int_op!(CastSIntToUInt, SIntType, UIntType, UIntValue); +impl_cast_int_op!(CastSIntToSInt, SIntType, SIntType, SIntValue); macro_rules! impl_cast_bit_op { - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[trait] $Trait:ident::$trait_fn:ident $(,)?) => { - impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to); + ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty, #[trait] $Trait:ident::$trait_fn:ident $(,)?) => { + impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to, #[to_value = $to_value_expr] $to_value); impl $Trait for Expr<$from> { - fn $trait_fn(&self) -> Expr<$to> { + type Output = Expr<$to>; + + fn $trait_fn(&self) -> Self::Output { self.cast_to_static() } } $(impl $Trait for Expr<$from_dyn> { - fn $trait_fn(&self) -> Expr<$to> { + type Output = Expr<$to>; + + fn $trait_fn(&self) -> Self::Output { self.cast_to_static() } })? }; - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[dyn] $to_dyn:ty $(,)?) => { - impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to); + ($name:ident, $from:ty, $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty, #[dyn] $to_dyn:ty $(,)?) => { + impl_cast_bit_op!($name, $from, $to, #[to_value = $to_value_expr] $to_value); - impl ExprCastTo<$to_dyn> for $from { - fn cast_to(src: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { + impl CastToImpl<$to_dyn> for $from { + type ValueOutput = <$to_dyn as BoolOrIntType>::Value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to_dyn) -> Self::ValueOutput { + >::cast_value_to(this, value, StaticType::TYPE).cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { assert!(to_type.width() == 1); Expr::as_dyn_int( - $name::new(src).to_expr(), + $name::new(value).to_expr(), ) } } - - $(impl ExprCastTo<$to_dyn> for $from_dyn { - fn cast_to(src: Expr, to_type: $to_dyn) -> Expr<$to_dyn> { - assert!(to_type.width() == 1); - Expr::as_dyn_int($name::new(Expr::from_dyn_int(src)).to_expr()) - } - })? }; - ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty $(,)?) => { + ($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[to_value = $to_value_expr:expr] $to_value:ty $(,)?) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { arg: Expr<$from>, @@ -1738,9 +3385,16 @@ macro_rules! impl_cast_bit_op { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = $to; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + Self::Type::TYPE + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -1750,50 +3404,88 @@ macro_rules! impl_cast_bit_op { } } - impl ExprCastTo<$to> for $from { - fn cast_to(src: Expr, _to_type: $to) -> Expr<$to> { - $name::new(src).to_expr() + impl CastToImpl<$to> for $from { + type ValueOutput = $to_value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + ($to_value_expr)(this, value, to_type) + } + + fn cast_expr_to(value: Expr, _to_type: $to) -> Expr<$to> { + $name::new(value).to_expr() } } - $(impl ExprCastTo<$to> for $from_dyn { - fn cast_to(src: Expr, _to_type: $to) -> Expr<$to> { - $name::new(Expr::<$from>::from_dyn_int(src)).to_expr() + $(impl CastToImpl<$to> for $from_dyn { + type ValueOutput = $to_value; + + fn cast_value_to(this: Self, value: Cow<'_, Self::SimValue>, to_type: $to) -> Self::ValueOutput { + <$from_dyn as CastToImpl<$from>>::cast_value_to(this, value, StaticType::TYPE).cast_to(to_type) + } + + fn cast_expr_to(value: Expr, _to_type: $to) -> Expr<$to> { + $name::new(Expr::<$from>::from_dyn_int(value)).to_expr() } })? }; } -impl_cast_bit_op!(CastBoolToUInt, Bool, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastBoolToSInt, Bool, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastUIntToBool, UInt<1>, #[dyn] UInt, Bool); -impl_cast_bit_op!(CastSIntToBool, SInt<1>, #[dyn] SInt, Bool); -impl_cast_bit_op!(CastBoolToSyncReset, Bool, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastUIntToSyncReset, UInt<1>, #[dyn] UInt, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastSIntToSyncReset, SInt<1>, #[dyn] SInt, SyncReset, #[trait] ToSyncReset::to_sync_reset); -impl_cast_bit_op!(CastBoolToAsyncReset, Bool, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastUIntToAsyncReset, UInt<1>, #[dyn] UInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset); -impl_cast_bit_op!(CastSyncResetToBool, SyncReset, Bool); -impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset); -impl_cast_bit_op!(CastAsyncResetToBool, AsyncReset, Bool); -impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset); -impl_cast_bit_op!(CastResetToBool, Reset, Bool); -impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[dyn] SInt); -impl_cast_bit_op!(CastBoolToClock, Bool, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastUIntToClock, UInt<1>, #[dyn] UInt, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastSIntToClock, SInt<1>, #[dyn] SInt, Clock, #[trait] ToClock::to_clock); -impl_cast_bit_op!(CastClockToBool, Clock, Bool); -impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt); -impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[dyn] SInt); +impl_cast_bit_op!(CastBoolToUInt, Bool, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastBoolToSInt, Bool, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastUIntToBool, UInt<1>, #[dyn] UInt, Bool, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0]] bool); +impl_cast_bit_op!(CastSIntToBool, SInt<1>, #[dyn] SInt, Bool, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0]] bool); +impl_cast_bit_op!(CastBoolToSyncReset, Bool, SyncReset, #[to_value = |_, v: Cow<'_, bool>, _| v.to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastUIntToSyncReset, UInt<1>, #[dyn] UInt, SyncReset, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastSIntToSyncReset, SInt<1>, #[dyn] SInt, SyncReset, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_sync_reset()] SimValue, #[trait] ToSyncReset::to_sync_reset); +impl_cast_bit_op!(CastBoolToAsyncReset, Bool, AsyncReset, #[to_value = |_, v: Cow<'_, bool>, _| v.to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!(CastUIntToAsyncReset, UInt<1>, #[dyn] UInt, AsyncReset, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_async_reset()] SimValue, #[trait] ToAsyncReset::to_async_reset); +impl_cast_bit_op!( + CastSyncResetToBool, + SyncReset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset, #[to_value = |_, v: Cow<'_, bool>, t| SimValue::from_value(t, *v)] SimValue); +impl_cast_bit_op!( + CastAsyncResetToBool, + AsyncReset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset, #[to_value = |_, v: Cow<'_, bool>, t| SimValue::from_value(t, *v)] SimValue); +impl_cast_bit_op!( + CastResetToBool, + Reset, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); +impl_cast_bit_op!(CastBoolToClock, Bool, Clock, #[to_value = |_, v: Cow<'_, bool>, _| v.to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!(CastUIntToClock, UInt<1>, #[dyn] UInt, Clock, #[to_value = |_, v: Cow<'_, UIntValue<_>>, _| v.bits()[0].to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!(CastSIntToClock, SInt<1>, #[dyn] SInt, Clock, #[to_value = |_, v: Cow<'_, SIntValue<_>>, _| v.bits()[0].to_clock()] SimValue, #[trait] ToClock::to_clock); +impl_cast_bit_op!( + CastClockToBool, + Clock, + Bool, + #[to_value = |_, v: Cow<'_, bool>, _| *v] + bool +); +impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] UIntValue>, #[dyn] UInt); +impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[to_value = |_, v: Cow<'_, bool>, _| (*v).into()] SIntValue>, #[dyn] SInt); impl ToReset for Expr { - fn to_reset(&self) -> Expr { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { struct Dispatch; impl ResetTypeDispatch for Dispatch { type Input = Expr; @@ -1815,101 +3507,279 @@ impl ToReset for Expr { } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, _to_type: AsyncReset) -> Expr { - src +impl ToReset for SimValue { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { + (*self).to_expr().to_sync_reset().to_reset() } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl ToReset for SimValue { + type Output = Expr; + + fn to_reset(&self) -> Self::Output { + (*self).to_expr().to_async_reset().to_reset() } } -impl ExprCastTo for AsyncReset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: AsyncReset, + ) -> Self::ValueOutput { + value.to_async_reset() + } + + fn cast_expr_to(value: Expr, _to_type: AsyncReset) -> Expr { + value } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: SyncReset, + ) -> Self::ValueOutput { + value.to_sync_reset() + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, _to_type: SyncReset) -> Expr { - src +impl CastToImpl for AsyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for SyncReset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: SyncReset, + ) -> Self::ValueOutput { + value.to_sync_reset() + } + + fn cast_expr_to(value: Expr, _to_type: SyncReset) -> Expr { + value } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for SyncReset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, _to_type: Reset) -> Expr { - src +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Reset { - fn cast_to(src: Expr, to_type: Clock) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: SyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, to_type: AsyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + #[track_caller] + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Reset, + ) -> Self::ValueOutput { + SimValue::from_value(to_type, *value) + } + + #[track_caller] + fn cast_expr_to(value: Expr, _to_type: Reset) -> Expr { + value } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, to_type: SyncReset) -> Expr { - src.cast_to(Bool).cast_to(to_type) +impl CastToImpl for Reset { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: Clock, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: Clock) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo for Clock { - fn cast_to(src: Expr, _to_type: Clock) -> Expr { - src +impl CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: AsyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: AsyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo<()> for PhantomConst { - fn cast_to(src: Expr, to_type: ()) -> Expr<()> { - src.cast_to_bits().cast_bits_to(to_type) +impl CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: SyncReset, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: SyncReset) -> Expr { + value.cast_to(Bool).cast_to(to_type) } } -impl ExprCastTo> for () { - fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { - src.cast_to_bits().cast_bits_to(to_type) +impl CastToImpl for Clock { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Clock, + ) -> Self::ValueOutput { + value.to_clock() + } + + fn cast_expr_to(value: Expr, _to_type: Clock) -> Expr { + value } } -impl ExprCastTo> +impl CastToImpl<()> for PhantomConst { + type ValueOutput = (); + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + _to_type: (), + ) -> Self::ValueOutput { + () + } + + fn cast_expr_to(value: Expr, to_type: ()) -> Expr<()> { + value.cast_to_bits().cast_bits_to(to_type) + } +} + +impl CastToImpl> for () { + type ValueOutput = PhantomConst; + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + to_type: PhantomConst, + ) -> Self::ValueOutput { + to_type + } + + fn cast_expr_to(value: Expr, to_type: PhantomConst) -> Expr> { + value.cast_to_bits().cast_bits_to(to_type) + } +} + +impl CastToImpl> for PhantomConst { - fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { - src.cast_to_bits().cast_bits_to(to_type) + type ValueOutput = PhantomConst; + + fn cast_value_to( + _this: Self, + _value: Cow<'_, Self::SimValue>, + to_type: PhantomConst, + ) -> Self::ValueOutput { + to_type + } + + fn cast_expr_to(value: Expr, to_type: PhantomConst) -> Expr> { + value.cast_to_bits().cast_bits_to(to_type) } } @@ -1934,10 +3804,10 @@ impl fmt::Debug for FieldAccess { impl FieldAccess { #[track_caller] pub fn new_by_index(base: Expr, field_index: usize) -> Self { - let field = Expr::ty(base).fields()[field_index]; + let field = base.ty().fields()[field_index]; let field_type = FieldType::from_canonical(field.ty); let literal_bits = base.to_literal_bits().map(|bits| { - bits[Expr::ty(base).field_offsets()[field_index].bit_width..][..field.ty.bit_width()] + bits[base.ty().field_offsets()[field_index].bit_width..][..field.ty.bit_width()] .intern() }); let target = base.target().map(|base| { @@ -1956,7 +3826,7 @@ impl FieldAccess { } #[track_caller] pub fn new_by_name(base: Expr, name: Interned) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let Some(field_index) = base_ty.name_indexes().get(&name) else { panic!("unknown field {name:?}: in {base_ty:?}"); }; @@ -1991,9 +3861,16 @@ impl ToLiteralBits for FieldAccess { } } -impl ToExpr for FieldAccess { +impl ValueType for FieldAccess { type Type = FieldType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.field_type + } +} + +impl ToExpr for FieldAccess { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::FieldAccess(FieldAccess { @@ -2023,10 +3900,10 @@ pub struct VariantAccess { impl VariantAccess { #[track_caller] pub fn new_by_index(base: Expr, variant_index: usize) -> Self { - let variant = Expr::ty(base).variants()[variant_index]; + let variant = base.ty().variants()[variant_index]; let variant_type = variant.ty.map(VariantType::from_canonical); let literal_bits = base.to_literal_bits().and_then(|bits| { - let discriminant_bit_width = Expr::ty(base).discriminant_bit_width(); + let discriminant_bit_width = base.ty().discriminant_bit_width(); if bits[..discriminant_bit_width] != [variant_index].view_bits::()[..discriminant_bit_width] { @@ -2047,7 +3924,7 @@ impl VariantAccess { } #[track_caller] pub fn new_by_name(base: Expr, name: Interned) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let Some(variant_index) = base_ty.name_indexes().get(&name) else { panic!("unknown variant {name:?}: in {base_ty:?}"); }; @@ -2078,9 +3955,17 @@ impl ToLiteralBits for VariantAccess { impl_get_target_none!([VariantType: Type] VariantAccess); -impl ToExpr for VariantAccess { +impl ValueType for VariantAccess { type Type = VariantType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.variant_type + .unwrap_or_else(|| VariantType::from_canonical(().canonical())) + } +} + +impl ToExpr for VariantAccess { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::VariantAccess(VariantAccess { @@ -2091,9 +3976,7 @@ impl ToExpr for VariantAccess { literal_bits: self.literal_bits, }) .intern(), - __ty: self - .variant_type - .unwrap_or_else(|| VariantType::from_canonical(().canonical())), + __ty: self.ty(), __flow: Flow::Source, } } @@ -2111,7 +3994,7 @@ pub struct ArrayIndex { impl ArrayIndex { #[track_caller] pub fn new(base: Expr, element_index: usize) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); assert!(element_index < base_ty.len()); let element_type = ElementType::from_canonical(base_ty.element()); let literal_bits = base.to_literal_bits().map(|bits| { @@ -2159,15 +4042,22 @@ impl GetTarget for ArrayIndex { } } -impl ToExpr for ArrayIndex { +impl ValueType for ArrayIndex { type Type = ElementType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.element_type + } +} + +impl ToExpr for ArrayIndex { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::ArrayIndex(ArrayIndex { base: self.base, element_index: self.element_index, - element_type: Expr::ty(self.base).element(), + element_type: self.base.ty().element(), literal_bits: self.literal_bits, target: self.target, }) @@ -2203,7 +4093,7 @@ pub struct DynArrayIndex { impl DynArrayIndex { #[track_caller] pub fn new(base: Expr, element_index: Expr) -> Self { - let base_ty = Expr::ty(base); + let base_ty = base.ty(); let element_type = ElementType::from_canonical(base_ty.element()); let literal_bits = base .to_literal_bits() @@ -2255,15 +4145,22 @@ impl GetTarget for DynArrayIndex { } } -impl ToExpr for DynArrayIndex { +impl ValueType for DynArrayIndex { type Type = ElementType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.element_type + } +} + +impl ToExpr for DynArrayIndex { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::DynArrayIndex(DynArrayIndex { base: self.base, element_index: self.element_index, - element_type: Expr::ty(self.base).element(), + element_type: self.base.ty().element(), literal_bits: self.literal_bits, target: self.target, }) @@ -2356,7 +4253,7 @@ macro_rules! impl_int_slice { impl $name { pub fn new(base: Expr<$ty>, range: Range) -> Self { assert!( - range.start <= range.end && range.end <= Expr::ty(base).width(), + range.start <= range.end && range.end <= base.ty().width(), "invalid range" ); let literal_bits = base @@ -2385,37 +4282,63 @@ macro_rules! impl_int_slice { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + UInt::new_dyn(self.range().len()) + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), - __ty: UInt::new_dyn(self.range().len()), + __ty: self.ty(), __flow: Flow::Source, } } } + impl> std::ops::Index for Valueless<$ty> { + type Output = Valueless; + + #[track_caller] + fn index(&self, index: I) -> &Self::Output { + let range = self.ty().slice_index_to_range(index); + Interned::into_inner(Valueless::new(UInt::new_dyn(range.len())).intern_sized()) + } + } + impl> ExprIndex for $ty { type Output = UInt; #[track_caller] fn expr_index(this: &Expr, index: I) -> &Expr { let base = Expr::as_dyn_int(*this); - let base_ty = Expr::ty(base); - let range = base_ty.slice_index_to_range(index); + let range = base.ty().slice_index_to_range(index); Interned::into_inner($name::new(base, range).to_expr().intern_sized()) } } + impl std::ops::Index for Valueless<$ty> { + type Output = Valueless; + + #[track_caller] + fn index(&self, index: usize) -> &Self::Output { + assert!(index < self.ty().width()); + &const { Valueless::new(Bool) } + } + } + impl ExprIndex for $ty { type Output = Bool; #[track_caller] fn expr_index(this: &Expr, index: usize) -> &Expr { let base = Expr::as_dyn_int(*this); - let base_ty = Expr::ty(base); + let base_ty = base.ty(); assert!(index < base_ty.width()); Interned::into_inner( $name::new(base, index..(index + 1)) @@ -2457,9 +4380,16 @@ macro_rules! impl_reduce_bits { impl_get_target_none!([] $name); - impl ToExpr for $name { + impl ValueType for $name { type Type = UInt<1>; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + + impl ToExpr for $name { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::$name(*self).intern(), @@ -2480,126 +4410,99 @@ impl_reduce_bits!(ReduceBitXorU, UInt, |bits| (bits.count_ones() % 2 != 0) impl_reduce_bits!(ReduceBitXorS, SInt, |bits| (bits.count_ones() % 2 != 0) .to_literal_bits()); -impl ReduceBits for Expr> { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - ReduceBitAndU::new(Expr::as_dyn_int(self)).to_expr() +impl ReduceBitsImpl for UIntType { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + value.bits().all().into() } - fn reduce_bitor(self) -> Self::UIntOutput { - ReduceBitOrU::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + value.bits().any().into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - ReduceBitXorU::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (value.bits().count_ones() % 2 != 0).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self.reduce_bitor().cast_to_static() + fn expr_reduce_bitand(this: Expr) -> Expr> { + ReduceBitAndU::new(Expr::as_dyn_int(this)).to_expr() } - fn any_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitand()).cast_to_static() + fn expr_reduce_bitor(this: Expr) -> Expr> { + ReduceBitOrU::new(Expr::as_dyn_int(this)).to_expr() } - fn all_one_bits(self) -> Self::BoolOutput { - self.reduce_bitand().cast_to_static() - } - - fn all_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitor()).cast_to_static() - } - - fn parity_odd(self) -> Self::BoolOutput { - self.reduce_bitxor().cast_to_static() - } - - fn parity_even(self) -> Self::BoolOutput { - (!self.reduce_bitxor()).cast_to_static() + fn expr_reduce_bitxor(this: Expr) -> Expr> { + ReduceBitXorU::new(Expr::as_dyn_int(this)).to_expr() } } -impl ReduceBits for Expr> { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - ReduceBitAndS::new(Expr::as_dyn_int(self)).to_expr() +impl ReduceBitsImpl for SIntType { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + value.bits().all().into() } - fn reduce_bitor(self) -> Self::UIntOutput { - ReduceBitOrS::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + value.bits().any().into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - ReduceBitXorS::new(Expr::as_dyn_int(self)).to_expr() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (value.bits().count_ones() % 2 != 0).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self.reduce_bitor().cast_to_static() + fn expr_reduce_bitand(this: Expr) -> Expr> { + ReduceBitAndS::new(Expr::as_dyn_int(this)).to_expr() } - fn any_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitand()).cast_to_static() + fn expr_reduce_bitor(this: Expr) -> Expr> { + ReduceBitOrS::new(Expr::as_dyn_int(this)).to_expr() } - fn all_one_bits(self) -> Self::BoolOutput { - self.reduce_bitand().cast_to_static() - } - - fn all_zero_bits(self) -> Self::BoolOutput { - (!self.reduce_bitor()).cast_to_static() - } - - fn parity_odd(self) -> Self::BoolOutput { - self.reduce_bitxor().cast_to_static() - } - - fn parity_even(self) -> Self::BoolOutput { - (!self.reduce_bitxor()).cast_to_static() + fn expr_reduce_bitxor(this: Expr) -> Expr> { + ReduceBitXorS::new(Expr::as_dyn_int(this)).to_expr() } } -impl ReduceBits for Expr { - type UIntOutput = Expr>; - type BoolOutput = Expr; - - fn reduce_bitand(self) -> Self::UIntOutput { - self.cast_to_static() +impl ReduceBitsImpl for Bool { + fn value_reduce_bitand( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (*value).into() } - fn reduce_bitor(self) -> Self::UIntOutput { - self.cast_to_static() + fn value_reduce_bitor(_this: Self, value: Cow<'_, Self::SimValue>) -> UIntValue> { + (*value).into() } - fn reduce_bitxor(self) -> Self::UIntOutput { - self.cast_to_static() + fn value_reduce_bitxor( + _this: Self, + value: Cow<'_, Self::SimValue>, + ) -> UIntValue> { + (*value).into() } - fn any_one_bits(self) -> Self::BoolOutput { - self + fn expr_reduce_bitand(this: Expr) -> Expr> { + this.cast_to_static() } - fn any_zero_bits(self) -> Self::BoolOutput { - !self + fn expr_reduce_bitor(this: Expr) -> Expr> { + this.cast_to_static() } - fn all_one_bits(self) -> Self::BoolOutput { - self - } - - fn all_zero_bits(self) -> Self::BoolOutput { - !self - } - - fn parity_odd(self) -> Self::BoolOutput { - self - } - - fn parity_even(self) -> Self::BoolOutput { - !self + fn expr_reduce_bitxor(this: Expr) -> Expr> { + this.cast_to_static() } } @@ -2629,13 +4532,20 @@ impl ToLiteralBits for CastToBits { impl_get_target_none!([] CastToBits); -impl ToExpr for CastToBits { +impl ValueType for CastToBits { type Type = UInt; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + UInt::new_dyn(self.arg.ty().bit_width()) + } +} + +impl ToExpr for CastToBits { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::CastToBits(*self).intern(), - __ty: UInt::new_dyn(Expr::ty(self.arg).bit_width()), + __ty: self.ty(), __flow: Flow::Source, } } @@ -2653,7 +4563,7 @@ impl CastBitsTo { pub fn new(arg: Expr, ty: T) -> Self { let ty_props = ty.canonical().type_properties(); assert!(ty_props.is_castable_from_bits); - assert_eq!(Expr::ty(arg).width(), ty_props.bit_width); + assert_eq!(arg.ty().width(), ty_props.bit_width); Self { arg, ty, @@ -2663,9 +4573,6 @@ impl CastBitsTo { pub fn arg(self) -> Expr { self.arg } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for CastBitsTo { @@ -2676,9 +4583,16 @@ impl ToLiteralBits for CastBitsTo { impl_get_target_none!([T: Type] CastBitsTo); -impl ToExpr for CastBitsTo { +impl ValueType for CastBitsTo { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for CastBitsTo { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::CastBitsTo(CastBitsTo { @@ -2703,9 +4617,6 @@ impl Uninit { pub fn new(ty: T) -> Self { Self { ty } } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for Uninit { @@ -2716,9 +4627,16 @@ impl ToLiteralBits for Uninit { impl_get_target_none!([T: Type] Uninit); -impl ToExpr for Uninit { +impl ValueType for Uninit { type Type = T; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for Uninit { fn to_expr(&self) -> Expr { Expr { __enum: ExprEnum::Uninit(Uninit { diff --git a/crates/fayalite/src/expr/ops/test_ops_impls.rs b/crates/fayalite/src/expr/ops/test_ops_impls.rs new file mode 100644 index 0000000..d29aff0 --- /dev/null +++ b/crates/fayalite/src/expr/ops/test_ops_impls.rs @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{expr::Valueless, expr::ops::make_impls, prelude::*}; +use std::num::NonZero; + +macro_rules! assert_neg_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_neg_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Neg>::Output as ValueType>::Type, + > { + std::ops::Neg::neg(v) + } + }; + }; +} + +make_impls! { + #[kinds((sint_local<'a, Width>))] + assert_neg_impls! {} +} + +macro_rules! assert_not_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_not_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Not>::Output as ValueType>::Type, + > { + std::ops::Not::not(v) + } + }; + }; +} + +make_impls! { + #[kinds((int_local<'a, Width>), (bool<'a>))] + assert_not_impls! {} +} + +macro_rules! assert_cast_to_bits_impls { + ([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => { + const _: () = { + fn _check_cast_to_bits_impl<$($Lifetimes)*$($Bounds)*>(v: $ty) + -> impl ValueType< + ValueCategory = <$ty as ValueType>::ValueCategory, + Type = <::Type> as CastToBits>::Output as ValueType>::Type, + > { + <$ty as CastToBits>::cast_to_bits(&v) + } + }; + }; +} + +make_impls! { + #[kinds((uint<'a, Width>), (sint<'a, Width>), (bool<'a>))] + assert_cast_to_bits_impls! {} +} + +macro_rules! assert_simple_bin_op_impls { + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), !$Trait:ident::$f:ident) => { + #[expect(dead_code)] + const _: () = { + trait HasImpl { + fn check_impl(self); + } + + trait NoImpl { + fn check_impl(&self) -> &'static dyn NoImpl; + } + + impl NoImpl for (L, R) { + fn check_impl(&self) -> &'static dyn NoImpl { + unreachable!() + } + } + + impl, R> HasImpl for (L, R) { + fn check_impl(self) {} + } + + fn check_simple_bin_op_no_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) { + let _: &'static dyn NoImpl = (l, r).check_impl(); + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shl::shl) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize) + -> impl ValueType< + ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Shl>::Output as ValueType>::Type, + > { + std::ops::Shl::shl(l, r) + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shr::shr) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize) + -> impl ValueType< + ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::Shr>::Output as ValueType>::Type, + > { + std::ops::Shr::shr(l, r) + } + }; + }; + ([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), $Trait:ident::$f:ident) => { + const _: () = { + #[expect(dead_code)] + fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) + -> impl ValueType< + ValueCategory = <($($L)*, $($R)*) as ValueType>::ValueCategory, + Type = <::Type> as std::ops::$Trait::Type>>>::Output as ValueType>::Type, + > { + std::ops::$Trait::$f(l, r) + } + }; + }; + ($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), !$FirstTrait:ident::$first_f:ident, $($rest:tt)*) => { + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), !$FirstTrait::$first_f + } + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)* + } + }; + ($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), $FirstTrait:ident::$first_f:ident, $($rest:tt)*) => { + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $FirstTrait::$first_f + } + assert_simple_bin_op_impls! { + $LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)* + } + }; +} + +make_impls! { + #[kinds((uint_local<'l, L>))] + #[kinds((uint<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((uint<'l, L>))] + #[kinds((uint_local<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor, Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((sint_local<'l, L>))] + #[kinds((sint<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((sint<'l, L>))] + #[kinds((sint_local<'r, R>))] + assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((sint<'l, L>))] + #[kinds((uint_local<'r, R>))] + assert_simple_bin_op_impls! {Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((uint_local<'l, L>))] + assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((sint_local<'l, L>))] + assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr} +} + +make_impls! { + #[kinds((bool_local<'l>))] + #[kinds((bool_local<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((bool<'l>))] + #[kinds((Valueless))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((Valueless))] + #[kinds((bool<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +make_impls! { + #[kinds((bool_at_most_sim_value<'l>))] + #[kinds((bool_at_most_sim_value<'r>))] + assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor} +} + +// assert there are no impls of BitAnd/BitOr/BitXor between Expr and Rust's bool, +// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do +// what is usually wanted. +make_impls! { + #[kinds((Expr))] + #[kinds(bool)] + assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor} +} + +make_impls! { + #[kinds(bool)] + #[kinds((Expr))] + assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor} +} diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index c8c55e9..95d8e0f 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -3,7 +3,7 @@ use crate::{ array::Array, bundle::{Bundle, BundleField}, - expr::{Expr, Flow, ToExpr}, + expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, intern::{Intern, Interned}, memory::{DynPortType, MemPort}, module::{Instance, ModuleIO, TargetName}, @@ -196,9 +196,18 @@ macro_rules! impl_target_base { } } - impl ToExpr for $TargetBase { + impl ValueType for $TargetBase { type Type = CanonicalType; + type ValueCategory = ValueCategoryExpr; + fn ty(&self) -> Self::Type { + match self { + $(Self::$Variant(v) => v.ty().canonical(),)* + } + } + } + + impl ToExpr for $TargetBase { fn to_expr(&self) -> Expr { match self { $(Self::$Variant(v) => Expr::canonical(v.to_expr()),)* diff --git a/crates/fayalite/src/expr/value_category.rs b/crates/fayalite/src/expr/value_category.rs new file mode 100644 index 0000000..548ae7b --- /dev/null +++ b/crates/fayalite/src/expr/value_category.rs @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{expr::ValueType, ty::Type}; + +trait ValueCategorySealed {} + +#[expect(private_bounds)] +pub trait ValueCategory: + ValueCategorySealed + + Copy + + Ord + + Default + + std::fmt::Debug + + std::hash::Hash + + 'static + + Send + + Sync +{ + type DispatchOutput>: ValueType; + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput; + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum; +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryValue; + +impl ValueCategorySealed for ValueCategoryValue {} + +impl ValueCategory for ValueCategoryValue { + type DispatchOutput> = D::Value; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_value() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Value(dispatch.dispatch_value()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategorySimValue; + +impl ValueCategorySealed for ValueCategorySimValue {} + +impl ValueCategory for ValueCategorySimValue { + type DispatchOutput> = D::SimValue; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_sim_value() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::SimValue(dispatch.dispatch_sim_value()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryExpr; + +impl ValueCategorySealed for ValueCategoryExpr {} + +impl ValueCategory for ValueCategoryExpr { + type DispatchOutput> = D::Expr; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_expr() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Expr(dispatch.dispatch_expr()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +pub struct ValueCategoryValueless; + +impl ValueCategorySealed for ValueCategoryValueless {} + +impl ValueCategory for ValueCategoryValueless { + type DispatchOutput> = D::Valueless; + + #[track_caller] + fn dispatch>( + dispatch: D, + ) -> Self::DispatchOutput { + dispatch.dispatch_valueless() + } + + #[track_caller] + fn dispatch_enum>( + dispatch: D, + ) -> ValueCategoryDispatchOutputEnum { + ValueCategoryDispatchOutputEnum::Valueless(dispatch.dispatch_valueless()) + } +} + +pub trait ValueCategoryIsValue: ValueCategoryIsValueOrSimValue {} + +impl ValueCategoryIsValue for ValueCategoryValue {} + +pub trait ValueCategoryIsValueOrSimValue: ValueCategoryIsValueSimValueOrExpr {} + +impl ValueCategoryIsValueOrSimValue for ValueCategoryValue {} +impl ValueCategoryIsValueOrSimValue for ValueCategorySimValue {} + +pub trait ValueCategoryIsValueSimValueOrExpr: ValueCategoryIsValueSimValueExprOrValueless {} + +impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryValue {} +impl ValueCategoryIsValueSimValueOrExpr for ValueCategorySimValue {} +impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryExpr {} + +pub trait ValueCategoryIsValueSimValueExprOrValueless: ValueCategory {} + +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValue {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategorySimValue {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsValueless: ValueCategoryIsExprOrValueless {} + +impl ValueCategoryIsValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsExprOrValueless: ValueCategoryIsValueSimValueExprOrValueless {} + +impl ValueCategoryIsExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsExprOrValueless for ValueCategoryValueless {} + +pub trait ValueCategoryIsSimValueExprOrValueless: + ValueCategoryIsValueSimValueExprOrValueless +{ +} + +impl ValueCategoryIsSimValueExprOrValueless for ValueCategorySimValue {} +impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryExpr {} +impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryValueless {} + +trait ValueCategoryCommonSealed {} + +#[expect(private_bounds)] +pub trait ValueCategoryCommon< + T: ValueCategoryCommonSealed + + Copy + + Ord + + Default + + std::fmt::Debug + + std::hash::Hash + + 'static + + Send + + Sync, +>: ValueCategory +{ + type Common: ValueCategory; +} + +macro_rules! impl_value_category_common { + ($($T:ident),* $(,)?) => { + impl_value_category_common!([$($T,)*]); + }; + ([$($A:ident,)?]) => {}; + ([$FirstT:ident, $($T:ident,)+]) => { + impl_value_category_common!([$($T,)*]); + + impl<$FirstT: ValueCategory, $($T: ValueCategory),*> ValueCategoryCommonSealed for ($FirstT, $($T),*) {} + + impl ValueCategoryCommon<($FirstT, $($T),*)> for This + where + $FirstT: ValueCategory, + $($T: ValueCategory,)* + This: ValueCategoryCommon<($FirstT,), Common: ValueCategoryCommon<($($T,)*)>>, + { + type Common = <>::Common as ValueCategoryCommon<($($T,)*)>>::Common; + } + }; +} + +impl_value_category_common!(T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0); + +impl ValueCategoryCommonSealed for (T,) {} + +impl ValueCategoryCommonSealed for () {} + +impl ValueCategoryCommon<()> for T { + type Common = T; +} + +impl ValueCategoryCommon<(ValueCategoryValue,)> for T { + type Common = ValueCategoryValue; +} + +impl ValueCategoryCommon<(ValueCategorySimValue,)> for T { + type Common = ValueCategorySimValue; +} + +impl ValueCategoryCommon<(T,)> for ValueCategorySimValue { + type Common = ValueCategorySimValue; +} + +impl ValueCategoryCommon<(ValueCategoryExpr,)> for T { + type Common = ValueCategoryExpr; +} + +impl ValueCategoryCommon<(T,)> for ValueCategoryExpr { + type Common = ValueCategoryExpr; +} + +impl ValueCategoryCommon<(ValueCategoryValueless,)> + for T +{ + type Common = ValueCategoryValueless; +} + +impl ValueCategoryCommon<(T,)> for ValueCategoryValueless { + type Common = ValueCategoryValueless; +} + +pub trait ValueCategoryDispatch: Sized { + type Type: Type; + type InputValueCategory: ValueCategory; + type Value: ValueType; + type SimValue: ValueType; + type Expr: ValueType; + type Valueless: ValueType; + fn dispatch_value(self) -> Self::Value + where + Self: ValueCategoryDispatch; + fn dispatch_sim_value(self) -> Self::SimValue + where + Self: ValueCategoryDispatch; + fn dispatch_expr(self) -> Self::Expr + where + Self: ValueCategoryDispatch; + fn dispatch_valueless(self) -> Self::Valueless + where + Self: ValueCategoryDispatch; + fn dispatch(self) -> ::DispatchOutput { + Self::InputValueCategory::dispatch(self) + } + fn dispatch_enum(self) -> ValueCategoryDispatchOutputEnum { + Self::InputValueCategory::dispatch_enum(self) + } +} + +pub enum ValueCategoryDispatchOutputEnum { + Value(T::Value), + SimValue(T::SimValue), + Expr(T::Expr), + Valueless(T::Valueless), +} + +impl ValueCategoryDispatchOutputEnum { + pub fn try_into_value(self) -> Result { + if let Self::Value(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_sim_value(self) -> Result { + if let Self::SimValue(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_expr(self) -> Result { + if let Self::Expr(v) = self { + Ok(v) + } else { + Err(self) + } + } + pub fn try_into_valueless(self) -> Result { + if let Self::Valueless(v) = self { + Ok(v) + } else { + Err(self) + } + } + #[track_caller] + #[cold] + fn unwrap_invalid(self, expected_kind: &'static str) -> ! { + match self { + Self::Value(_) => panic!("expected {expected_kind}, got Value"), + Self::SimValue(_) => panic!("expected {expected_kind}, got SimValue"), + Self::Expr(_) => panic!("expected {expected_kind}, got Expr"), + Self::Valueless(_) => panic!("expected {expected_kind}, got Valueless"), + } + } + #[track_caller] + pub fn into_value_unwrap(self) -> T::Value { + let Self::Value(v) = self else { + self.unwrap_invalid("Value"); + }; + v + } + #[track_caller] + pub fn into_sim_value_unwrap(self) -> T::SimValue { + let Self::SimValue(v) = self else { + self.unwrap_invalid("SimValue"); + }; + v + } + #[track_caller] + pub fn into_expr_unwrap(self) -> T::Expr { + let Self::Expr(v) = self else { + self.unwrap_invalid("Expr"); + }; + v + } + #[track_caller] + pub fn into_valueless_unwrap(self) -> T::Valueless { + let Self::Valueless(v) = self else { + self.unwrap_invalid("Valueless"); + }; + v + } + pub fn ty(&self) -> T::Type { + match self { + Self::Value(v) => v.ty(), + Self::SimValue(v) => v.ty(), + Self::Expr(v) => v.ty(), + Self::Valueless(v) => v.ty(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + type Identity = T; + + macro_rules! check_categories { + ($($Categories:ident,)+) => { + check_categories!([] [$($Categories,)+]); + }; + ([$($Categories:ident,)*] []) => {}; + ([$($WeakerCategories:ident,)*] [$Category:ident, $($StrongerCategories:ident,)*]) => { + $(const _: $Category = Identity::<<$WeakerCategories as ValueCategoryCommon<($Category,)>>::Common> {}; + const _: $Category = Identity::<<$Category as ValueCategoryCommon<($WeakerCategories,)>>::Common> {};)* + const _: $Category = Identity::<<$Category as ValueCategoryCommon<($Category,)>>::Common> {}; + check_categories!([$($WeakerCategories,)* $Category,] [$($StrongerCategories,)*]); + }; + } + + check_categories!( + ValueCategoryValue, + ValueCategorySimValue, + ValueCategoryExpr, + ValueCategoryValueless, + ); +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 59fbec7..383bd95 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -12,7 +12,7 @@ use crate::{ clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo, Expr, ExprEnum, + CastBitsTo, Expr, ExprEnum, ToExpr, ValueType, ops::{self, VariantAccess}, target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, @@ -877,7 +877,7 @@ impl<'a> Exporter<'a> { definitions: &RcDefinitions, const_ty: bool, ) -> Result { - let from_ty = Expr::ty(value); + let from_ty = value.ty(); let mut value = self.expr(Expr::canonical(value), definitions, const_ty)?; if from_ty.width().checked_add(1) == Some(to_ty.width()) && !FromTy::Signed::VALUE @@ -926,7 +926,7 @@ impl<'a> Exporter<'a> { definitions: &RcDefinitions, const_ty: bool, ) -> Result { - let base_width = Expr::ty(base).width(); + let base_width = base.ty().width(); let base = self.expr(Expr::canonical(base), definitions, const_ty)?; if range.is_empty() { Ok(format!("tail({base}, {base_width})")) @@ -1470,7 +1470,7 @@ impl<'a> Exporter<'a> { ExprEnum::SIntLiteral(literal) => Ok(self.sint_literal(&literal)), ExprEnum::BoolLiteral(literal) => Ok(self.bool_literal(literal)), ExprEnum::PhantomConst(ty) => self.expr( - UInt[0].zero().cast_bits_to(ty.canonical()), + UInt[0].zero().to_expr().cast_bits_to(ty.canonical()), definitions, const_ty, ), @@ -1653,7 +1653,7 @@ impl<'a> Exporter<'a> { let value_str = self.expr(expr.arg(), definitions, const_ty)?; self.expr_cast_to_bits( value_str, - Expr::ty(expr.arg()), + expr.arg().ty(), definitions, Indent { indent_depth: &Cell::new(0), @@ -1776,7 +1776,7 @@ impl<'a> Exporter<'a> { let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; let name = self .type_state - .get_bundle_field(Expr::ty(expr.base()), expr.field_name())?; + .get_bundle_field(expr.base().ty(), expr.field_name())?; write!(out, ".{name}").unwrap(); Ok(out) } @@ -2101,12 +2101,12 @@ impl<'a> Exporter<'a> { rhs, source_location, }) => { - if Expr::ty(lhs) != Expr::ty(rhs) { + if lhs.ty() != rhs.ty() { writeln!( body, "{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}", - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), ) .unwrap(); } @@ -2200,7 +2200,7 @@ impl<'a> Exporter<'a> { FileInfo::new(source_location), ) .unwrap(); - let enum_ty = Expr::ty(expr); + let enum_ty = expr.ty(); let match_arms_indent = indent.push(); for ((variant_index, variant), match_arm_block) in enum_ty.variants().iter().enumerate().zip(blocks) diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 7fa77ce..524ef27 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -4,8 +4,10 @@ use crate::{ array::ArrayType, expr::{ - Expr, NotALiteralExpr, ToExpr, ToLiteralBits, + Expr, NotALiteralExpr, ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, + Valueless, target::{GetTarget, Target}, + value_category::ValueCategoryValue, }, hdl, intern::{Intern, Interned, Memoize}, @@ -31,7 +33,7 @@ use std::{ num::NonZero, ops::{Index, Not, Range, RangeBounds, RangeInclusive}, str::FromStr, - sync::Arc, + sync::{Arc, OnceLock}, }; mod uint_in_range; @@ -60,6 +62,56 @@ mod sealed { pub const DYN_SIZE: usize = !0; pub type DynSize = ConstUsize; +trait KnownSizeBaseSealed {} + +impl KnownSizeBaseSealed for [(); N] {} + +#[expect(private_bounds)] +pub trait KnownSizeBase: KnownSizeBaseSealed {} + +macro_rules! impl_known_size_base { + ($($size:literal),* $(,)?) => { + $(impl KnownSizeBase for [(); $size] {})* + }; +} + +#[rustfmt::skip] +impl_known_size_base! { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F, + 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F, + 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, + 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F, + 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F, + 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F, + 0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06A, 0x06B, 0x06C, 0x06D, 0x06E, 0x06F, + 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F, + 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08A, 0x08B, 0x08C, 0x08D, 0x08E, 0x08F, + 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09A, 0x09B, 0x09C, 0x09D, 0x09E, 0x09F, + 0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x0AF, + 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0x0BF, + 0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, 0x0CF, + 0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DC, 0x0DD, 0x0DE, 0x0DF, + 0x0E0, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x0E7, 0x0E8, 0x0E9, 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF, + 0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, 0x0FC, 0x0FD, 0x0FE, 0x0FF, + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x10F, + 0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11A, 0x11B, 0x11C, 0x11D, 0x11E, 0x11F, + 0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12A, 0x12B, 0x12C, 0x12D, 0x12E, 0x12F, + 0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13A, 0x13B, 0x13C, 0x13D, 0x13E, 0x13F, + 0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14A, 0x14B, 0x14C, 0x14D, 0x14E, 0x14F, + 0x150, 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, 0x15A, 0x15B, 0x15C, 0x15D, 0x15E, 0x15F, + 0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, 0x169, 0x16A, 0x16B, 0x16C, 0x16D, 0x16E, 0x16F, + 0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x17F, + 0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, 0x18A, 0x18B, 0x18C, 0x18D, 0x18E, 0x18F, + 0x190, 0x191, 0x192, 0x193, 0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19A, 0x19B, 0x19C, 0x19D, 0x19E, 0x19F, + 0x1A0, 0x1A1, 0x1A2, 0x1A3, 0x1A4, 0x1A5, 0x1A6, 0x1A7, 0x1A8, 0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD, 0x1AE, 0x1AF, + 0x1B0, 0x1B1, 0x1B2, 0x1B3, 0x1B4, 0x1B5, 0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB, 0x1BC, 0x1BD, 0x1BE, 0x1BF, + 0x1C0, 0x1C1, 0x1C2, 0x1C3, 0x1C4, 0x1C5, 0x1C6, 0x1C7, 0x1C8, 0x1C9, 0x1CA, 0x1CB, 0x1CC, 0x1CD, 0x1CE, 0x1CF, + 0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4, 0x1D5, 0x1D6, 0x1D7, 0x1D8, 0x1D9, 0x1DA, 0x1DB, 0x1DC, 0x1DD, 0x1DE, 0x1DF, + 0x1E0, 0x1E1, 0x1E2, 0x1E3, 0x1E4, 0x1E5, 0x1E6, 0x1E7, 0x1E8, 0x1E9, 0x1EA, 0x1EB, 0x1EC, 0x1ED, 0x1EE, 0x1EF, + 0x1F0, 0x1F1, 0x1F2, 0x1F3, 0x1F4, 0x1F5, 0x1F6, 0x1F7, 0x1F8, 0x1F9, 0x1FA, 0x1FB, 0x1FC, 0x1FD, 0x1FE, 0x1FF, + 0x200, +} + pub trait KnownSize: GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default { @@ -89,35 +141,15 @@ pub trait KnownSize: + ToSimValueWithType>; } -macro_rules! known_widths { - ([] $($bits:literal)+) => { - impl KnownSize for ConstUsize<{ - let v = 0; - $(let v = v * 2 + $bits;)* - v - }> { - const SIZE: Self = Self; - type ArrayMatch = [Expr; Self::VALUE]; - type ArraySimValue = [SimValue; Self::VALUE]; - } - }; - ([2 $($rest:tt)*] $($bits:literal)+) => { - known_widths!([$($rest)*] $($bits)* 0); - known_widths!([$($rest)*] $($bits)* 1); - }; - ([2 $($rest:tt)*]) => { - known_widths!([$($rest)*] 0); - known_widths!([$($rest)*] 1); - impl KnownSize for ConstUsize<{2 $(* $rest)*}> { - const SIZE: Self = Self; - type ArrayMatch = [Expr; Self::VALUE]; - type ArraySimValue = [SimValue; Self::VALUE]; - } - }; +impl KnownSize for ConstUsize +where + [(); N]: KnownSizeBase, +{ + const SIZE: Self = Self; + type ArrayMatch = [Expr; N]; + type ArraySimValue = [SimValue; N]; } -known_widths!([2 2 2 2 2 2 2 2 2]); - pub trait SizeType: sealed::SizeTypeSealed + Copy @@ -530,6 +562,23 @@ fn deserialize_int_value<'de, D: Deserializer<'de>>( }) } +macro_rules! impl_valueless_op_forward { + ( + #[forward_to = $ForwardTrait:path] + impl<$($G:ident: $Bound:path),*> $TraitPath:ident$(::$TraitPathRest:ident)+<$($Rhs:ty)?> for $SelfTy:ty { + $($(type $Output:ident;)? + use $forward_fn:path as $fn:ident($self:ident $(, $rhs:ident)?);)? + } + ) => { + impl<$($G: $Bound),*> $TraitPath$(::$TraitPathRest)+<$($Rhs)?> for $SelfTy { + $($(type $Output = <$SelfTy as $ForwardTrait>::$Output;)? + fn $fn($self $(, $rhs: $Rhs)?) $(-> Self::$Output)? { + $forward_fn($self $(, $rhs)?) + })? + } + }; +} + macro_rules! impl_int { ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -582,6 +631,176 @@ macro_rules! impl_int { } } + impl std::ops::Add>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn add(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty() + .width() + .max(rhs.ty().width()) + .checked_add(1) + .expect("width too big"), + )) + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::Add>>] + impl std::ops::Sub>> + for Valueless<$name> + { + type Output; + + use std::ops::Add::>>::add as sub(self, rhs); + } + } + + impl std::ops::BitOr>> + for Valueless<$name> + { + type Output = Valueless; + + fn bitor(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new(UInt::new_dyn(self.ty().width().max(rhs.ty().width()))) + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::BitOr>>] + impl std::ops::BitAnd>> + for Valueless<$name> + { + type Output; + + use std::ops::BitOr::>>::bitor as bitand(self, rhs); + } + } + + impl_valueless_op_forward! { + #[forward_to = std::ops::BitOr>>] + impl std::ops::BitXor>> + for Valueless<$name> + { + type Output; + + use std::ops::BitOr::>>::bitor as bitxor(self, rhs); + } + } + + impl std::ops::Not for Valueless<$name> { + type Output = Valueless>; + + fn not(self) -> Self::Output { + Valueless::new(self.ty().as_same_width_uint()) + } + } + + impl std::ops::Not for $value { + type Output = UIntValue; + + fn not(mut self) -> Self::Output { + // modifies in-place despite using `Not::not` + let _ = Not::not(self.bits_mut()); + UIntValue::new(self.into_bits()) + } + } + + impl std::ops::Not for &'_ $value { + type Output = UIntValue; + + fn not(self) -> Self::Output { + $value::not(self.clone()) + } + } + + impl std::ops::Mul>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn mul(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty() + .width() + .checked_add(rhs.ty().width()) + .expect("width too big"), + )) + } + } + + impl std::ops::Rem>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn rem(self, rhs: Valueless<$name>) -> Self::Output { + Valueless::new($name::new_dyn(self.ty().width().min(rhs.ty().width()))) + } + } + + impl std::ops::Shl>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + #[track_caller] + fn shl(self, rhs: Valueless>) -> Self::Output { + let Some(pow2_rhs_width) = rhs + .ty() + .width() + .try_into() + .ok() + .and_then(|v| 2usize.checked_pow(v)) + else { + panic!( + "dynamic left-shift amount's bit-width is too big, try casting the shift \ + amount to a smaller bit-width before shifting" + ); + }; + Valueless::new($name::new_dyn( + (pow2_rhs_width - 1) + .checked_add(self.ty().width()) + .expect("width too big"), + )) + } + } + + impl std::ops::Shr>> + for Valueless<$name> + { + type Output = Valueless<$name>; + + fn shr(self, _rhs: Valueless>) -> Self::Output { + self + } + } + + impl std::ops::Shl for Valueless<$name> { + type Output = Valueless<$name>; + + fn shl(self, rhs: usize) -> Self::Output { + Valueless::new($name::new_dyn( + self.ty().width().checked_add(rhs).expect("width too big"), + )) + } + } + + impl std::ops::Shr for Valueless<$name> { + type Output = Valueless<$name>; + + fn shr(self, rhs: usize) -> Self::Output { + let width = self.ty().width().saturating_sub(rhs); + Valueless::new($name::new_dyn(if $SIGNED && width == 0 { + 1 + } else { + width + })) + } + } + impl sealed::BoolOrIntTypeSealed for $name {} impl BoolOrIntType for $name { @@ -843,6 +1062,12 @@ macro_rules! impl_int { } } + impl<'a, Width: Size> From<&'a $value> for BigInt { + fn from(v: &'a $value) -> BigInt { + v.to_bigint() + } + } + impl $value { pub fn width(&self) -> usize { if let Some(retval) = Width::KNOWN_VALUE { @@ -861,11 +1086,6 @@ macro_rules! impl_int { pub fn into_bits(self) -> Arc { self.bits } - pub fn ty(&self) -> $name { - $name { - width: Width::from_usize(self.width()), - } - } pub fn as_dyn_int(self) -> $value { $value { bits: self.bits, @@ -878,6 +1098,38 @@ macro_rules! impl_int { pub fn bits_mut(&mut self) -> &mut BitSlice { Arc::::make_mut(&mut self.bits) } + pub fn hdl_cmp(&self, other: &$value) -> std::cmp::Ordering { + self.to_bigint().cmp(&other.to_bigint()) + } + } + + impl ValueType for $value { + type Type = $name; + type ValueCategory = ValueCategoryValue; + + fn ty(&self) -> Self::Type { + $name { + width: Width::from_usize(self.width()), + } + } + } + + impl<'a, Width: Size> ToSimValueInner<'a> for $value { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this) + } + } + + impl<'a, Width: Size> ToSimValueInner<'a> for &'a $value { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Borrowed(&**this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Borrowed(this) + } } impl ToLiteralBits for $value { @@ -1015,11 +1267,58 @@ impl SInt { } } -macro_rules! impl_prim_int { +impl std::ops::Div>> + for Valueless> +{ + type Output = Self; + + fn div(self, _rhs: Valueless>) -> Self::Output { + self + } +} + +impl std::ops::Div>> + for Valueless> +{ + type Output = Valueless; + + fn div(self, _rhs: Valueless>) -> Self::Output { + Valueless::new(SInt::new_dyn( + self.ty().width().checked_add(1).expect("width too big"), + )) + } +} + +impl std::ops::Neg for Valueless> { + type Output = Valueless; + fn neg(self) -> Self::Output { + Valueless::new(SInt::new_dyn( + self.ty().width().checked_add(1).expect("width too big"), + )) + } +} + +impl std::ops::Neg for SIntValue { + type Output = SIntValue; + fn neg(self) -> Self::Output { + -&self + } +} + +impl std::ops::Neg for &'_ SIntValue { + type Output = SIntValue; + fn neg(self) -> Self::Output { + let ty = self.to_valueless().neg().ty(); + ty.from_bigint_wrapping(&-self.to_bigint()) + } +} + +macro_rules! impl_prim_int_to_expr { ( $(#[$meta:meta])* $prim_int:ident, $ty:ty ) => { + $(#[$meta])* impl From<$prim_int> for <$ty as BoolOrIntType>::Value { fn from(v: $prim_int) -> Self { <$ty>::le_bytes_to_value_wrapping( @@ -1028,15 +1327,32 @@ macro_rules! impl_prim_int { ) } } + $(#[$meta])* impl From> for <$ty as BoolOrIntType>::Value { fn from(v: NonZero<$prim_int>) -> Self { v.get().into() } } $(#[$meta])* - impl ToExpr for $prim_int { + impl<'a> ToSimValueInner<'a> for $prim_int { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this.into()) + } + } + $(#[$meta])* + impl ValueType for $prim_int { type Type = $ty; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + $(#[$meta])* + impl ToExpr for $prim_int { fn to_expr(&self) -> Expr { <$ty>::le_bytes_to_expr_wrapping( &self.to_le_bytes(), @@ -1045,9 +1361,25 @@ macro_rules! impl_prim_int { } } $(#[$meta])* - impl ToExpr for NonZero<$prim_int> { + impl<'a> ToSimValueInner<'a> for NonZero<$prim_int> { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this.into()) + } + } + $(#[$meta])* + impl ValueType for NonZero<$prim_int> { type Type = $ty; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + StaticType::TYPE + } + } + $(#[$meta])* + impl ToExpr for NonZero<$prim_int> { fn to_expr(&self) -> Expr { self.get().to_expr() } @@ -1055,28 +1387,208 @@ macro_rules! impl_prim_int { }; } -impl_prim_int!(u8, UInt<8>); -impl_prim_int!(u16, UInt<16>); -impl_prim_int!(u32, UInt<32>); -impl_prim_int!(u64, UInt<64>); -impl_prim_int!(u128, UInt<128>); -impl_prim_int!(i8, SInt<8>); -impl_prim_int!(i16, SInt<16>); -impl_prim_int!(i32, SInt<32>); -impl_prim_int!(i64, SInt<64>); -impl_prim_int!(i128, SInt<128>); +macro_rules! impl_for_all_prim_uints { + ( + #[size =, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize =, $($rest)*] + $m!() + ); + }; + ( + #[size = size, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = usize, $($rest)*] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero = $NonZero:ident] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = $($usize)?, NonZero = $NonZero, NonZero = $((NonZero<$usize>))?] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero =] + $m:ident!() + ) => { + impl_for_all_prim_uints!( + #[usize = $($usize)?, NonZero =, NonZero =] + $m!() + ); + }; + ( + #[usize = $($usize:ident)?, NonZero = $($NonZero:ident)?, NonZero = $(($($NonZeroUsize:tt)*))?] + $m:ident!() + ) => { + $m!(u8, UInt<8>); + $m!(u16, UInt<16>); + $m!(u32, UInt<32>); + $m!(u64, UInt<64>); + $m!(u128, UInt<128>); + $($m!($NonZero, UInt<8>);)? + $($m!($NonZero, UInt<16>);)? + $($m!($NonZero, UInt<32>);)? + $($m!($NonZero, UInt<64>);)? + $($m!($NonZero, UInt<128>);)? + $($m!( + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] + $usize, UInt<64> + );)? + $($m!( + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] + $($NonZeroUsize)*, UInt<64> + );)? + }; +} -impl_prim_int!( - /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] - usize, UInt<64> +macro_rules! impl_for_all_prim_sints { + ( + #[size =, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize =, $($rest)*] + $m!() + ); + }; + ( + #[size = size, $(rest:tt)*] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = isize, $($rest)*] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero = $NonZero:ident] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = $($isize)?, NonZero = $NonZero, NonZero = $((NonZero<$isize>))?] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero =] + $m:ident!() + ) => { + impl_for_all_prim_sints!( + #[isize = $($isize)?, NonZero =, NonZero =] + $m!() + ); + }; + ( + #[isize = $($isize:ident)?, NonZero = $($NonZero:ident)?, NonZero = $(($($NonZeroIsize:tt)*))?] + $m:ident!() + ) => { + $m!(i8, SInt<8>); + $m!(i16, SInt<16>); + $m!(i32, SInt<32>); + $m!(i64, SInt<64>); + $m!(i128, SInt<128>); + $($m!($NonZero, SInt<8>);)? + $($m!($NonZero, SInt<16>);)? + $($m!($NonZero, SInt<32>);)? + $($m!($NonZero, SInt<64>);)? + $($m!($NonZero, SInt<128>);)? + $($m!( + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] + $isize, SInt<64> + );)? + $($m!( + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] + $($NonZeroIsize)*, SInt<64> + );)? + }; +} + +impl_for_all_prim_uints!( + #[usize = usize, NonZero =] + impl_prim_int_to_expr!() ); -impl_prim_int!( - /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] - isize, SInt<64> +impl_for_all_prim_sints!( + #[isize = isize, NonZero =] + impl_prim_int_to_expr!() ); -pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { +macro_rules! impl_prim_int_from_value { + ($prim_int:ident, UInt<$width:literal>) => { + impl_prim_int_from_value!($prim_int, UInt<$width>, UIntValue>); + }; + ($prim_int:ident, SInt<$width:literal>) => { + impl_prim_int_from_value!($prim_int, SInt<$width>, SIntValue>); + }; + ($prim_int:ident, $ty:ty, $value:ty) => { + impl From<$value> for $prim_int { + fn from(value: $value) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a mut $value> for $prim_int { + fn from(value: &'a mut $value) -> Self { + value.as_int() + } + } + + impl From> for $prim_int { + fn from(value: SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a mut SimValue<$ty>> for $prim_int { + fn from(value: &'a mut SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a SimValue<$ty>> for $prim_int { + fn from(value: &'a SimValue<$ty>) -> Self { + value.as_int() + } + } + + impl<'a> From<&'a $value> for $prim_int { + fn from(value: &'a $value) -> Self { + value.as_int() + } + } + + impl $value { + pub fn as_int(&self) -> $prim_int { + let mut le_bytes = (0 as $prim_int).to_le_bytes(); + let bitslice = BitSlice::::from_slice_mut(&mut le_bytes); + bitslice.clone_from_bitslice(self.bits()); + $prim_int::from_le_bytes(le_bytes) + } + } + }; +} + +impl_for_all_prim_uints!( + #[usize =, NonZero =] + impl_prim_int_from_value!() +); + +impl_for_all_prim_sints!( + #[isize =, NonZero =] + impl_prim_int_from_value!() +); + +pub trait BoolOrIntType: + Type::Value> + sealed::BoolOrIntTypeSealed +{ type Width: Size; type Signed: GenericConstBool; type Value: Clone @@ -1301,12 +1813,80 @@ impl StaticType for Bool { const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } +impl From for UIntValue> { + fn from(value: bool) -> Self { + static VALUES: OnceLock<[UIntValue>; 2]> = OnceLock::new(); + let values = VALUES + .get_or_init(|| std::array::from_fn(|i| UInt::<1>::new_static().from_int_wrapping(i))); + values[value as usize].clone() + } +} + +impl From for SIntValue> { + fn from(value: bool) -> Self { + SIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + +impl From for UIntValue { + fn from(value: bool) -> Self { + UIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + +impl From for SIntValue { + fn from(value: bool) -> Self { + SIntValue::new(UIntValue::>::from(value).into_bits()) + } +} + impl ToLiteralBits for bool { fn to_literal_bits(&self) -> Result, NotALiteralExpr> { Ok(interned_bit(*self)) } } +impl<'a> ToSimValueInner<'a> for bool { + fn to_sim_value_inner(this: &Self) -> Cow<'_, ::SimValue> { + Cow::Owned(*this) + } + fn into_sim_value_inner(this: Self) -> Cow<'a, ::SimValue> { + Cow::Owned(this) + } +} + +impl std::ops::Not for Valueless { + type Output = Valueless; + + fn not(self) -> Self::Output { + self + } +} + +impl std::ops::BitAnd for Valueless { + type Output = Valueless; + + fn bitand(self, _rhs: Valueless) -> Self::Output { + self + } +} + +impl std::ops::BitOr for Valueless { + type Output = Valueless; + + fn bitor(self, _rhs: Valueless) -> Self::Output { + self + } +} + +impl std::ops::BitXor for Valueless { + type Output = Valueless; + + fn bitxor(self, _rhs: Valueless) -> Self::Output { + self + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index 4ed101e..acf2fec 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -4,13 +4,13 @@ use crate::{ bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, - ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, + CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl, + HdlPartialOrd, HdlPartialOrdImpl, }, - int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, + int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue}, intern::{Intern, InternSlice, Interned}, phantom_const::PhantomConst, - sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, + sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, @@ -22,7 +22,7 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, de::{Error, Visitor, value::UsizeDeserializer}, }; -use std::{fmt, marker::PhantomData, ops::Index}; +use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index}; const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; @@ -135,30 +135,57 @@ impl ToSimValueWithType for bool { } } -impl ExprCastTo for UIntInRangeMaskType { - fn cast_to(src: Expr, to_type: Bool) -> Expr { - src.cast_to_bits().cast_to(to_type) +impl CastToImpl for UIntInRangeMaskType { + type ValueOutput = bool; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + _to_type: Bool, + ) -> Self::ValueOutput { + *value + } + + fn cast_expr_to(value: Expr, to_type: Bool) -> Expr { + value.cast_to_bits().cast_to(to_type) } } -impl ExprCastTo for Bool { - fn cast_to(src: Expr, to_type: UIntInRangeMaskType) -> Expr { - src.cast_to_static::>().cast_bits_to(to_type) +impl CastToImpl for Bool { + type ValueOutput = SimValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: UIntInRangeMaskType, + ) -> Self::ValueOutput { + SimValue::from_value(to_type, *value) + } + + fn cast_expr_to(value: Expr, to_type: UIntInRangeMaskType) -> Expr { + value.cast_to_static::>().cast_bits_to(to_type) } } -impl ExprPartialEq for UIntInRangeMaskType { - fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { +impl HdlPartialEqImpl for UIntInRangeMaskType { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr) -> Expr { lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) } - fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { - lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) - } -} -impl SimValuePartialEq for UIntInRangeMaskType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) } } @@ -482,36 +509,64 @@ macro_rules! define_uint_in_range_type { } } - impl ExprCastTo> + impl CastToImpl> for $UIntInRangeType { - fn cast_to(src: Expr, to_type: UIntType) -> Expr> { - src.cast_to_bits().cast_to(to_type) + type ValueOutput = UIntValue; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: UIntType, + ) -> Self::ValueOutput { + value.cast_to(to_type) + } + + fn cast_expr_to(value: Expr, to_type: UIntType) -> Expr> { + value.cast_to_bits().cast_to(to_type) } } - impl ExprCastTo<$UIntInRangeType> + impl CastToImpl<$UIntInRangeType> for UIntType { - fn cast_to( - src: Expr, + type ValueOutput = SimValue<$UIntInRangeType>; + + fn cast_value_to( + _this: Self, + value: Cow<'_, Self::SimValue>, + to_type: $UIntInRangeType, + ) -> Self::ValueOutput { + value.cast_to(to_type.value).cast_bits_to(to_type) + } + + fn cast_expr_to( + value: Expr, to_type: $UIntInRangeType, ) -> Expr<$UIntInRangeType> { - src.cast_to(to_type.value).cast_bits_to(to_type) + value.cast_to(to_type.value).cast_bits_to(to_type) } } impl - ExprPartialEq<$UIntInRangeType> + HdlPartialEqImpl<$UIntInRangeType> for $UIntInRangeType { - fn cmp_eq( + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + fn cmp_expr_eq( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) } - fn cmp_ne( + fn cmp_expr_ne( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { @@ -520,28 +575,60 @@ macro_rules! define_uint_in_range_type { } impl - ExprPartialOrd<$UIntInRangeType> + HdlPartialOrdImpl<$UIntInRangeType> for $UIntInRangeType { - fn cmp_lt( + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::lt(&*lhs_value, &*rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::le(&*lhs_value, &*rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::gt(&*lhs_value, &*rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + PartialOrd::ge(&*lhs_value, &*rhs_value) + } + fn cmp_expr_lt( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) } - fn cmp_le( + fn cmp_expr_le( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) } - fn cmp_gt( + fn cmp_expr_gt( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) } - fn cmp_ge( + fn cmp_expr_ge( lhs: Expr, rhs: Expr<$UIntInRangeType>, ) -> Expr { @@ -549,70 +636,138 @@ macro_rules! define_uint_in_range_type { } } - impl - SimValuePartialEq<$UIntInRangeType> - for $UIntInRangeType - { - fn sim_value_eq( - this: &SimValue, - other: &SimValue<$UIntInRangeType>, - ) -> bool { - **this == **other - } - } - - impl ExprPartialEq> + impl HdlPartialEqImpl> for $UIntInRangeType { - fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialEq::cmp_eq(&*lhs_value, &*rhs_value) + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_eq(rhs) } - fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_ne(rhs) } } - impl ExprPartialEq<$UIntInRangeType> + impl HdlPartialEqImpl<$UIntInRangeType> for UIntType { - fn cmp_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialEq::cmp_eq(&*lhs_value, *rhs_value) + } + fn cmp_expr_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_eq(rhs.cast_to_bits()) } - fn cmp_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_ne(rhs.cast_to_bits()) } } - impl ExprPartialOrd> + impl HdlPartialOrdImpl> for $UIntInRangeType { - fn cmp_lt(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_lt(&*lhs_value, &*rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_le(&*lhs_value, &*rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_gt(&*lhs_value, &*rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: UIntType, + rhs_value: Cow<'_, UIntValue>, + ) -> bool { + HdlPartialOrd::cmp_ge(&*lhs_value, &*rhs_value) + } + fn cmp_expr_lt(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_lt(rhs) } - fn cmp_le(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_le(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_le(rhs) } - fn cmp_gt(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_gt(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_gt(rhs) } - fn cmp_ge(lhs: Expr, rhs: Expr>) -> Expr { + fn cmp_expr_ge(lhs: Expr, rhs: Expr>) -> Expr { lhs.cast_to_bits().cmp_ge(rhs) } } - impl ExprPartialOrd<$UIntInRangeType> + impl HdlPartialOrdImpl<$UIntInRangeType> for UIntType { - fn cmp_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_value_lt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_lt(&*lhs_value, *rhs_value) + } + fn cmp_value_le( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_le(&*lhs_value, *rhs_value) + } + fn cmp_value_gt( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_gt(&*lhs_value, *rhs_value) + } + fn cmp_value_ge( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: $UIntInRangeType, + rhs_value: Cow<'_, <$UIntInRangeType as Type>::SimValue>, + ) -> bool { + HdlPartialOrd::cmp_ge(&*lhs_value, *rhs_value) + } + fn cmp_expr_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_lt(rhs.cast_to_bits()) } - fn cmp_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_le(rhs.cast_to_bits()) } - fn cmp_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_gt(rhs.cast_to_bits()) } - fn cmp_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + fn cmp_expr_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { lhs.cmp_ge(rhs.cast_to_bits()) } } diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 46eb59b..83e7437 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -7,7 +7,10 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, clock::Clock, - expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat}, + expr::{ + Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat, + value_category::ValueCategoryExpr, + }, hdl, int::{Bool, DynSize, Size, UInt, UIntType}, intern::{Intern, Interned}, @@ -366,10 +369,16 @@ impl fmt::Debug for MemPort { } } -impl MemPort { - pub fn ty(&self) -> T::Port { +impl ValueType for MemPort { + type Type = T::Port; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> T::Port { T::port_ty(self) } +} + +impl MemPort { pub fn source_location(&self) -> SourceLocation { self.source_location } @@ -830,7 +839,7 @@ impl MemBuilder { depth: Option, initial_value: Expr, ) -> Interned { - let initial_value_ty = Expr::ty(initial_value); + let initial_value_ty = initial_value.ty(); assert_eq!( *mem_element_type, Element::from_canonical(initial_value_ty.element()), @@ -1011,7 +1020,7 @@ impl MemBuilder { target.depth, initial_value, )); - target.depth = Some(Expr::ty(initial_value).len()); + target.depth = Some(initial_value.ty().len()); } #[track_caller] pub fn initial_value_bit_slice(&mut self, initial_value: Interned) { diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 5ffeaae..9d1a0e7 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -8,12 +8,13 @@ use crate::{ clock::{Clock, ClockDomain}, enum_::{Enum, EnumMatchVariantsIter, EnumType}, expr::{ - Expr, Flow, ToExpr, + Expr, Flow, ToExpr, ValueType, ops::VariantAccess, target::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, + value_category::ValueCategoryExpr, }, formal::FormalKind, int::{Bool, DynSize, Size}, @@ -205,7 +206,7 @@ impl StmtConnect { source_location, } = *self; assert!( - Expr::ty(lhs).can_connect(Expr::ty(rhs)), + lhs.ty().can_connect(rhs.ty()), "can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}", ); assert!( @@ -219,14 +220,14 @@ impl StmtConnect { match Expr::flow(rhs) { Flow::Source | Flow::Duplex => {} Flow::Sink => assert!( - Expr::ty(rhs).is_passive(), + rhs.ty().is_passive(), "can't connect from sink with non-passive type\nat: {source_location}" ), } } #[track_caller] fn assert_validity(&self) { - self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs)); + self.assert_validity_with_original_types(self.lhs.ty(), self.rhs.ty()); } } @@ -331,7 +332,7 @@ impl Copy for StmtMatch {} impl StmtMatch { #[track_caller] fn assert_validity(&self) { - assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len()); + assert_eq!(self.expr.ty().variants().len(), self.blocks.len()); } } @@ -765,6 +766,15 @@ impl fmt::Debug for Instance { } } +impl ValueType for Instance { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> T { + self.instantiated.io_ty() + } +} + impl Instance { pub fn canonical(self) -> Instance { let Self { @@ -828,9 +838,6 @@ impl Instance { pub fn must_connect_to(&self) -> bool { true } - pub fn ty(&self) -> T { - self.instantiated.io_ty() - } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -2028,7 +2035,7 @@ impl RegBuilder { init: _, ty: _, } = self; - let ty = Expr::ty(init); + let ty = init.ty(); RegBuilder { name, source_location, @@ -2597,7 +2604,7 @@ pub fn enum_match_variants_helper( ModuleBuilder::with(|m| { let mut impl_ = m.impl_.borrow_mut(); let body = impl_.body.builder_normal_body(); - let enum_ty = Expr::ty(base); + let enum_ty = base.ty(); let outer_block: BlockId = m.block_stack.top(); let blocks = Interned::from_iter(enum_ty.variants().iter().map(|_| body.new_block())); body.block(outer_block).stmts.push( @@ -2649,7 +2656,7 @@ pub fn connect_any_with_loc( rhs, source_location, }; - connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig)); + connect.assert_validity_with_original_types(lhs_orig.ty(), rhs_orig.ty()); ModuleBuilder::with(|m| { m.impl_ .borrow_mut() @@ -2764,7 +2771,7 @@ pub fn memory_with_init_and_loc( source_location: SourceLocation, ) -> MemBuilder { let initial_value = initial_value.to_expr(); - let mut retval = memory_array_with_loc(name, Expr::ty(initial_value), source_location); + let mut retval = memory_array_with_loc(name, initial_value.ty(), source_location); retval.initial_value(initial_value); retval } @@ -2807,6 +2814,15 @@ pub struct ModuleIO { source_location: SourceLocation, } +impl ValueType for ModuleIO { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + impl fmt::Debug for ModuleIO { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ModuleIO") @@ -2908,9 +2924,6 @@ impl ModuleIO { unreachable!() } } - pub fn ty(&self) -> T { - self.ty - } } #[derive(PartialEq, Eq, Hash, Clone, Copy)] diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index bd4e939..61167fd 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -6,7 +6,7 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ - ExprEnum, + ExprEnum, ValueType, ops::{self, ArrayLiteral}, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, @@ -521,7 +521,7 @@ impl State { Entry::Vacant(entry) => ( *entry.insert(Resets::with_new_nodes( &mut self.reset_graph, - Expr::ty(expr), + expr.ty(), source_location, )), true, @@ -1020,7 +1020,7 @@ fn cast_bit_op( } macro_rules! match_arg_ty { ($($Variant:ident),*) => { - *match Expr::ty(arg) { + *match arg.ty() { CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) @@ -1660,7 +1660,8 @@ impl RunPassDispatch for AnyReg { let clock_domain = Expr::::from_canonical( Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0, ); - match Expr::ty(clock_domain) + match clock_domain + .ty() .field_by_name("rst".intern()) .expect("ClockDomain has rst field") .ty diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index bd5f7d5..8902921 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -5,7 +5,7 @@ use crate::{ bundle::{Bundle, BundleField, BundleType}, enum_::{Enum, EnumType, EnumVariant}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, + CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, ValueType, ops::{self, EnumLiteral}, }, hdl, @@ -528,7 +528,7 @@ fn connect_port( rhs: Expr, source_location: SourceLocation, ) { - if Expr::ty(lhs) == Expr::ty(rhs) { + if lhs.ty() == rhs.ty() { stmts.push( StmtConnect { lhs, @@ -539,7 +539,7 @@ fn connect_port( ); return; } - match (Expr::ty(lhs), Expr::ty(rhs)) { + match (lhs.ty(), rhs.ty()) { (CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => { let lhs = Expr::::from_canonical(lhs); for field in lhs_type.fields() { @@ -586,8 +586,8 @@ fn connect_port( | (CanonicalType::PhantomConst(_), _) | (CanonicalType::DynSimOnly(_), _) => unreachable!( "trying to connect memory ports:\n{:?}\n{:?}", - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), ), } } @@ -607,14 +607,17 @@ fn match_int_tag( }; }; let mut retval = StmtIf { - cond: int_tag_expr - .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)), + cond: int_tag_expr.cmp_eq( + int_tag_expr + .ty() + .from_int_wrapping(next_to_last_variant_index), + ), source_location, blocks: [next_to_last_block, last_block], }; for (variant_index, block) in blocks_iter.rev() { retval = StmtIf { - cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)), + cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)), source_location, blocks: [ block, @@ -657,7 +660,7 @@ impl Folder for State { ExprEnum::VariantAccess(op) => { let folded_base_expr = Expr::canonical(op.base()).fold(self)?; Ok(*Expr::expr_enum(self.handle_variant_access( - Expr::ty(op.base()), + op.base().ty(), folded_base_expr, op.variant_index(), )?)) @@ -867,7 +870,7 @@ impl Folder for State { }) => { let folded_expr = Expr::canonical(expr).fold(self)?; let folded_blocks = blocks.fold(self)?; - self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks) + self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks) } Stmt::Connect(StmtConnect { lhs, @@ -878,8 +881,8 @@ impl Folder for State { let folded_rhs = rhs.fold(self)?; let mut output_stmts = vec![]; self.handle_stmt_connect( - Expr::ty(lhs), - Expr::ty(rhs), + lhs.ty(), + rhs.ty(), folded_lhs, folded_rhs, source_location, diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 35f186d..d741836 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -4,7 +4,7 @@ use crate::{ annotations::TargetedAnnotation, array::Array, bundle::{Bundle, BundleType}, - expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr}, + expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType}, int::{Bool, SInt, Size, UInt}, intern::{Intern, Interned}, memory::{Mem, MemPort, PortType}, @@ -530,7 +530,7 @@ impl ModuleState { connect_read( output_stmts, wire_read, - Expr::::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)), + Expr::::from_canonical(port_read).cast_bits_to(wire_read.ty()), ); }; let connect_write_enum = diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 2c33a76..2869a49 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -11,7 +11,7 @@ use crate::{ clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ - Expr, ExprEnum, ops, + Expr, ExprEnum, ValueType, ops, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index eb6a758..32e9d6b 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -2,13 +2,10 @@ // See Notices.txt for copyright information use crate::{ - expr::{ - Expr, ToExpr, - ops::{ExprPartialEq, ExprPartialOrd}, - }, + expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType}, int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + sim::value::{SimValue, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, @@ -22,6 +19,7 @@ use serde::{ }; use std::{ any::Any, + borrow::Cow, fmt, hash::{Hash, Hasher}, marker::PhantomData, @@ -372,50 +370,102 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst { } } -impl ExprPartialEq for PhantomConst { - fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - true.to_expr() - } - - fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - false.to_expr() - } -} - -impl ExprPartialOrd for PhantomConst { - fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - false.to_expr() - } - - fn cmp_le(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - true.to_expr() - } - - fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - false.to_expr() - } - - fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); - true.to_expr() - } -} - -impl SimValuePartialEq for PhantomConst { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - assert_eq!(SimValue::ty(this), SimValue::ty(other)); +impl HdlPartialEqImpl for PhantomConst { + #[track_caller] + fn cmp_value_eq( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); true } + + #[track_caller] + fn cmp_expr_eq(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } + + #[track_caller] + fn cmp_expr_ne(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } +} + +impl HdlPartialOrdImpl for PhantomConst { + #[track_caller] + fn cmp_value_lt( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + false + } + + #[track_caller] + fn cmp_value_le( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + true + } + + #[track_caller] + fn cmp_value_gt( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + false + } + + #[track_caller] + fn cmp_value_ge( + lhs: Self, + _lhs_value: Cow<'_, Self::SimValue>, + rhs: Self, + _rhs_value: Cow<'_, ::SimValue>, + ) -> bool { + assert_eq!(lhs, rhs); + true + } + + #[track_caller] + fn cmp_expr_lt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } + + #[track_caller] + fn cmp_expr_le(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } + + #[track_caller] + fn cmp_expr_gt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + false.to_expr() + } + + #[track_caller] + fn cmp_expr_ge(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(lhs.ty(), rhs.ty()); + true.to_expr() + } } impl ToSimValue for PhantomConst { - type Type = PhantomConst; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(*self, *self) } @@ -479,7 +529,7 @@ impl_phantom_const_get! { impl_phantom_const_get! { impl PhantomConstGet for Expr> { fn get(&self) -> _ { - PhantomConst::get(Expr::ty(*self)) + PhantomConst::get(self.ty()) } } } diff --git a/crates/fayalite/src/platform.rs b/crates/fayalite/src/platform.rs index 194aa6e..c6901cf 100644 --- a/crates/fayalite/src/platform.rs +++ b/crates/fayalite/src/platform.rs @@ -3,7 +3,7 @@ use crate::{ bundle::{Bundle, BundleField, BundleType}, - expr::{Expr, ExprEnum}, + expr::{Expr, ExprEnum, ValueType}, intern::{Intern, Interned}, module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc}, source_location::SourceLocation, @@ -615,8 +615,8 @@ impl Peripheral { }; drop(state); let Self { ty, common } = self; - let instance_io_field = Expr::field(output, &common.id.name); - assert_eq!(ty, Expr::ty(instance_io_field)); + let instance_io_field = Expr::field::(output, &common.id.name); + assert_eq!(ty, instance_io_field.ty()); Ok(UsedPeripheral { instance_io_field, common, @@ -654,7 +654,7 @@ impl UsedPeripheral { ref common, } = *self; PeripheralRef { - ty: Expr::ty(instance_io_field), + ty: instance_io_field.ty(), common, } } @@ -915,7 +915,7 @@ impl<'a, T: Type> PeripheralRef<'a, T> { main_module_io_fields.push(BundleField { name: self.name(), flipped: self.is_input(), - ty: Expr::ty(canonical_wire), + ty: canonical_wire.ty(), }); on_use_function(&mut **shared_state, self.canonical(), canonical_wire); drop(on_use_state); diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 4cc173e..4c5bfdf 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -13,7 +13,7 @@ pub use crate::{ enum_::{Enum, HdlNone, HdlOption, HdlSome}, expr::{ CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, - ReduceBits, ToExpr, repeat, + ReduceBits, ToExpr, ValueType, repeat, }, formal::{ MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, diff --git a/crates/fayalite/src/reg.rs b/crates/fayalite/src/reg.rs index 20e0b94..7f50655 100644 --- a/crates/fayalite/src/reg.rs +++ b/crates/fayalite/src/reg.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use crate::{ clock::ClockDomain, - expr::{Expr, Flow}, + expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr}, intern::Interned, module::{NameId, ScopedNameId}, reset::{Reset, ResetType}, @@ -20,7 +20,16 @@ pub struct Reg { init: Option>, } -impl fmt::Debug for Reg { +impl ValueType for Reg { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl fmt::Debug for Reg { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { name, @@ -68,7 +77,7 @@ impl Reg { "register type must be a storable type" ); if let Some(init) = init { - assert_eq!(ty, Expr::ty(init), "register's type must match init type"); + assert_eq!(ty, init.ty(), "register's type must match init type"); } Self { name: scoped_name, @@ -78,9 +87,6 @@ impl Reg { init, } } - pub fn ty(&self) -> T { - self.ty - } pub fn source_location(&self) -> SourceLocation { self.source_location } diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 5dff278..13273ac 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -2,13 +2,15 @@ // See Notices.txt for copyright information use crate::{ clock::Clock, - expr::{Expr, ToExpr, ops}, - int::{Bool, SInt, UInt}, + expr::{CastToImpl, Expr, ValueType}, + int::{Bool, SInt, SIntValue, UInt, UIntValue}, + sim::value::SimValue, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, }, + util::ConstUsize, }; use bitvec::{bits, order::Lsb0}; @@ -19,15 +21,15 @@ mod sealed { pub trait ResetType: StaticType + sealed::ResetTypeSealed - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo - + ops::ExprCastTo> - + ops::ExprCastTo> - + ops::ExprCastTo - + ops::ExprCastTo + + CastToImpl + + CastToImpl> + + CastToImpl> + + CastToImpl> + + CastToImpl> + + CastToImpl, ValueOutput = UIntValue>> + + CastToImpl, ValueOutput = SIntValue>> + + CastToImpl + + CastToImpl { fn dispatch(input: D::Input, dispatch: D) -> D::Output; } @@ -132,29 +134,34 @@ macro_rules! reset_type { } pub trait $Trait { - fn $trait_fn(&self) -> Expr<$name>; + type Output: ValueType; + fn $trait_fn(&self) -> Self::Output; } impl $Trait for &'_ T { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } impl $Trait for &'_ mut T { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } impl $Trait for Box { - fn $trait_fn(&self) -> Expr<$name> { + type Output = T::Output; + fn $trait_fn(&self) -> Self::Output { (**self).$trait_fn() } } $($impl_trait $Trait for Expr<$name> { - fn $trait_fn(&self) -> Expr<$name> { + type Output = Expr<$name>; + fn $trait_fn(&self) -> Self::Output { *self } })? @@ -171,13 +178,15 @@ reset_type!( ); impl ToSyncReset for bool { - fn to_sync_reset(&self) -> Expr { - self.to_expr().to_sync_reset() + type Output = SimValue; + fn to_sync_reset(&self) -> Self::Output { + SimValue::from_value(SyncReset, *self) } } impl ToAsyncReset for bool { - fn to_async_reset(&self) -> Expr { - self.to_expr().to_async_reset() + type Output = SimValue; + fn to_async_reset(&self) -> Self::Output { + SimValue::from_value(AsyncReset, *self) } } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 808ead4..7068e44 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6,7 +6,7 @@ use crate::{ bundle::{BundleField, BundleType}, expr::{ - Flow, ToLiteralBits, + Flow, target::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, @@ -47,7 +47,6 @@ use num_bigint::BigInt; use num_traits::{Signed, Zero}; use std::{ any::Any, - borrow::Cow, cell::{Cell, RefCell}, collections::{BTreeMap, BTreeSet}, fmt, @@ -1504,9 +1503,10 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo fn call(self, state: &mut interpreter::State) -> Self::Output { let Self { compiled_value, io } = self; match compiled_value.range.len().as_single() { - Some(TypeLenSingle::SmallSlot) => Expr::ty(io) + Some(TypeLenSingle::SmallSlot) => io + .ty() .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), - Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping( + Some(TypeLenSingle::BigSlot) => io.ty().value_from_int_wrapping( state.big_slots[compiled_value.range.big_slots.start].clone(), ), Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), @@ -2889,7 +2889,7 @@ impl SimulationImpl { }, ); } - let size = Expr::ty(io).size(); + let size = io.ty().size(); let mut opaque = OpaqueSimValue::with_capacity(size); opaque.rewrite_with(size, |mut writer| { SimulationImpl::read_write_sim_value_helper( @@ -2922,7 +2922,7 @@ impl SimulationImpl { ); writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) }); - SimValue::from_opaque(Expr::ty(io), opaque) + SimValue::from_opaque(io.ty(), opaque) } /// doesn't modify `opaque` fn value_changed( @@ -3006,7 +3006,7 @@ impl SimulationImpl { .get_module_mut(which_module) .write_helper(io, which_module); self.event_queue.add_event_for_now(EventKind::State); - assert_eq!(Expr::ty(io), SimValue::ty(value)); + assert_eq!(io.ty(), value.ty()); Self::read_write_sim_value_helper( &mut self.state, compiled_value, @@ -3269,16 +3269,12 @@ macro_rules! impl_simulation_methods { pub $($async)? fn write_bool_or_int( &mut $self, io: Expr, - value: impl ToExpr, + value: impl ToSimValueWithType, ) { - let value = value.to_expr(); - assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); - let value = value - .to_literal_bits() - .expect("the value that is being written to an input must be a literal"); + let value = value.to_sim_value_with_type(io.ty()); $self.sim_impl.borrow_mut().write_bool_or_int( io, - I::bits_to_value(Cow::Borrowed(&value)), + SimValue::into_value(value), $which_module, ); } @@ -3343,7 +3339,7 @@ macro_rules! impl_simulation_methods { pub $($async)? fn write>(&mut $self, io: Expr, value: V) { $self.sim_impl.borrow_mut().write( Expr::canonical(io), - &SimValue::into_canonical(value.into_sim_value_with_type(Expr::ty(io))), + &SimValue::into_canonical(value.into_sim_value_with_type(io.ty())), $which_module, ); } diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs index 7a0ac0a..07621c5 100644 --- a/crates/fayalite/src/sim/compiler.rs +++ b/crates/fayalite/src/sim/compiler.rs @@ -7,7 +7,7 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ - ExprEnum, Flow, ops, + ExprEnum, Flow, ValueType, ops, target::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, @@ -1714,7 +1714,7 @@ impl MakeTraceDeclTarget { } fn ty(self) -> CanonicalType { match self { - MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr), + MakeTraceDeclTarget::Expr(expr) => expr.ty(), MakeTraceDeclTarget::Memory { ty, .. } => ty, } } @@ -2771,7 +2771,7 @@ impl Compiler { let retval = parts .into_iter() .map(|part| part.cast_to_bits()) - .reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width)) + .reduce(|accumulator, part| accumulator | (part << accumulator.ty().width)) .unwrap_or_else(|| UInt[0].zero().to_expr()); let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); let retval = self @@ -2783,7 +2783,7 @@ impl Compiler { instantiated_module: InstantiatedModule, expr: ops::CastToBits, ) -> CompiledValue { - match Expr::ty(expr.arg()) { + match expr.arg().ty() { CanonicalType::UInt(_) => { self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg) } @@ -2949,7 +2949,7 @@ impl Compiler { return retval; } let mut cast_bit = |arg: Expr| { - let src_signed = match Expr::ty(arg) { + let src_signed = match arg.ty() { CanonicalType::UInt(_) => false, CanonicalType::SInt(_) => true, CanonicalType::Bool(_) => false, @@ -2963,7 +2963,7 @@ impl Compiler { CanonicalType::PhantomConst(_) => unreachable!(), CanonicalType::DynSimOnly(_) => unreachable!(), }; - let dest_signed = match Expr::ty(expr) { + let dest_signed = match expr.ty() { CanonicalType::UInt(_) => false, CanonicalType::SInt(_) => true, CanonicalType::Bool(_) => false, @@ -2977,22 +2977,23 @@ impl Compiler { CanonicalType::PhantomConst(_) => unreachable!(), CanonicalType::DynSimOnly(_) => unreachable!(), }; - self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { - match (src_signed, dest_signed) { - (false, false) | (true, true) => { - vec![Insn::Copy { dest, src }] - } - (false, true) => vec![Insn::CastToSInt { - dest, - src, - dest_width: 1, - }], - (true, false) => vec![Insn::CastToUInt { - dest, - src, - dest_width: 1, - }], + self.simple_nary_big_expr(instantiated_module, expr.ty(), [arg], |dest, [src]| match ( + src_signed, + dest_signed, + ) { + (false, false) | (true, true) => { + vec![Insn::Copy { dest, src }] } + (false, true) => vec![Insn::CastToSInt { + dest, + src, + dest_width: 1, + }], + (true, false) => vec![Insn::CastToUInt { + dest, + src, + dest_width: 1, + }], }) .into() }; @@ -3032,21 +3033,13 @@ impl Compiler { }) .into(), ExprEnum::PhantomConst(_) => self - .compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default()) + .compile_aggregate_literal(instantiated_module, expr.ty(), Interned::default()) .into(), ExprEnum::BundleLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.field_values(), - ) + .compile_aggregate_literal(instantiated_module, expr.ty(), literal.field_values()) .into(), ExprEnum::ArrayLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.element_values(), - ) + .compile_aggregate_literal(instantiated_module, expr.ty(), literal.element_values()) .into(), ExprEnum::EnumLiteral(expr) => { let enum_bits_ty = UInt[expr.ty().type_properties().bit_width]; @@ -3074,13 +3067,13 @@ impl Compiler { ExprEnum::NotU(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { vec![Insn::NotU { dest, src, - width: Expr::ty(expr.arg()).width(), + width: expr.arg().ty().width(), }] }, ) @@ -3088,7 +3081,7 @@ impl Compiler { ExprEnum::NotS(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::NotS { dest, src }], ) @@ -3096,7 +3089,7 @@ impl Compiler { ExprEnum::NotB(expr) => self .simple_nary_big_expr( instantiated_module, - Expr::ty(expr.arg()).canonical(), + expr.arg().ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { vec![Insn::NotU { @@ -3600,12 +3593,12 @@ impl Compiler { .map_ty(Bundle::from_canonical) .field_by_index(expr.field_index()), ExprEnum::VariantAccess(variant_access) => { - let start = Expr::ty(variant_access.base()).discriminant_bit_width(); - let len = Expr::ty(expr).bit_width(); + let start = variant_access.base().ty().discriminant_bit_width(); + let len = expr.ty().bit_width(); self.compile_expr( instantiated_module, variant_access.base().cast_to_bits()[start..start + len] - .cast_bits_to(Expr::ty(expr)), + .cast_bits_to(expr.ty()), ) } ExprEnum::ArrayIndex(expr) => self @@ -3627,45 +3620,39 @@ impl Compiler { .map_ty(Array::from_canonical) .element_dyn(index_slot) } - ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitAndU(expr) => if expr.arg().ty().width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) } else { self.compile_expr( instantiated_module, - Expr::canonical( - expr.arg() - .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), - ), + Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))), ) } .into(), - ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitAndS(expr) => if expr.arg().ty().width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) } else { self.compile_expr( instantiated_module, - Expr::canonical( - expr.arg() - .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), - ), + Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))), ) } .into(), - ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitOrU(expr) => if expr.arg().ty().width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) } else { self.compile_expr( instantiated_module, - Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))), ) } .into(), - ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { + ExprEnum::ReduceBitOrS(expr) => if expr.arg().ty().width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) } else { self.compile_expr( instantiated_module, - Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))), ) } .into(), @@ -3678,7 +3665,7 @@ impl Compiler { vec![Insn::ReduceBitXor { dest, src, - input_width: Expr::ty(expr.arg()).width(), + input_width: expr.arg().ty().width(), }] }, ) @@ -3692,7 +3679,7 @@ impl Compiler { vec![Insn::ReduceBitXor { dest, src, - input_width: Expr::ty(expr.arg()).width(), + input_width: expr.arg().ty().width(), }] }, ) @@ -3790,8 +3777,8 @@ impl Compiler { mut rhs: Expr, source_location: SourceLocation, ) { - if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { - match Expr::ty(lhs) { + if lhs.ty() != rhs.ty() || !lhs.ty().is_passive() { + match lhs.ty() { CanonicalType::UInt(lhs_ty) => { rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); } @@ -3800,7 +3787,7 @@ impl Compiler { } CanonicalType::Bool(_) => unreachable!(), CanonicalType::Array(lhs_ty) => { - let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Array(rhs_ty) = rhs.ty() else { unreachable!(); }; assert_eq!(lhs_ty.len(), rhs_ty.len()); @@ -3820,13 +3807,13 @@ impl Compiler { return; } CanonicalType::Enum(lhs_ty) => { - let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Enum(rhs_ty) = rhs.ty() else { unreachable!(); }; todo!("handle connect with different enum types"); } CanonicalType::Bundle(lhs_ty) => { - let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { + let CanonicalType::Bundle(rhs_ty) = rhs.ty() else { unreachable!(); }; assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 391172e..2b121b5 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ + expr::ValueType, int::{BoolOrIntType, SInt, UInt}, intern::{Intern, Interned, Memoize}, sim::interpreter::parts::{ diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index ff836d5..def2ae3 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -6,7 +6,10 @@ use crate::{ bundle::{Bundle, BundleType}, clock::Clock, enum_::{Enum, EnumType}, - expr::{CastBitsTo, Expr, ToExpr}, + expr::{ + CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType, + value_category::{ValueCategorySimValue, ValueCategoryValue}, + }, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -26,6 +29,7 @@ use std::{ borrow::{Borrow, BorrowMut, Cow}, fmt::{self, Write}, hash::{BuildHasher, Hash, Hasher, RandomState}, + num::NonZero, ops::{Deref, DerefMut, Index, IndexMut}, sync::{Arc, Mutex}, }; @@ -136,7 +140,7 @@ impl + Serialize> Serialize for SimValue { S: Serializer, { SerdeSimValue { - ty: SimValue::ty(self), + ty: self.ty(), value: std::borrow::Cow::Borrowed(&*self), } .serialize(serializer) @@ -157,6 +161,15 @@ pub struct SimValue { inner: AlternatingCell>, } +impl ValueType for SimValue { + type Type = T; + type ValueCategory = ValueCategorySimValue; + + fn ty(&self) -> Self::Type { + self.inner.share().ty + } +} + impl> fmt::Display for SimValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { T::SimValue::fmt(self, f) @@ -218,9 +231,6 @@ impl SimValue { inner: AlternatingCell::new_unique(inner), } } - pub fn ty(this: &Self) -> T { - this.inner.share().ty - } pub fn into_opaque(this: Self) -> OpaqueSimValue { this.inner.into_inner().into_opaque() } @@ -259,7 +269,7 @@ impl SimValue { SimValue::from_opaque(ty.canonical(), opaque) } pub fn canonical(this: &Self) -> SimValue { - SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone()) + SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone()) } #[track_caller] pub fn from_dyn_int(v: SimValue) -> Self @@ -280,7 +290,7 @@ impl SimValue { where T: IntType, { - SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone()) + SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone()) } #[track_caller] pub fn from_bundle(v: SimValue) -> Self @@ -302,7 +312,7 @@ impl SimValue { T: BundleType, { SimValue::from_opaque( - Bundle::from_canonical(Self::ty(this).canonical()), + Bundle::from_canonical(this.ty().canonical()), Self::opaque(&this).clone(), ) } @@ -326,7 +336,7 @@ impl SimValue { T: EnumType, { SimValue::from_opaque( - Enum::from_canonical(Self::ty(this).canonical()), + Enum::from_canonical(this.ty().canonical()), Self::opaque(&this).clone(), ) } @@ -357,8 +367,6 @@ impl fmt::Debug for SimValue { } impl ToExpr for SimValue { - type Type = T; - #[track_caller] fn to_expr(&self) -> Expr { let inner = self.inner.share(); @@ -366,7 +374,7 @@ impl ToExpr for SimValue { inner.sim_only_values_len, 0, "can't convert sim-only values to Expr" ); - inner.opaque.bits().cast_bits_to(inner.ty) + inner.opaque.bits().to_expr().cast_bits_to(inner.ty) } } @@ -438,59 +446,112 @@ impl AsMut<[SimValue]> for SimValue> { } } -pub trait SimValuePartialEq: Type { - #[track_caller] - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool; -} - -impl, U: Type> PartialEq> for SimValue { +impl PartialEq> for SimValue +where + Self: for<'a> HdlPartialEq<&'a SimValue, Output = SimValue>, +{ #[track_caller] fn eq(&self, other: &SimValue) -> bool { - T::sim_value_eq(self, other) + *self.cmp_eq(other) + } + #[track_caller] + fn ne(&self, other: &SimValue) -> bool { + *self.cmp_ne(other) } } -impl SimValuePartialEq for UIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueEq: Type +where + for<'a> SimValue: HdlPartialEq<&'a SimValue, Output = SimValue>, +{ +} + +impl Eq for SimValue where + for<'a> SimValue: HdlPartialEq<&'a SimValue, Output = SimValue> +{ +} + +impl PartialOrd> for SimValue +where + Self: for<'a> HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ + #[track_caller] + fn partial_cmp(&self, other: &SimValue) -> Option { + if *self.cmp_eq(other) { + Some(std::cmp::Ordering::Equal) + } else if *self.cmp_lt(other) { + Some(std::cmp::Ordering::Less) + } else if *self.cmp_gt(other) { + Some(std::cmp::Ordering::Greater) + } else { + None + } + } + + #[track_caller] + fn lt(&self, other: &SimValue) -> bool { + *self.cmp_lt(other) + } + + #[track_caller] + fn le(&self, other: &SimValue) -> bool { + *self.cmp_le(other) + } + + #[track_caller] + fn gt(&self, other: &SimValue) -> bool { + *self.cmp_gt(other) + } + + #[track_caller] + fn ge(&self, other: &SimValue) -> bool { + *self.cmp_ge(other) } } -impl SimValuePartialEq for SIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueOrd: SimValueEq +where + for<'a> SimValue: HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ +} + +impl Ord for SimValue +where + for<'a> SimValue: HdlPartialOrd<&'a SimValue, Output = SimValue>, +{ + #[track_caller] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + if *self.cmp_eq(other) { + std::cmp::Ordering::Equal + } else if *self.cmp_lt(other) { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } } } -impl SimValuePartialEq for Bool { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other - } +impl SimValueEq for UIntType {} + +impl SimValueOrd for UIntType {} + +impl SimValueEq for SIntType {} + +impl SimValueOrd for SIntType {} + +macro_rules! impl_sim_value_cmp_as_bool { + ($ty:ident) => { + impl SimValueEq for $ty {} + + impl SimValueOrd for $ty {} + }; } -impl SimValuePartialEq for Clock { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other - } -} - -impl SimValuePartialEq for Reset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other - } -} - -impl SimValuePartialEq for SyncReset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other - } -} - -impl SimValuePartialEq for AsyncReset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other - } -} +impl_sim_value_cmp_as_bool!(Bool); +impl_sim_value_cmp_as_bool!(Clock); +impl_sim_value_cmp_as_bool!(Reset); +impl_sim_value_cmp_as_bool!(SyncReset); +impl_sim_value_cmp_as_bool!(AsyncReset); #[doc(hidden)] pub mod match_sim_value { @@ -603,9 +664,7 @@ pub mod match_sim_value { } } -pub trait ToSimValue: ToSimValueWithType<::Type> { - type Type: Type; - +pub trait ToSimValue: ToSimValueWithType<::Type> + ValueType { #[track_caller] fn to_sim_value(&self) -> SimValue; #[track_caller] @@ -647,31 +706,31 @@ pub trait ToSimValueWithType { macro_rules! forward_to_sim_value_with_type { ([$($generics:tt)*] $ty:ty) => { - impl<$($generics)*> ToSimValueWithType<::Type> for $ty { - fn to_sim_value_with_type(&self, ty: ::Type) -> SimValue<::Type> { + impl<$($generics)*> ToSimValueWithType<::Type> for $ty { + fn to_sim_value_with_type(&self, ty: ::Type) -> SimValue<::Type> { let retval = Self::to_sim_value(self); - assert_eq!(SimValue::ty(&retval), ty); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn into_sim_value_with_type(self, ty: ::Type) -> SimValue<::Type> + fn into_sim_value_with_type(self, ty: ::Type) -> SimValue<::Type> where Self: Sized, { let retval = Self::into_sim_value(self); - assert_eq!(SimValue::ty(&retval), ty); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn arc_into_sim_value_with_type(self: Arc, ty: ::Type) -> SimValue<::Type> { + fn arc_into_sim_value_with_type(self: Arc, ty: ::Type) -> SimValue<::Type> { let retval = Self::arc_into_sim_value(self); - assert_eq!(SimValue::ty(&retval), ty); + assert_eq!(retval.ty(), ty); retval } #[track_caller] - fn arc_to_sim_value_with_type(self: &Arc, ty: ::Type) -> SimValue<::Type> { + fn arc_to_sim_value_with_type(self: &Arc, ty: ::Type) -> SimValue<::Type> { let retval = Self::arc_to_sim_value(self); - assert_eq!(SimValue::ty(&retval), ty); + assert_eq!(retval.ty(), ty); retval } } @@ -679,7 +738,6 @@ macro_rules! forward_to_sim_value_with_type { } impl ToSimValue for SimValue { - type Type = T; fn to_sim_value(&self) -> SimValue { self.clone() } @@ -736,8 +794,6 @@ impl ToSimValueWithType for BitSlice { } impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -750,8 +806,6 @@ impl, T: Type> ToSimValueWithType for &' } impl ToSimValue for &'_ mut This { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -764,8 +818,6 @@ impl, T: Type> ToSimValueWithType for &' } impl ToSimValue for Arc { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::arc_to_sim_value(self) } @@ -786,7 +838,6 @@ impl, T: Type> ToSimValueWithType for Ar impl ToSimValue for crate::intern::Interned { - type Type = This::Type; fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -801,8 +852,6 @@ impl + Send + Sync + 'static, T: Type> ToSi } impl ToSimValue for Box { - type Type = This::Type; - fn to_sim_value(&self) -> SimValue { This::to_sim_value(self) } @@ -845,8 +894,6 @@ impl, T: Type> ToSimValueWithType> for [ } impl> ToSimValue for [Element] { - type Type = Array; - #[track_caller] fn to_sim_value(&self) -> SimValue { SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) @@ -882,8 +929,6 @@ impl, const N: usize> ToSimValue for [Elem where ConstUsize: KnownSize, { - type Type = Array; - fn to_sim_value(&self) -> SimValue { SimValue::from_array_elements(StaticType::TYPE, self) } @@ -937,8 +982,6 @@ impl, T: Type> ToSimValueWithType> for V } impl> ToSimValue for Vec { - type Type = Array; - fn to_sim_value(&self) -> SimValue { SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) } @@ -979,8 +1022,6 @@ impl, T: Type> ToSimValueWithType> for B } impl> ToSimValue for Box<[Element]> { - type Type = Array; - fn to_sim_value(&self) -> SimValue { SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) } @@ -1010,11 +1051,10 @@ impl> ToSimValueWithType ToSimValue for Expr { - type Type = T; #[track_caller] fn to_sim_value(&self) -> SimValue { SimValue::from_bitslice( - Expr::ty(*self), + self.ty(), &crate::expr::ToLiteralBits::to_literal_bits(self) .expect("must be a literal expression"), ) @@ -1034,8 +1074,6 @@ macro_rules! impl_to_sim_value_for_bool_like { } impl ToSimValue for bool { - type Type = Bool; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(Bool, *self) } @@ -1073,10 +1111,8 @@ impl ToSimValueWithType for bool { } macro_rules! impl_to_sim_value_for_primitive_int { - ($prim:ident) => { + ($prim:ty) => { impl ToSimValue for $prim { - type Type = <$prim as ToExpr>::Type; - #[track_caller] fn to_sim_value( &self, @@ -1087,15 +1123,15 @@ macro_rules! impl_to_sim_value_for_primitive_int { forward_to_sim_value_with_type!([] $prim); - impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { + impl ToSimValueWithType<<<$prim as ValueType>::Type as IntType>::Dyn> for $prim { #[track_caller] fn to_sim_value_with_type( &self, - ty: <<$prim as ToExpr>::Type as IntType>::Dyn, - ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { + ty: <<$prim as ValueType>::Type as IntType>::Dyn, + ) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> { SimValue::from_value( ty, - <<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(), + <<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(), ) } } @@ -1103,7 +1139,7 @@ macro_rules! impl_to_sim_value_for_primitive_int { impl ToSimValueWithType for $prim { #[track_caller] fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { - let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); + let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty); SimValue::into_canonical(self.to_sim_value_with_type(ty)) } } @@ -1122,12 +1158,22 @@ impl_to_sim_value_for_primitive_int!(i32); impl_to_sim_value_for_primitive_int!(i64); impl_to_sim_value_for_primitive_int!(i128); impl_to_sim_value_for_primitive_int!(isize); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); +impl_to_sim_value_for_primitive_int!(NonZero); macro_rules! impl_to_sim_value_for_int_value { ($IntValue:ident, $Int:ident, $IntType:ident) => { impl ToSimValue for $IntValue { - type Type = $IntType; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(self.ty(), self.clone()) } @@ -1488,8 +1534,6 @@ impl ToSimValueWithType> for SimOnlyValue { } impl ToSimValue for DynSimOnlyValue { - type Type = DynSimOnly; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(self.ty(), self.clone()) } @@ -1499,29 +1543,58 @@ impl ToSimValue for DynSimOnlyValue { } } -impl ToSimValue for SimOnlyValue { +impl ValueType for SimOnlyValue { type Type = SimOnly; + type ValueCategory = ValueCategoryValue; + fn ty(&self) -> Self::Type { + SimOnly::new() + } +} + +impl ToSimValue for SimOnlyValue { fn to_sim_value(&self) -> SimValue { - SimValue::from_value(Default::default(), self.clone()) + SimValue::from_value(self.ty(), self.clone()) } fn into_sim_value(self) -> SimValue { - SimValue::from_value(Default::default(), self) + SimValue::from_value(self.ty(), self) } } -impl SimValuePartialEq for DynSimOnly { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl HdlPartialEqImpl for DynSimOnly { + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: Self, + rhs_value: Cow<'_, Self::SimValue>, + ) -> bool { + *lhs_value == *rhs_value + } + + #[track_caller] + fn cmp_expr_eq(_lhs: Expr, _rhs: Expr) -> Expr { + panic!("can't compare Expr"); } } -impl, U: SimOnlyValueTrait> SimValuePartialEq> - for SimOnly +impl, R: SimOnlyValueTrait> HdlPartialEqImpl> + for SimOnly { - fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { - ***this == ***other + #[track_caller] + fn cmp_value_eq( + _lhs: Self, + lhs_value: Cow<'_, Self::SimValue>, + _rhs: SimOnly, + rhs_value: Cow<'_, as Type>::SimValue>, + ) -> bool { + **lhs_value == **rhs_value + } + + #[track_caller] + fn cmp_expr_eq(_lhs: Expr, _rhs: Expr>) -> Expr { + panic!("can't compare Expr>"); } } diff --git a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs index 3df80a8..2424c03 100644 --- a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs +++ b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs @@ -3,6 +3,7 @@ //! `unsafe` parts of [`DynSimOnlyValue`] +use crate::expr::{ValueType, value_category::ValueCategoryValue}; use serde::{Serialize, de::DeserializeOwned}; use std::{ any::{self, TypeId}, @@ -295,10 +296,16 @@ impl From> for DynSimOnlyValue { } } -impl DynSimOnlyValue { - pub fn ty(&self) -> DynSimOnly { +impl ValueType for DynSimOnlyValue { + type Type = DynSimOnly; + type ValueCategory = ValueCategoryValue; + + fn ty(&self) -> Self::Type { self.0.ty() } +} + +impl DynSimOnlyValue { pub fn type_id(&self) -> TypeId { self.0.type_id_dyn() } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index d9ea6b2..76c0955 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,7 +11,7 @@ use crate::{ intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, - sim::value::{DynSimOnlyValue, DynSimOnly, SimValue, ToSimValueWithType}, + sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType}, source_location::SourceLocation, util::{ConstUsize, slice_range, try_slice_range}, }; diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 057af46..a15b837 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -25,7 +25,7 @@ impl ReadyValid { #[hdl] pub fn firing_data(expr: impl ToExpr) -> Expr> { let expr = expr.to_expr(); - let option_ty = Expr::ty(expr).data; + let option_ty = expr.ty().data; #[hdl] let firing_data = wire(option_ty); connect(firing_data, option_ty.HdlNone()); @@ -42,7 +42,7 @@ impl ReadyValid { ) -> Expr> { let data = HdlOption::map(expr.data, f); #[hdl] - let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]); + let mapped = wire(ReadyValid[data.ty().HdlSome]); connect(mapped.data, data); connect(expr.ready, mapped.ready); mapped @@ -81,7 +81,7 @@ pub fn queue( let count: UInt = m.output(count_ty); #[hdl] - let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index 51f8cf7..a350d9a 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::{Expr, Flow, ToExpr}, + expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, intern::Interned, module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, source_location::SourceLocation, @@ -16,7 +16,16 @@ pub struct Wire { ty: T, } -impl fmt::Debug for Wire { +impl ValueType for Wire { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl fmt::Debug for Wire { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Wire({:?}: ", self.name)?; self.ty.fmt(f)?; @@ -49,9 +58,6 @@ impl Wire { ty: T::from_canonical(ty), } } - pub fn ty(&self) -> T { - self.ty - } pub fn new_unchecked( scoped_name: ScopedNameId, source_location: SourceLocation, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index a039250..2761cba 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -215,7 +215,7 @@ where let o: Array = m.output(); let bytes = v.to_string().as_bytes().to_expr(); #[hdl] - let o2: Array> = m.output(Expr::ty(bytes)); + let o2: Array> = m.output(bytes.ty()); connect( o, #[hdl] @@ -1176,7 +1176,7 @@ pub fn check_memory_init() { let waddr2: UInt<4> = m.input(); #[hdl] let wdata2: UInt<31> = m.input(); - let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static())); + let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::>())); #[hdl] let mut mem2 = memory_with_init(mem_init2); let read_port2 = mem2.new_read_port(); @@ -1784,7 +1784,7 @@ pub fn check_memory_of_bundle_of_arrays() { [(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)], [(i * i).wrapping_mul(i), i * 2], ], - i.cast_to_static(), + i.cast_to_static::>(), ) })); #[hdl] @@ -1954,9 +1954,9 @@ pub fn check_memory_of_array_of_bundle() { let clk: Clock = m.input(); let mem_init = Vec::from_iter((0..0x10u8).map(|i| { [ - (i, i.cast_to_static()), - ((i * i), (i / 2).cast_to_static()), - ((i * i).wrapping_mul(i), (i / 4).cast_to_static()), + (i, i.cast_to_static::>()), + ((i * i), (i / 2).cast_to_static::>()), + ((i * i).wrapping_mul(i), (i / 4).cast_to_static::>()), ] })); #[hdl] @@ -2269,7 +2269,8 @@ pub fn check_memory_of_bundle() { let wmask: (Bool, Bool) = m.input(); #[hdl] let clk: Clock = m.input(); - let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static()))); + let mem_init = + Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::>()))); #[hdl] let mut mem = memory_with_init(mem_init); let read_port = mem.new_read_port(); diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index be4e207..cbe0b58 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -86,7 +86,7 @@ pub fn mod1() { #[hdl] let child = instance(mod1_child()); #[hdl] - let o: mod1_child = m.output(Expr::ty(child)); + let o: mod1_child = m.output(child.ty()); connect(o, child); } @@ -979,10 +979,10 @@ fn test_memories2() { }, ) in io_cycles.into_iter().enumerate() { - sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static()); + sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static::>()); sim.write_bool(sim.io().rw.en, en); sim.write_bool(sim.io().rw.wmode, wmode); - sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static()); + sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static::>()); sim.write_bool(sim.io().rw.wmask, wmask); sim.advance_time(SimDuration::from_nanos(250)); sim.write_clock(sim.io().rw.clk, true); @@ -1195,9 +1195,9 @@ fn test_memories3() { w_data: [0; 8], w_mask: [false; 8], }); - sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); + sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static::>()); sim.write_bool(sim.io().r.en, r_en); - sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); + sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static::>()); sim.write_bool(sim.io().w.en, w_en); for (i, v) in w_data.into_iter().enumerate() { sim.write_bool_or_int(sim.io().w.data[i], v); diff --git a/crates/fayalite/tests/sim/expected/connect_const_reset.txt b/crates/fayalite/tests/sim/expected/connect_const_reset.txt index a9c1878..6b5814a 100644 --- a/crates/fayalite/tests/sim/expected/connect_const_reset.txt +++ b/crates/fayalite/tests/sim/expected/connect_const_reset.txt @@ -21,7 +21,7 @@ Simulation { }, SlotDebugData { name: "", - ty: Bool, + ty: UInt<1>, }, SlotDebugData { name: "", @@ -51,12 +51,12 @@ Simulation { insns: [ // at: module-XXXXXXXXXX.rs:1:1 0: Const { - dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, value: 0x1, }, 1: Copy { dest: StatePartIndex(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset }, - src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, // at: module-XXXXXXXXXX.rs:4:1 2: Copy { diff --git a/crates/fayalite/tests/sim/expected/phantom_const.txt b/crates/fayalite/tests/sim/expected/phantom_const.txt index 8c2237d..94072ac 100644 --- a/crates/fayalite/tests/sim/expected/phantom_const.txt +++ b/crates/fayalite/tests/sim/expected/phantom_const.txt @@ -30,7 +30,7 @@ Simulation { .. }, big_slots: StatePartLayout { - len: 7, + len: 8, debug_data: [ SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", @@ -54,12 +54,16 @@ Simulation { }, SlotDebugData { name: "", - ty: Bool, + ty: UInt<1>, }, SlotDebugData { name: "", ty: Clock, }, + SlotDebugData { + name: "", + ty: Bool, + }, ], .. }, @@ -90,78 +94,83 @@ Simulation { insns: [ // at: module-XXXXXXXXXX.rs:1:1 0: Const { - dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "", ty: Bool }, value: 0x0, }, + // at: module-XXXXXXXXXX.rs:7:1 1: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: Const { + dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + value: 0x0, + }, + 3: Copy { dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Clock }, - src: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, // at: module-XXXXXXXXXX.rs:6:1 - 2: Copy { + 4: Copy { dest: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock }, src: StatePartIndex(6), // (0x0) SlotDebugData { name: "", ty: Clock }, }, - // at: module-XXXXXXXXXX.rs:7:1 - 3: Copy { - dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool }, - src: StatePartIndex(5), // (0x0) SlotDebugData { name: "", ty: Bool }, - }, // at: module-XXXXXXXXXX.rs:1:1 - 4: Const { + 5: Const { dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> }, value: 0x0, }, - 5: CastToUInt { + 6: CastToUInt { dest: StatePartIndex(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> }, src: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> }, dest_width: 0, }, // at: module-XXXXXXXXXX.rs:5:1 - 6: Copy { + 7: Copy { dest: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> }, src: StatePartIndex(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> }, }, // at: module-XXXXXXXXXX.rs:3:1 - 7: CastBigToArrayIndex { + 8: CastBigToArrayIndex { dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: UInt<0> }, src: StatePartIndex(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> }, }, - 8: IsNonZeroDestIsSmall { + 9: IsNonZeroDestIsSmall { dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool }, }, - 9: BranchIfSmallZero { - target: 11, + 10: BranchIfSmallZero { + target: 12, value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 10: Branch { - target: 11, + 11: Branch { + target: 12, }, - 11: IsNonZeroDestIsSmall { + 12: IsNonZeroDestIsSmall { dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock }, }, - 12: AndSmall { + 13: AndSmall { dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, rhs: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, }, - 13: BranchIfSmallZero { - target: 14, + 14: BranchIfSmallZero { + target: 15, value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 14: XorSmallImmediate { + 15: XorSmallImmediate { dest: StatePartIndex(0), // (0x1 1) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, rhs: 0x1, }, // at: module-XXXXXXXXXX.rs:1:1 - 15: Return, + 16: Return, ], .. }, - pc: 15, + pc: 16, memory_write_log: [], memories: StatePart { value: [ @@ -192,6 +201,7 @@ Simulation { 0, 0, 0, + 0, ], }, sim_only_slots: StatePart { diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.txt b/crates/fayalite/tests/sim/expected/ripple_counter.txt index 9e46be4..9cc5f02 100644 --- a/crates/fayalite/tests/sim/expected/ripple_counter.txt +++ b/crates/fayalite/tests/sim/expected/ripple_counter.txt @@ -162,7 +162,7 @@ Simulation { }, SlotDebugData { name: "", - ty: Bool, + ty: UInt<1>, }, SlotDebugData { name: "", @@ -429,12 +429,12 @@ Simulation { }, // at: module-XXXXXXXXXX.rs:1:1 26: Const { - dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, value: 0x0, }, 27: Copy { dest: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, - src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, 28: Copy { dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr index 1fd291c..7191e5e 100644 --- a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -4,14 +4,14 @@ error[E0277]: `Cell` cannot be shared between thr 11 | fn f(v: SimValue<()>) -> Interned> { | ^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -28,13 +28,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th 11 | fn f(v: SimValue<()>) -> Interned> { | ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -51,7 +51,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta 11 | fn f(v: SimValue<()>) -> Interned> { | ^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely | - = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` note: required because it appears within the type `DynSimOnlyValue` --> src/sim/value/sim_only_value_unsafe.rs | @@ -92,7 +92,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -103,29 +103,15 @@ note: required by a bound in `fayalite::intern::Interned` | pub struct Interned { | ^^^^ required by this bound in `Interned` -error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied +error[E0277]: the trait bound `fayalite::prelude::SimValue<()>: Intern` is not satisfied --> tests/ui/simvalue_is_not_internable.rs:12:26 | 12 | Intern::intern_sized(v) - | -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>` + | -------------------- ^ the trait `Hash` is not implemented for `fayalite::prelude::SimValue<()>` | | | required by a bound introduced by this call | - = note: required for `SimValue<()>` to implement `Intern` -help: consider dereferencing here - | -12 | Intern::intern_sized(*v) - | + - -error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied - --> tests/ui/simvalue_is_not_internable.rs:12:26 - | -12 | Intern::intern_sized(v) - | -------------------- ^ the trait `std::cmp::Eq` is not implemented for `SimValue<()>` - | | - | required by a bound introduced by this call - | - = note: required for `SimValue<()>` to implement `Intern` + = note: required for `fayalite::prelude::SimValue<()>` to implement `Intern` help: consider dereferencing here | 12 | Intern::intern_sized(*v) @@ -139,14 +125,14 @@ error[E0277]: `Cell` cannot be shared between thr | | | required by a bound introduced by this call | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -172,13 +158,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th | | | required by a bound introduced by this call | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -204,7 +190,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta | | | required by a bound introduced by this call | - = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` note: required because it appears within the type `DynSimOnlyValue` --> src/sim/value/sim_only_value_unsafe.rs | @@ -245,7 +231,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -269,14 +255,14 @@ error[E0277]: `Cell` cannot be shared between thr 12 | Intern::intern_sized(v) | ^^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -293,13 +279,13 @@ error[E0277]: `UnsafeCell>` cannot be shared between th 12 | Intern::intern_sized(v) | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely | - = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` note: required because it appears within the type `util::alternating_cell::AlternatingCell>` --> src/util/alternating_cell.rs | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue { @@ -316,7 +302,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta 12 | Intern::intern_sized(v) | ^^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely | - = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` + = help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` note: required because it appears within the type `DynSimOnlyValue` --> src/sim/value/sim_only_value_unsafe.rs | @@ -357,7 +343,7 @@ note: required because it appears within the type `util::alternating_cell::Alter | | pub(crate) struct AlternatingCell { | ^^^^^^^^^^^^^^^ -note: required because it appears within the type `SimValue<()>` +note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | | pub struct SimValue {